1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2011 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
, style
, color
, unit
, canvas
, path
, mesh
, pycompat
, trafo
27 from pyx
import text
as textmodule
28 from pyx
import bitmap
as bitmapmodule
34 """Interface class for graph styles
36 Each graph style must support the methods described in this
37 class. However, since a graph style might not need to perform
38 actions on all the various events, it does not need to overwrite
39 all methods of this base class (e.g. this class is not an abstract
40 class in any respect).
42 A style should never store private data by instance variables
43 (i.e. accessing self), but it should use the sharedata and privatedata
44 instances instead. A style instance can be used multiple times with
45 different sharedata and privatedata instances at the very same time.
46 The sharedata and privatedata instances act as data containers and
47 sharedata allows for sharing information across several styles.
49 Every style contains two class variables, which are not to be
51 - providesdata is a list of variable names a style offers via
52 the sharedata instance. This list is used to determine whether
53 all needs of subsequent styles are fulfilled. Otherwise
54 getdefaultprovider should return a proper style to be used.
55 - needsdata is a list of variable names the style needs to access in the
59 providesdata
= [] # by default, we provide nothing
60 needsdata
= [] # and do not depend on anything
62 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
63 """Set column information
65 This method is used setup the column name information to be
66 accessible to the style later on. The style should analyse
67 the list of column names. The method should return a list of
68 column names which the style will make use of."""
71 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
74 This method is called in order to adjust the axis range to
75 the provided data. columnname is the column name (each style
76 is subsequently called for all column names)."""
79 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
80 """Select stroke/fill attributes
82 This method is called to allow for the selection of
83 changable attributes of a style."""
86 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
87 """Initialize drawing of data
89 This method might be used to initialize the drawing of data."""
92 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
95 This method is called for each data point. The data is
96 available in the dictionary point. The dictionary
97 keys are the column names."""
100 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
101 """Finalize drawing of data
103 This method is called after the last data point was
104 drawn using the drawpoint method above."""
107 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
111 # The following two methods are used to register and get a default provider
112 # for keys. A key is a variable name in sharedata. A provider is a style
113 # which creates variables in sharedata.
115 _defaultprovider
= {}
117 def registerdefaultprovider(style
, keys
):
118 """sets a style as a default creator for sharedata variables 'keys'"""
120 assert key
in style
.providesdata
, "key not provided by style"
121 # we might allow for overwriting the defaults, i.e. the following is not checked:
122 # assert key in _defaultprovider.keys(), "default provider already registered for key"
123 _defaultprovider
[key
] = style
125 def getdefaultprovider(key
):
126 """returns a style, which acts as a default creator for the
127 sharedata variable 'key'"""
128 return _defaultprovider
[key
]
133 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
135 def __init__(self
, epsilon
=1e-10):
136 self
.epsilon
= epsilon
138 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
139 sharedata
.poscolumnnames
= []
140 sharedata
.vposmissing
= []
141 for count
, axisnames
in enumerate(graph
.axesnames
):
142 # all used axisnames are also data columns
143 for axisname
in axisnames
:
144 for columnname
in columnnames
:
145 if axisname
== columnname
:
146 sharedata
.poscolumnnames
.append(columnname
)
147 if len(sharedata
.poscolumnnames
) > count
+1:
148 raise ValueError("multiple axes per graph dimension")
149 elif len(sharedata
.poscolumnnames
) < count
+1:
150 sharedata
.vposmissing
.append(count
)
151 sharedata
.poscolumnnames
.append(None)
152 return [columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None]
154 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
155 if columnname
in sharedata
.poscolumnnames
:
156 graph
.axes
[columnname
].adjustaxis(data
)
158 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
159 sharedata
.vpos
= [None]*(len(graph
.axesnames
))
160 privatedata
.pointpostmplist
= [[columnname
, index
, graph
.axes
[columnname
]] # temporarily used by drawpoint only
161 for index
, columnname
in enumerate([columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None])]
162 for missing
in sharedata
.vposmissing
:
163 for pointpostmp
in privatedata
.pointpostmplist
:
164 if pointpostmp
[1] >= missing
:
167 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
168 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
169 sharedata
.vposvalid
= 1 # valid position inside the graph
170 for columnname
, index
, axis
in privatedata
.pointpostmplist
:
172 v
= axis
.convert(point
[columnname
])
173 except (ArithmeticError, ValueError, TypeError):
174 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
175 sharedata
.vpos
[index
] = None
177 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
178 sharedata
.vposvalid
= 0
179 sharedata
.vpos
[index
] = v
182 registerdefaultprovider(pos(), pos
.providesdata
)
187 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
197 def __init__(self
, usenames
={}, epsilon
=1e-10):
198 self
.usenames
= usenames
199 self
.epsilon
= epsilon
201 def _numberofbits(self
, mask
):
205 return self
._numberofbits
(mask
>> 1) + 1
207 return self
._numberofbits
(mask
>> 1)
209 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
211 privatedata
.rangeposcolumns
= []
212 sharedata
.vrangemissing
= []
213 sharedata
.vrangeminmissing
= []
214 sharedata
.vrangemaxmissing
= []
215 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
216 for count
, axisnames
in enumerate(graph
.axesnames
):
217 for axisname
in axisnames
:
219 usename
= self
.usenames
[axisname
]
223 for columnname
in columnnames
:
225 if usename
== columnname
:
226 mask
+= self
.mask_value
227 elif usename
+ "min" == columnname
:
228 mask
+= self
.mask_min
229 elif usename
+ "max" == columnname
:
230 mask
+= self
.mask_max
231 elif "d" + usename
+ "min" == columnname
:
232 mask
+= self
.mask_dmin
233 elif "d" + usename
+ "max" == columnname
:
234 mask
+= self
.mask_dmax
235 elif "d" + usename
== columnname
:
240 usecolumns
.append(columnname
)
241 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
242 if (self
._numberofbits
(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
243 self
._numberofbits
(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
244 raise ValueError("multiple range definition")
245 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
246 if not (mask
& self
.mask_value
):
247 raise ValueError("missing value for delta")
248 privatedata
.rangeposdeltacolumns
[axisname
] = {}
249 privatedata
.rangeposcolumns
.append((axisname
, usename
, mask
))
250 elif mask
== self
.mask_value
:
251 usecolumns
= usecolumns
[:-1]
252 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
253 raise ValueError("multiple axes per graph dimension")
254 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
255 sharedata
.vrangemissing
.append(count
)
256 sharedata
.vrangeminmissing
.append(count
)
257 sharedata
.vrangemaxmissing
.append(count
)
259 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
260 sharedata
.vrangeminmissing
.append(count
)
261 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
262 sharedata
.vrangemaxmissing
.append(count
)
265 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
266 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
267 if columnname
== usename
+ "min" and mask
& self
.mask_min
:
268 graph
.axes
[axisname
].adjustaxis(data
)
269 if columnname
== usename
+ "max" and mask
& self
.mask_max
:
270 graph
.axes
[axisname
].adjustaxis(data
)
272 # delta handling: fill rangeposdeltacolumns
273 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
274 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
275 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
276 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
277 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
278 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
279 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
280 if columnname
== "d" + usename
and mask
& self
.mask_d
:
281 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
283 # delta handling: process rangeposdeltacolumns
284 for a
, d
in privatedata
.rangeposdeltacolumns
.items():
285 if d
.has_key(self
.mask_value
):
287 if k
!= self
.mask_value
:
288 if k
& (self
.mask_dmin | self
.mask_d
):
290 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
292 mindata
.append(value
-delta
)
295 graph
.axes
[a
].adjustaxis(mindata
)
296 if k
& (self
.mask_dmax | self
.mask_d
):
298 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
300 maxdata
.append(value
+delta
)
303 graph
.axes
[a
].adjustaxis(maxdata
)
306 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
307 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
308 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
309 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
310 for missing
in sharedata
.vrangemissing
:
311 for rangepostmp
in privatedata
.rangepostmplist
:
312 if rangepostmp
[2] >= missing
:
315 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
316 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
318 if mask
& self
.mask_min
:
319 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
320 if mask
& self
.mask_dmin
:
321 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
322 if mask
& self
.mask_d
:
323 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
324 except (ArithmeticError, ValueError, TypeError):
325 sharedata
.vrange
[index
][0] = None
327 if mask
& self
.mask_max
:
328 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
329 if mask
& self
.mask_dmax
:
330 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
331 if mask
& self
.mask_d
:
332 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
333 except (ArithmeticError, ValueError, TypeError):
334 sharedata
.vrange
[index
][1] = None
336 # some range checks for data consistency
337 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
338 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
339 raise ValueError("inverse range")
340 # disabled due to missing vpos access:
341 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
342 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
343 # raise ValueError("negative minimum errorbar")
344 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
345 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
346 # raise ValueError("negative maximum errorbar")
349 registerdefaultprovider(range(), range.providesdata
)
352 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
353 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
354 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
355 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
356 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
358 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
359 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
360 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
361 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
362 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
364 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
365 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
366 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
),
367 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
368 path
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
369 path
.closepath()), attrs
)
371 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
372 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
373 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
374 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
375 path
.closepath()), attrs
)
377 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
378 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
379 path
.closepath()), attrs
)
381 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
382 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
383 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
384 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
385 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
386 path
.closepath()), attrs
)
389 class _styleneedingpointpos(_style
):
391 needsdata
= ["vposmissing"]
393 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
394 if len(sharedata
.vposmissing
):
395 raise ValueError("incomplete position information")
399 class symbol(_styleneedingpointpos
):
401 needsdata
= ["vpos", "vposmissing", "vposvalid"]
403 # "inject" the predefinied symbols into the class:
405 # Note, that statements like cross = _crosssymbol are
406 # invalid, since the would lead to unbound methods, but
407 # a single entry changeable list does the trick.
409 # Once we require Python 2.2+ we should use staticmethods
410 # to implement the default symbols inplace.
412 cross
= attr
.changelist([_crosssymbol
])
413 plus
= attr
.changelist([_plussymbol
])
414 square
= attr
.changelist([_squaresymbol
])
415 triangle
= attr
.changelist([_trianglesymbol
])
416 circle
= attr
.changelist([_circlesymbol
])
417 diamond
= attr
.changelist([_diamondsymbol
])
419 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
420 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
421 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
422 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
423 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
424 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
425 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
426 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
427 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
428 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
430 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
431 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
433 defaultsymbolattrs
= [deco
.stroked
]
435 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
438 self
.symbolattrs
= symbolattrs
440 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
441 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
442 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
443 if self
.symbolattrs
is not None:
444 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
446 privatedata
.symbolattrs
= None
448 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
449 privatedata
.symbolcanvas
= canvas
.canvas()
451 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
452 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
453 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
454 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
456 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
457 graph
.insert(privatedata
.symbolcanvas
)
459 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
460 if privatedata
.symbolattrs
is not None:
461 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
464 class _line(_styleneedingpointpos
):
466 # this style is not a complete style, but it provides the basic functionality to
467 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
469 def initpointstopath(self
, privatedata
):
470 privatedata
.path
= path
.path()
471 privatedata
.linebasepoints
= []
472 privatedata
.lastvpos
= None
474 def addpointstopath(self
, privatedata
):
475 # add baselinepoints to privatedata.path
476 if len(privatedata
.linebasepoints
) > 1:
477 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
478 if len(privatedata
.linebasepoints
) > 2:
479 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
481 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
482 privatedata
.linebasepoints
= []
484 def addpoint(self
, privatedata
, graphvpos_pt
, vposavailable
, vposvalid
, vpos
):
485 # append linebasepoints
487 if len(privatedata
.linebasepoints
):
488 # the last point was inside the graph
489 if vposvalid
: # shortcut for the common case
490 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
494 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
497 # 1 = vstart + (vend - vstart) * cut
499 newcut
= (1 - vstart
)/(vend
- vstart
)
500 except (ArithmeticError, TypeError):
503 # 0 = vstart + (vend - vstart) * cut
505 newcut
= - vstart
/(vend
- vstart
)
506 except (ArithmeticError, TypeError):
508 if newcut
is not None and newcut
< cut
:
512 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
513 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
514 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
515 self
.addpointstopath(privatedata
)
517 # the last point was outside the graph
518 if privatedata
.lastvpos
is not None:
522 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
525 # 1 = vstart + (vend - vstart) * cut
527 newcut
= (1 - vstart
)/(vend
- vstart
)
528 except (ArithmeticError, TypeError):
531 # 0 = vstart + (vend - vstart) * cut
533 newcut
= - vstart
/(vend
- vstart
)
534 except (ArithmeticError, TypeError):
536 if newcut
is not None and newcut
> cut
:
540 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
541 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
542 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
543 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
545 # sometimes cut beginning and end
548 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
553 # 1 = vstart + (vend - vstart) * cutfrom
555 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
556 except (ArithmeticError, TypeError):
561 # 0 = vstart + (vend - vstart) * cutfrom
563 newcutfrom
= - vstart
/(vend
- vstart
)
564 except (ArithmeticError, TypeError):
566 if newcutfrom
is not None and newcutfrom
> cutfrom
:
570 # 1 = vstart + (vend - vstart) * cutto
572 newcutto
= (1 - vstart
)/(vend
- vstart
)
573 except (ArithmeticError, TypeError):
576 # 0 = vstart + (vend - vstart) * cutto
578 newcutto
= - vstart
/(vend
- vstart
)
579 except (ArithmeticError, TypeError):
581 if newcutto
is not None and newcutto
< cutto
:
587 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
588 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
589 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
590 privatedata
.linebasepoints
.append(graphvpos_pt(*cutfromvpos
))
591 privatedata
.linebasepoints
.append(graphvpos_pt(*cuttovpos
))
592 self
.addpointstopath(privatedata
)
593 privatedata
.lastvpos
= vpos
[:]
595 if len(privatedata
.linebasepoints
) > 1:
596 self
.addpointstopath(privatedata
)
597 privatedata
.lastvpos
= None
599 def addinvalid(self
, privatedata
):
600 if len(privatedata
.linebasepoints
) > 1:
601 self
.addpointstopath(privatedata
)
602 privatedata
.lastvpos
= None
604 def donepointstopath(self
, privatedata
):
605 if len(privatedata
.linebasepoints
) > 1:
606 self
.addpointstopath(privatedata
)
607 return privatedata
.path
612 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
614 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
615 style
.linestyle
.dashed
,
616 style
.linestyle
.dotted
,
617 style
.linestyle
.dashdotted
])
619 defaultlineattrs
= [changelinestyle
]
621 def __init__(self
, lineattrs
=[]):
622 self
.lineattrs
= lineattrs
624 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
625 if self
.lineattrs
is not None:
626 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
628 privatedata
.lineattrs
= None
630 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
631 self
.initpointstopath(privatedata
)
633 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
634 self
.addpoint(privatedata
, graph
.vpos_pt
, sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
)
636 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
637 path
= self
.donepointstopath(privatedata
)
638 if privatedata
.lineattrs
is not None and len(path
):
639 graph
.stroke(path
, privatedata
.lineattrs
)
641 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
642 if privatedata
.lineattrs
is not None:
643 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
)
646 class impulses(_styleneedingpointpos
):
648 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
650 defaultlineattrs
= [line
.changelinestyle
]
651 defaultfrompathattrs
= []
653 def __init__(self
, lineattrs
=[], fromvalue
=0, frompathattrs
=[], valueaxisindex
=1):
654 self
.lineattrs
= lineattrs
655 self
.fromvalue
= fromvalue
656 self
.frompathattrs
= frompathattrs
657 self
.valueaxisindex
= valueaxisindex
659 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
660 privatedata
.insertfrompath
= selectindex
== 0
661 if self
.lineattrs
is not None:
662 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
664 privatedata
.lineattrs
= None
666 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
667 if self
.fromvalue
is not None:
669 i
= sharedata
.poscolumnnames
.index(columnname
)
673 if i
== self
.valueaxisindex
:
674 graph
.axes
[sharedata
.poscolumnnames
[i
]].adjustaxis([self
.fromvalue
])
676 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
677 privatedata
.impulsescanvas
= canvas
.canvas()
678 if self
.fromvalue
is not None:
679 valueaxisname
= sharedata
.poscolumnnames
[self
.valueaxisindex
]
680 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
681 privatedata
.vfromvaluecut
= 0
682 if privatedata
.vfromvalue
< 0:
683 privatedata
.vfromvalue
= 0
684 if privatedata
.vfromvalue
> 1:
685 privatedata
.vfromvalue
= 1
686 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
687 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
688 self
.defaultfrompathattrs
+ self
.frompathattrs
)
690 privatedata
.vfromvalue
= 0
692 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
693 if sharedata
.vposvalid
and privatedata
.lineattrs
is not None:
694 vpos
= sharedata
.vpos
[:]
695 vpos
[self
.valueaxisindex
] = privatedata
.vfromvalue
696 privatedata
.impulsescanvas
.stroke(graph
.vgeodesic(*(vpos
+ sharedata
.vpos
)), privatedata
.lineattrs
)
698 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
699 graph
.insert(privatedata
.impulsescanvas
)
701 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
702 if privatedata
.lineattrs
is not None:
703 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
)
706 class errorbar(_style
):
708 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
710 defaulterrorbarattrs
= []
712 def __init__(self
, size
=0.1*unit
.v_cm
,
716 self
.errorbarattrs
= errorbarattrs
717 self
.epsilon
= epsilon
719 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
720 for i
in sharedata
.vposmissing
:
721 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
722 raise ValueError("position and range for a graph dimension missing")
725 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
726 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
727 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
729 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
730 if privatedata
.errorbarattrs
is not None:
731 privatedata
.errorbarcanvas
= canvas
.canvas(privatedata
.errorbarattrs
)
732 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
734 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
735 if privatedata
.errorbarattrs
is not None:
736 for i
in privatedata
.dimensionlist
:
737 for j
in privatedata
.dimensionlist
:
739 (sharedata
.vpos
[j
] is None or
740 sharedata
.vpos
[j
] < -self
.epsilon
or
741 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
744 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
745 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
746 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
748 vminpos
= sharedata
.vpos
[:]
749 if sharedata
.vrange
[i
][0] is not None:
750 vminpos
[i
] = sharedata
.vrange
[i
][0]
754 if vminpos
[i
] > 1+self
.epsilon
:
756 if vminpos
[i
] < -self
.epsilon
:
759 vmaxpos
= sharedata
.vpos
[:]
760 if sharedata
.vrange
[i
][1] is not None:
761 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
765 if vmaxpos
[i
] < -self
.epsilon
:
767 if vmaxpos
[i
] > 1+self
.epsilon
:
770 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
771 for j
in privatedata
.dimensionlist
:
774 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
776 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
778 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
779 if privatedata
.errorbarattrs
is not None:
780 graph
.insert(privatedata
.errorbarcanvas
)
783 class text(_styleneedingpointpos
):
785 needsdata
= ["vpos", "vposmissing", "vposvalid"]
787 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
789 def __init__(self
, textname
="text", dxname
=None, dyname
=None,
790 dxunit
=0.3*unit
.v_cm
, dyunit
=0.3*unit
.v_cm
,
791 textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
792 self
.textname
= textname
799 self
.textattrs
= textattrs
801 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
802 if self
.textname
not in columnnames
:
803 raise ValueError("column '%s' missing" % self
.textname
)
804 names
= [self
.textname
]
805 if self
.dxname
is not None:
806 if self
.dxname
not in columnnames
:
807 raise ValueError("column '%s' missing" % self
.dxname
)
808 names
.append(self
.dxname
)
809 if self
.dyname
is not None:
810 if self
.dyname
not in columnnames
:
811 raise ValueError("column '%s' missing" % self
.dyname
)
812 names
.append(self
.dyname
)
813 return names
+ _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
815 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
816 if self
.textattrs
is not None:
817 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
819 privatedata
.textattrs
= None
821 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
822 if self
.dxname
is None:
823 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
825 privatedata
.dxunit_pt
= unit
.topt(self
.dxunit
)
826 if self
.dyname
is None:
827 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
829 privatedata
.dyunit_pt
= unit
.topt(self
.dyunit
)
831 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
832 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
833 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
835 text
= str(point
[self
.textname
])
839 if self
.dxname
is None:
840 dx_pt
= privatedata
.textdx_pt
842 dx_pt
= float(point
[self
.dxname
]) * privatedata
.dxunit_pt
843 if self
.dyname
is None:
844 dy_pt
= privatedata
.textdy_pt
846 dy_pt
= float(point
[self
.dyname
]) * privatedata
.dyunit_pt
847 graph
.text_pt(x_pt
+ dx_pt
, y_pt
+ dy_pt
, text
, privatedata
.textattrs
)
849 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
850 raise RuntimeError("Style currently doesn't provide a graph key")
853 class arrow(_styleneedingpointpos
):
855 needsdata
= ["vpos", "vposmissing", "vposvalid"]
857 defaultlineattrs
= []
858 defaultarrowattrs
= []
860 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
):
861 self
.linelength
= linelength
862 self
.arrowsize
= arrowsize
863 self
.lineattrs
= lineattrs
864 self
.arrowattrs
= arrowattrs
865 self
.arrowpos
= arrowpos
866 self
.epsilon
= epsilon
867 self
.decorator
= decorator
869 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
870 if len(graph
.axesnames
) != 2:
871 raise ValueError("arrow style restricted on two-dimensional graphs")
872 if "size" not in columnnames
:
873 raise ValueError("size missing")
874 if "angle" not in columnnames
:
875 raise ValueError("angle missing")
876 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
878 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
879 if self
.lineattrs
is not None:
880 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
882 privatedata
.lineattrs
= None
883 if self
.arrowattrs
is not None:
884 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
886 privatedata
.arrowattrs
= None
888 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
889 privatedata
.arrowcanvas
= canvas
.canvas()
891 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
892 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
893 linelength_pt
= unit
.topt(self
.linelength
)
894 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
896 angle
= point
["angle"] + 0.0
897 size
= point
["size"] + 0.0
901 if point
["size"] > self
.epsilon
:
902 dx
= math
.cos(angle
*math
.pi
/180)
903 dy
= math
.sin(angle
*math
.pi
/180)
904 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
905 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
906 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
907 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
909 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
),
910 privatedata
.lineattrs
+[self
.decorator(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
912 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
)
914 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
915 graph
.insert(privatedata
.arrowcanvas
)
917 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
918 raise RuntimeError("Style currently doesn't provide a graph key")
923 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
925 def __init__(self
, gradient
=color
.gradient
.Grey
):
926 self
.gradient
= gradient
928 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
929 if len(graph
.axesnames
) != 2:
930 raise TypeError("arrow style restricted on two-dimensional graphs")
931 if "color" not in columnnames
:
932 raise ValueError("color missing")
933 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
934 raise ValueError("incomplete range")
937 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
938 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
940 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
941 xvmin
= sharedata
.vrange
[0][0]
942 xvmax
= sharedata
.vrange
[0][1]
943 yvmin
= sharedata
.vrange
[1][0]
944 yvmax
= sharedata
.vrange
[1][1]
945 if (xvmin
is not None and xvmin
< 1 and
946 xvmax
is not None and xvmax
> 0 and
947 yvmin
is not None and yvmin
< 1 and
948 yvmax
is not None and yvmax
> 0):
957 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
958 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
959 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
960 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
961 p
.append(path
.closepath())
962 privatedata
.rectcanvas
.fill(p
, [self
.gradient
.getcolor(point
["color"])])
964 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
965 raise RuntimeError("Style currently doesn't provide a graph key")
968 class histogram(_style
):
970 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
972 defaultlineattrs
= [deco
.stroked
]
973 defaultfrompathattrs
= []
975 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0, rectkey
=0,
976 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
977 self
.lineattrs
= lineattrs
979 self
.fromvalue
= fromvalue
980 self
.frompathattrs
= frompathattrs
981 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
982 self
.rectkey
= rectkey
983 self
.autohistogramaxisindex
= autohistogramaxisindex
984 self
.autohistogrampointpos
= autohistogrampointpos
985 self
.epsilon
= epsilon
987 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
988 if len(graph
.axesnames
) != 2:
989 raise TypeError("histogram style restricted on two-dimensional graphs")
990 privatedata
.rangeaxisindex
= None
991 for i
in builtinrange(len(graph
.axesnames
)):
992 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
993 if i
in sharedata
.vposmissing
:
994 raise ValueError("pos and range missing")
996 if privatedata
.rangeaxisindex
is not None:
997 raise ValueError("multiple ranges")
998 privatedata
.rangeaxisindex
= i
999 if privatedata
.rangeaxisindex
is None:
1000 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
1001 privatedata
.autohistogram
= 1
1003 privatedata
.autohistogram
= 0
1006 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1007 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
1009 raise ValueError("several data points needed for automatic histogram width calculation")
1011 delta
= data
[1] - data
[0]
1012 min = data
[0] - self
.autohistogrampointpos
* delta
1013 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
1014 graph
.axes
[columnname
].adjustaxis([min, max])
1015 elif self
.fromvalue
is not None and columnname
== sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]:
1016 graph
.axes
[columnname
].adjustaxis([self
.fromvalue
])
1018 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1019 privatedata
.insertfrompath
= selectindex
== 0
1020 if self
.lineattrs
is not None:
1021 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
1023 privatedata
.lineattrs
= None
1025 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
1026 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
1027 if privatedata
.rangeaxisindex
:
1028 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
1030 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
1032 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
1033 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
1048 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
1049 if vvalue1cut
and not self
.fillable
:
1050 if privatedata
.rangeaxisindex
:
1051 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
1053 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
1054 if privatedata
.rangeaxisindex
:
1055 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
1057 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
1059 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
1061 if vvalue
< -self
.epsilon
:
1063 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1064 if vvalue
> 1+self
.epsilon
:
1066 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1067 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1082 if abs(vpos1cut
+ vpos2cut
) <= 1:
1085 if privatedata
.rangeaxisindex
:
1086 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1087 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1089 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1090 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1092 if privatedata
.rangeaxisindex
:
1093 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1095 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1096 if privatedata
.rangeaxisindex
:
1097 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1099 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1100 if self
.fillable
and vpos2cut
:
1101 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1102 if privatedata
.rangeaxisindex
:
1103 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1105 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1107 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1108 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1109 if self
.fillable
and not self
.steps
:
1110 if not currentvalid
:
1113 if vmin
< -self
.epsilon
:
1116 elif vmin
> 1+self
.epsilon
:
1120 if vmax
< -self
.epsilon
:
1123 if vmax
> 1+self
.epsilon
:
1127 if vvalue
< -self
.epsilon
:
1130 if vvalue
> 1+self
.epsilon
:
1134 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1135 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1138 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1141 if privatedata
.rangeaxisindex
:
1142 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1143 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1144 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1145 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1147 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1148 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1149 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1150 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1153 if privatedata
.rangeaxisindex
:
1154 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1155 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1156 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1157 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1159 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1160 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1161 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1162 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1163 elif privatedata
.vfromvaluecut
:
1165 if privatedata
.rangeaxisindex
:
1166 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1167 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1168 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1169 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1171 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1172 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1173 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1174 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1177 if privatedata
.rangeaxisindex
:
1178 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1179 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1180 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1181 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1183 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1184 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1185 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1186 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
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
))
1193 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1194 privatedata
.path
.append(path
.closepath())
1196 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1197 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, 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
))
1201 privatedata
.path
.append(path
.closepath())
1204 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1205 except (ArithmeticError, ValueError, TypeError):
1207 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1208 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1209 self
.vposline(privatedata
, sharedata
, graph
,
1210 vmin
, privatedata
.lastvvalue
, vvalue
)
1212 if privatedata
.lastvvalue
is not None and currentvalid
:
1213 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1214 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1215 self
.vposline(privatedata
, sharedata
, graph
,
1216 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1218 self
.vmoveto(privatedata
, sharedata
, graph
,
1220 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1221 self
.vmoveto(privatedata
, sharedata
, graph
,
1222 vmin
, privatedata
.vfromvalue
)
1223 self
.vposline(privatedata
, sharedata
, graph
,
1224 vmin
, privatedata
.vfromvalue
, vvalue
)
1226 self
.vvalueline(privatedata
, sharedata
, graph
,
1228 privatedata
.lastvvalue
= vvalue
1229 privatedata
.lastvmax
= vmax
1231 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1233 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1234 privatedata
.path
= path
.path()
1235 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1236 privatedata
.vcurrentpoint
= None
1237 privatedata
.count
= 0
1238 if self
.fromvalue
is not None:
1239 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1240 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1241 privatedata
.vfromvaluecut
= 0
1242 if privatedata
.vfromvalue
< 0:
1243 privatedata
.vfromvalue
= 0
1244 privatedata
.vfromvaluecut
= -1
1245 if privatedata
.vfromvalue
> 1:
1246 privatedata
.vfromvalue
= 1
1247 privatedata
.vfromvaluecut
= 1
1248 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1249 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1250 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1252 privatedata
.vfromvalue
= 0
1254 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1255 if privatedata
.autohistogram
:
1256 # automatic range handling
1257 privatedata
.count
+= 1
1258 if privatedata
.count
== 2:
1259 if privatedata
.rangeaxisindex
:
1260 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1261 self
.drawvalue(privatedata
, sharedata
, graph
,
1262 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1263 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1264 privatedata
.lastvpos
[0])
1266 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1267 self
.drawvalue(privatedata
, sharedata
, graph
,
1268 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1269 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1270 privatedata
.lastvpos
[1])
1271 elif privatedata
.count
> 2:
1272 if privatedata
.rangeaxisindex
:
1273 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1275 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1276 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1277 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1278 if privatedata
.count
> 1:
1279 if privatedata
.rangeaxisindex
:
1280 self
.drawvalue(privatedata
, sharedata
, graph
,
1281 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1282 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1285 self
.drawvalue(privatedata
, sharedata
, graph
,
1286 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1287 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1289 privatedata
.lastvpos
= sharedata
.vpos
[:]
1291 if privatedata
.rangeaxisindex
:
1292 self
.drawvalue(privatedata
, sharedata
, graph
,
1293 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1295 self
.drawvalue(privatedata
, sharedata
, graph
,
1296 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1298 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1299 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1300 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1301 graph
.draw(privatedata
.path
, privatedata
.lineattrs
)
1303 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1304 if privatedata
.lineattrs
is not None:
1306 p
= path
.rect_pt(x_pt
, y_pt
, width_pt
, height_pt
)
1308 p
= path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
)
1309 graph
.draw(p
, privatedata
.lineattrs
)
1312 class barpos(_style
):
1314 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1316 defaultfrompathattrs
= []
1318 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1319 self
.fromvalue
= fromvalue
1320 self
.frompathattrs
= frompathattrs
1321 self
.epsilon
= epsilon
1323 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1324 sharedata
.barposcolumnnames
= []
1325 sharedata
.barvalueindex
= None
1326 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1328 for axisname
in axisnames
:
1329 if axisname
in columnnames
:
1330 if sharedata
.barvalueindex
is not None:
1331 raise ValueError("multiple values")
1332 sharedata
.barvalueindex
= dimension
1333 sharedata
.barposcolumnnames
.append(axisname
)
1335 if (axisname
+ "name") in columnnames
:
1336 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1339 raise ValueError("multiple names and value")
1341 raise ValueError("value/name missing")
1342 if sharedata
.barvalueindex
is None:
1343 raise ValueError("missing value")
1344 sharedata
.vposmissing
= []
1345 return sharedata
.barposcolumnnames
1347 def addsubvalue(self
, value
, subvalue
):
1352 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1354 return value
, subvalue
1356 return value
, subvalue
1358 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1360 i
= sharedata
.barposcolumnnames
.index(columnname
)
1364 if i
== sharedata
.barvalueindex
:
1365 if self
.fromvalue
is not None:
1366 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1367 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1369 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1370 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1372 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1373 privatedata
.insertfrompath
= selectindex
== 0
1375 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1376 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1377 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1378 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1380 if self
.fromvalue
is not None:
1381 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(self
.fromvalue
)
1382 if privatedata
.vfromvalue
< 0:
1383 privatedata
.vfromvalue
= 0
1384 if privatedata
.vfromvalue
> 1:
1385 privatedata
.vfromvalue
= 1
1386 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1387 graph
.stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].vgridpath(privatedata
.vfromvalue
),
1388 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1390 privatedata
.vfromvalue
= 0
1392 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1393 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1394 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1395 if i
== sharedata
.barvalueindex
:
1396 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1397 sharedata
.lastbarvalue
= point
[barname
]
1399 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1400 except (ArithmeticError, ValueError, TypeError):
1401 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1405 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1406 except (ArithmeticError, ValueError, TypeError):
1407 sharedata
.vbarrange
[i
][j
] = None
1409 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1410 except (ArithmeticError, ValueError, TypeError):
1411 sharedata
.vpos
[i
] = None
1412 if sharedata
.vpos
[i
] is None:
1413 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1414 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1415 sharedata
.vposvalid
= 0
1417 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1420 class stackedbarpos(_style
):
1422 # provides no additional data, but needs some data (and modifies some of them)
1423 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1425 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1426 self
.stackname
= stackname
1427 self
.epsilon
= epsilon
1428 self
.addontop
= addontop
1430 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1431 if self
.stackname
not in columnnames
:
1432 raise ValueError("column '%s' missing" % self
.stackname
)
1433 return [self
.stackname
]
1435 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1436 if columnname
== self
.stackname
:
1437 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1439 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1440 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1441 sharedata
.stackedbar
+= 1
1443 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1444 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1447 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1448 except (ArithmeticError, ValueError, TypeError):
1449 sharedata
.lastbarvalue
= None
1451 sharedata
.lastbarvalue
= point
[self
.stackname
]
1453 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1454 except (ArithmeticError, ValueError, TypeError):
1455 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1456 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1458 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1459 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1460 for v
in sharedata
.vpos
:
1462 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1464 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1465 sharedata
.vposvalid
= 0
1470 needsdata
= ["vbarrange"]
1472 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1474 def __init__(self
, barattrs
=[], epsilon
=1e-10, gradient
=color
.gradient
.RedBlack
):
1475 self
.barattrs
= barattrs
1476 self
.epsilon
= epsilon
1477 self
.gradient
= gradient
1479 def lighting(self
, angle
, zindex
):
1480 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1482 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1485 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1486 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1488 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1489 privatedata
.barcanvas
= graph
.insert(canvas
.canvas())
1490 sharedata
.stackedbardraw
= 1
1491 privatedata
.stackedbar
= sharedata
.stackedbar
1492 privatedata
.todraw
= []
1494 def drawpointfill(self
, privatedata
, p
):
1496 privatedata
.barcanvas
.fill(p
, privatedata
.barattrs
)
1498 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1500 for vmin
, vmax
in sharedata
.vbarrange
:
1501 if vmin
is None or vmax
is None:
1502 self
.drawpointfill(privatedata
, None)
1505 vmin
, vmax
= vmax
, vmin
1506 if vmin
> 1 or vmax
< 0:
1507 self
.drawpointfill(privatedata
, None)
1513 vbarrange
.append((vmin
, vmax
))
1514 if len(vbarrange
) == 2:
1515 p
= graph
.vgeodesic(vbarrange
[0][0], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][0])
1516 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][1]))
1517 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][1]))
1518 p
.append(graph
.vgeodesic_el(vbarrange
[0][0], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][0]))
1519 p
.append(path
.closepath())
1520 self
.drawpointfill(privatedata
, p
)
1521 elif len(vbarrange
) == 3:
1523 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[1][0] - vbarrange
[1][1]):
1524 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1525 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1526 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1527 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0]))
1528 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1529 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1530 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1531 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1]))
1532 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1533 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1534 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1535 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1536 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0]))
1537 planes
.append((vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1538 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1539 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1540 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1]))
1541 if abs(vbarrange
[1][0] - vbarrange
[1][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1542 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1543 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1544 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1545 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1]))
1546 planes
.append((vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1547 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1548 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1549 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0]))
1550 v
= [0.5 * (vbarrange
[0][0] + vbarrange
[0][1]),
1551 0.5 * (vbarrange
[1][0] + vbarrange
[1][1]),
1552 0.5 * (vbarrange
[2][0] + vbarrange
[2][1])]
1553 v
[sharedata
.barvalueindex
] = 0.5
1554 zindex
= graph
.vzindex(*v
)
1555 for v11
, v12
, v13
, v21
, v22
, v23
, v31
, v32
, v33
, v41
, v42
, v43
in planes
:
1556 angle
= graph
.vangle(v11
, v12
, v13
, v21
, v22
, v23
, v41
, v42
, v43
)
1558 p
= graph
.vgeodesic(v11
, v12
, v13
, v21
, v22
, v23
)
1559 p
.append(graph
.vgeodesic_el(v21
, v22
, v23
, v31
, v32
, v33
))
1560 p
.append(graph
.vgeodesic_el(v31
, v32
, v33
, v41
, v42
, v43
))
1561 p
.append(graph
.vgeodesic_el(v41
, v42
, v43
, v11
, v12
, v13
))
1562 p
.append(path
.closepath())
1564 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
+ [self
.lighting(angle
, zindex
)]))
1566 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
))
1568 raise TypeError("bar style restricted to two- and three dimensional graphs")
1570 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1571 privatedata
.todraw
.sort()
1572 for vzindex
, p
, a
in privatedata
.todraw
:
1573 privatedata
.barcanvas
.fill(p
, a
)
1575 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1576 selectindex
= privatedata
.stackedbar
1577 selecttotal
= sharedata
.stackedbar
+ 1
1578 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1581 class changebar(bar
):
1583 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1584 if selecttotal
!= 1:
1585 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1587 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1588 if len(graph
.axesnames
) != 2:
1589 raise TypeError("changebar style restricted on two-dimensional graphs (at least for the moment)")
1590 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1591 privatedata
.bars
= []
1593 def drawpointfill(self
, privatedata
, p
):
1594 privatedata
.bars
.append(p
)
1596 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1597 selecttotal
= len(privatedata
.bars
)
1598 for selectindex
, p
in enumerate(privatedata
.bars
):
1600 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1601 privatedata
.barcanvas
.fill(p
, barattrs
)
1603 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1604 raise RuntimeError("Style currently doesn't provide a graph key")
1607 class gridpos(_style
):
1609 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1610 providesdata
= ["values1", "values2", "data12", "data21", "index1", "index2"]
1612 def __init__(self
, index1
=0, index2
=1, epsilon
=1e-10):
1613 self
.index1
= index1
1614 self
.index2
= index2
1615 self
.epsilon
= epsilon
1617 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1618 sharedata
.index1
= self
.index1
1619 sharedata
.index2
= self
.index2
1620 sharedata
.values1
= {}
1621 sharedata
.values2
= {}
1622 sharedata
.data12
= {}
1623 sharedata
.data21
= {}
1625 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1626 if sharedata
.vposavailable
:
1627 sharedata
.value1
= sharedata
.vpos
[self
.index1
]
1628 sharedata
.value2
= sharedata
.vpos
[self
.index2
]
1629 if not sharedata
.values1
.has_key(sharedata
.value1
):
1630 for hasvalue
in sharedata
.values1
.keys():
1631 if hasvalue
- self
.epsilon
<= sharedata
.value1
<= hasvalue
+ self
.epsilon
:
1632 sharedata
.value1
= hasvalue
1635 sharedata
.values1
[sharedata
.value1
] = 1
1636 if not sharedata
.values2
.has_key(sharedata
.value2
):
1637 for hasvalue
in sharedata
.values2
.keys():
1638 if hasvalue
- self
.epsilon
<= sharedata
.value2
<= hasvalue
+ self
.epsilon
:
1639 sharedata
.value2
= hasvalue
1642 sharedata
.values2
[sharedata
.value2
] = 1
1643 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1644 sharedata
.data12
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = data
1645 sharedata
.data21
.setdefault(sharedata
.value2
, {})[sharedata
.value1
] = data
1647 registerdefaultprovider(gridpos(), gridpos
.providesdata
)
1650 class grid(_line
, _style
):
1652 needsdata
= ["values1", "values2", "data12", "data21"]
1654 defaultgridattrs
= [line
.changelinestyle
]
1656 def __init__(self
, gridlines1
=1, gridlines2
=1, gridattrs
=[]):
1657 self
.gridlines1
= gridlines1
1658 self
.gridlines2
= gridlines2
1659 self
.gridattrs
= gridattrs
1661 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1662 if self
.gridattrs
is not None:
1663 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1665 privatedata
.gridattrs
= None
1667 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1668 values1
= sharedata
.values1
.keys()
1670 values2
= sharedata
.values2
.keys()
1673 for value2
in values2
:
1674 data1
= sharedata
.data21
[value2
]
1675 self
.initpointstopath(privatedata
)
1676 for value1
in values1
:
1678 data
= data1
[value1
]
1680 self
.addinvalid(privatedata
)
1682 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1683 p
= self
.donepointstopath(privatedata
)
1685 graph
.stroke(p
, privatedata
.gridattrs
)
1687 for value1
in values1
:
1688 data2
= sharedata
.data12
[value1
]
1689 self
.initpointstopath(privatedata
)
1690 for value2
in values2
:
1692 data
= data2
[value2
]
1694 self
.addinvalid(privatedata
)
1696 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1697 p
= self
.donepointstopath(privatedata
)
1699 graph
.stroke(p
, privatedata
.gridattrs
)
1702 class surface(_style
):
1704 needsdata
= ["values1", "values2", "data12", "data21"]
1706 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Grey
, mincolor
=None, maxcolor
=None,
1707 gridlines1
=0.05, gridlines2
=0.05, gridcolor
=None,
1708 backcolor
=color
.gray
.black
):
1709 self
.colorname
= colorname
1710 self
.gradient
= gradient
1711 self
.mincolor
= mincolor
1712 self
.maxcolor
= maxcolor
1713 self
.gridlines1
= gridlines1
1714 self
.gridlines2
= gridlines2
1715 self
.gridcolor
= gridcolor
1716 self
.backcolor
= backcolor
1718 colorspacestring
= gradient
.getcolor(0).colorspacestring()
1719 if self
.gridcolor
is not None and self
.gridcolor
.colorspacestring() != colorspacestring
:
1720 raise RuntimeError("colorspace mismatch (gradient/grid)")
1721 if self
.backcolor
is not None and self
.backcolor
.colorspacestring() != colorspacestring
:
1722 raise RuntimeError("colorspace mismatch (gradient/back)")
1724 def midvalue(self
, v1
, v2
, v3
, v4
):
1725 return [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1727 def midcolor(self
, c1
, c2
, c3
, c4
):
1728 return 0.25*(c1
+c2
+c3
+c4
)
1730 def lighting(self
, angle
, zindex
):
1731 if angle
< 0 and self
.backcolor
is not None:
1732 return self
.backcolor
1733 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1735 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1736 privatedata
.colorize
= self
.colorname
in columnnames
1737 if privatedata
.colorize
:
1738 return [self
.colorname
]
1741 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1742 privatedata
.colors
= {}
1743 privatedata
.mincolor
= privatedata
.maxcolor
= None
1745 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1746 if privatedata
.colorize
:
1748 color
= point
[self
.colorname
] + 0
1752 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1753 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1754 privatedata
.mincolor
= color
1755 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1756 privatedata
.maxcolor
= color
1758 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1759 v1
= [0]*len(graph
.axesnames
)
1760 v2
= [0]*len(graph
.axesnames
)
1761 v3
= [0]*len(graph
.axesnames
)
1762 v4
= [0]*len(graph
.axesnames
)
1763 v1
[sharedata
.index2
] = 0.5
1764 v2
[sharedata
.index1
] = 0.5
1765 v3
[sharedata
.index1
] = 0.5
1766 v3
[sharedata
.index2
] = 1
1767 v4
[sharedata
.index1
] = 1
1768 v4
[sharedata
.index2
] = 0.5
1769 sortElements
= [-graph
.vzindex(*v1
),
1770 -graph
.vzindex(*v2
),
1771 -graph
.vzindex(*v3
),
1772 -graph
.vzindex(*v4
)]
1774 values1
= sharedata
.values1
.keys()
1776 v1
= [0]*len(graph
.axesnames
)
1777 v2
= [0]*len(graph
.axesnames
)
1778 v1
[sharedata
.index1
] = -1
1779 v2
[sharedata
.index1
] = 1
1781 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1784 sortElements
= [sortElements
[3], sortElements
[1], sortElements
[2], sortElements
[0]]
1786 values2
= sharedata
.values2
.keys()
1788 v1
= [0]*len(graph
.axesnames
)
1789 v2
= [0]*len(graph
.axesnames
)
1790 v1
[sharedata
.index2
] = -1
1791 v2
[sharedata
.index2
] = 1
1792 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1795 sortElements
= [sortElements
[0], sortElements
[2], sortElements
[1], sortElements
[3]]
1797 sortElements
= [(zindex
, i
) for i
, zindex
in enumerate(sortElements
)]
1800 mincolor
, maxcolor
= privatedata
.mincolor
, privatedata
.maxcolor
1801 if self
.mincolor
is not None:
1802 mincolor
= self
.mincolor
1803 if self
.maxcolor
is not None:
1804 maxcolor
= self
.maxcolor
1807 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1808 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1810 available1
, valid1
, v1
= sharedata
.data12
[value1a
][value2a
]
1811 available2
, valid2
, v2
= sharedata
.data12
[value1a
][value2b
]
1812 available3
, valid3
, v3
= sharedata
.data12
[value1b
][value2a
]
1813 available4
, valid4
, v4
= sharedata
.data12
[value1b
][value2b
]
1816 if not available1
or not available2
or not available3
or not available4
:
1818 if not valid1
or not valid2
or not valid3
or not valid4
:
1819 warnings
.warn("surface elements partially outside of the graph are (currently) skipped completely")
1821 def shrink(index
, v1
, v2
, by
):
1824 for i
in builtinrange(3):
1826 v1
[i
], v2
[i
] = v1
[i
] + by
*(v2
[i
]-v1
[i
]), v2
[i
] + by
*(v1
[i
]-v2
[i
])
1828 v1f
, v2f
, v3f
, v4f
= v1
, v2
, v3
, v4
1829 if self
.gridcolor
is not None and self
.gridlines1
:
1830 v1
, v2
= shrink(sharedata
.index1
, v1
, v2
, self
.gridlines1
)
1831 v3
, v4
= shrink(sharedata
.index1
, v3
, v4
, self
.gridlines1
)
1832 if self
.gridcolor
is not None and self
.gridlines2
:
1833 v1
, v3
= shrink(sharedata
.index2
, v1
, v3
, self
.gridlines2
)
1834 v2
, v4
= shrink(sharedata
.index2
, v2
, v4
, self
.gridlines2
)
1835 v5
= self
.midvalue(v1
, v2
, v3
, v4
)
1836 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1837 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1838 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1839 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1840 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1841 if privatedata
.colorize
:
1842 def colorfromgradient(c
):
1843 vc
= (c
- mincolor
) / float(maxcolor
- mincolor
)
1845 warnings
.warn("gradiend color range is exceeded due to mincolor setting")
1848 warnings
.warn("gradiend color range is exceeded due to maxcolor setting")
1850 return self
.gradient
.getcolor(vc
)
1851 c1
= privatedata
.colors
[value1a
][value2a
]
1852 c2
= privatedata
.colors
[value1a
][value2b
]
1853 c3
= privatedata
.colors
[value1b
][value2a
]
1854 c4
= privatedata
.colors
[value1b
][value2b
]
1855 c5
= self
.midcolor(c1
, c2
, c3
, c4
)
1856 c1a
= c1b
= colorfromgradient(c1
)
1857 c2a
= c2c
= colorfromgradient(c2
)
1858 c3b
= c3d
= colorfromgradient(c3
)
1859 c4c
= c4d
= colorfromgradient(c4
)
1860 c5a
= c5b
= c5c
= c5d
= colorfromgradient(c5
)
1861 if self
.backcolor
is not None and sign
*graph
.vangle(*(v1
+v2
+v5
)) < 0:
1862 c1a
= c2a
= c5a
= self
.backcolor
1863 if self
.backcolor
is not None and sign
*graph
.vangle(*(v3
+v1
+v5
)) < 0:
1864 c3b
= c1b
= c5b
= self
.backcolor
1865 if self
.backcolor
is not None and sign
*graph
.vangle(*(v2
+v4
+v5
)) < 0:
1866 c2c
= c4c
= c5c
= self
.backcolor
1867 if self
.backcolor
is not None and sign
*graph
.vangle(*(v4
+v3
+v5
)) < 0:
1868 c4d
= c3d
= c5d
= self
.backcolor
1870 zindex
= graph
.vzindex(*v5
)
1871 c1a
= c2a
= c5a
= self
.lighting(sign
*graph
.vangle(*(v1
+v2
+v5
)), zindex
)
1872 c3b
= c1b
= c5b
= self
.lighting(sign
*graph
.vangle(*(v3
+v1
+v5
)), zindex
)
1873 c2c
= c4c
= c5c
= self
.lighting(sign
*graph
.vangle(*(v2
+v4
+v5
)), zindex
)
1874 c4d
= c3d
= c5d
= self
.lighting(sign
*graph
.vangle(*(v4
+v3
+v5
)), zindex
)
1875 for zindex
, i
in sortElements
:
1877 elements
.append(mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), c1a
),
1878 mesh
.node_pt((x2_pt
, y2_pt
), c2a
),
1879 mesh
.node_pt((x5_pt
, y5_pt
), c5a
))))
1880 if self
.gridcolor
is not None and self
.gridlines2
:
1881 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1882 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1883 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1884 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1885 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1886 mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
))))
1888 elements
.append(mesh
.element((mesh
.node_pt((x3_pt
, y3_pt
), c3b
),
1889 mesh
.node_pt((x1_pt
, y1_pt
), c1b
),
1890 mesh
.node_pt((x5_pt
, y5_pt
), c5b
))))
1891 if self
.gridcolor
is not None and self
.gridlines1
:
1892 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1893 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1894 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1895 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1896 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1897 mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
))))
1899 elements
.append(mesh
.element((mesh
.node_pt((x2_pt
, y2_pt
), c2c
),
1900 mesh
.node_pt((x4_pt
, y4_pt
), c4c
),
1901 mesh
.node_pt((x5_pt
, y5_pt
), c5c
))))
1902 if self
.gridcolor
is not None and self
.gridlines1
:
1903 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1904 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1905 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
))))
1906 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1907 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1908 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1910 elements
.append(mesh
.element((mesh
.node_pt((x4_pt
, y4_pt
), c4d
),
1911 mesh
.node_pt((x3_pt
, y3_pt
), c3d
),
1912 mesh
.node_pt((x5_pt
, y5_pt
), c5d
))))
1913 if self
.gridcolor
is not None and self
.gridlines2
:
1914 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1915 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1916 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
))))
1917 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1918 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1919 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1920 m
= mesh
.mesh(elements
, check
=0)
1924 class bitmap(_style
):
1926 needsdata
= ["values1", "values2", "data12", "data21"]
1928 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Grey
, mincolor
=None, maxcolor
=None, epsilon
=1e-10):
1929 self
.colorname
= colorname
1930 self
.gradient
= gradient
1931 self
.mincolor
= mincolor
1932 self
.maxcolor
= maxcolor
1933 self
.epsilon
= epsilon
1935 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1936 return [self
.colorname
]
1938 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1939 privatedata
.colors
= {}
1940 privatedata
.mincolor
= privatedata
.maxcolor
= None
1941 privatedata
.vfixed
= [None]*len(graph
.axesnames
)
1943 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1945 color
= point
[self
.colorname
] + 0
1949 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1950 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1951 privatedata
.mincolor
= color
1952 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1953 privatedata
.maxcolor
= color
1954 if len(privatedata
.vfixed
) > 2 and sharedata
.vposavailable
:
1955 for i
, (v1
, v2
) in enumerate(zip(privatedata
.vfixed
, sharedata
.vpos
)):
1956 if i
!= sharedata
.index1
and i
!= sharedata
.index2
:
1958 privatedata
.vfixed
[i
] = v2
1959 elif abs(v1
-v2
) > self
.epsilon
:
1960 raise ValueError("data must be in a plane for the bitmap style")
1962 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1963 mincolor
, maxcolor
= privatedata
.mincolor
, privatedata
.maxcolor
1964 if self
.mincolor
is not None:
1965 mincolor
= self
.mincolor
1966 if self
.maxcolor
is not None:
1967 maxcolor
= self
.maxcolor
1969 values1
= pycompat
.sorted(sharedata
.values1
.keys())
1970 values2
= pycompat
.sorted(sharedata
.values2
.keys())
1971 def equidistant(values
):
1974 raise ValueError("several data points required by the bitmap style in each dimension")
1975 range = values
[-1] - values
[0]
1976 for i
, value
in enumerate(values
):
1977 if abs(value
- values
[0] - i
* range / l
) > self
.epsilon
:
1978 raise ValueError("data must be equidistant for the bitmap style")
1979 equidistant(values1
)
1980 equidistant(values2
)
1982 for value2
in values2
:
1983 for value1
in values1
:
1985 available
, valid
, v
= sharedata
.data12
[value1
][value2
]
1995 mode
= {"/DeviceGray": "L",
1996 "/DeviceRGB": "RGB",
1997 "/DeviceCMYK": "CMYK"}[self
.gradient
.getcolor(0).colorspacestring()]
2000 empty
= "\0"*len(mode
)
2001 data
= cStringIO
.StringIO()
2002 for value2
in values2
:
2003 for value1
in values1
:
2005 available
, valid
, v
= sharedata
.data12
[value1
][value2
]
2012 c
= privatedata
.colors
[value1
][value2
]
2013 vc
= (c
- mincolor
) / float(maxcolor
- mincolor
)
2015 warnings
.warn("gradiend color range is exceeded due to mincolor setting")
2018 warnings
.warn("gradiend color range is exceeded due to maxcolor setting")
2020 c
= self
.gradient
.getcolor(vc
)
2022 data
.write(chr(255))
2023 data
.write(c
.to8bitstring())
2024 i
= bitmapmodule
.image(len(values1
), len(values2
), mode
, data
.getvalue())
2026 v1enlargement
= (values1
[-1]-values1
[0])*0.5/len(values1
)
2027 v2enlargement
= (values2
[-1]-values2
[0])*0.5/len(values2
)
2029 privatedata
.vfixed
[sharedata
.index1
] = values1
[0]-v1enlargement
2030 privatedata
.vfixed
[sharedata
.index2
] = values2
[-1]+v2enlargement
2031 x1_pt
, y1_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2032 privatedata
.vfixed
[sharedata
.index1
] = values1
[-1]+v1enlargement
2033 privatedata
.vfixed
[sharedata
.index2
] = values2
[-1]+v2enlargement
2034 x2_pt
, y2_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2035 privatedata
.vfixed
[sharedata
.index1
] = values1
[0]-v1enlargement
2036 privatedata
.vfixed
[sharedata
.index2
] = values2
[0]-v2enlargement
2037 x3_pt
, y3_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2038 t
= trafo
.trafo_pt(((x2_pt
-x1_pt
, x3_pt
-x1_pt
), (y2_pt
-y1_pt
, y3_pt
-y1_pt
)), (x1_pt
, y1_pt
))
2040 privatedata
.vfixed
[sharedata
.index1
] = values1
[-1]+v1enlargement
2041 privatedata
.vfixed
[sharedata
.index2
] = values2
[0]-v2enlargement
2042 vx4
, vy4
= t
.inverse().apply_pt(*graph
.vpos_pt(*privatedata
.vfixed
))
2043 if abs(vx4
- 1) > self
.epsilon
or abs(vy4
- 1) > self
.epsilon
:
2044 raise ValueError("invalid graph layout for bitmap style (bitmap positioning by affine transformation failed)")
2047 privatedata
.vfixed
[sharedata
.index1
] = 0
2048 privatedata
.vfixed
[sharedata
.index2
] = 0
2049 p
.append(path
.moveto_pt(*graph
.vpos_pt(*privatedata
.vfixed
)))
2050 vfixed2
= privatedata
.vfixed
+ privatedata
.vfixed
2051 vfixed2
[sharedata
.index1
] = 0
2052 vfixed2
[sharedata
.index2
] = 0
2053 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 1
2054 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 0
2055 p
.append(graph
.vgeodesic_el(*vfixed2
))
2056 vfixed2
[sharedata
.index1
] = 1
2057 vfixed2
[sharedata
.index2
] = 0
2058 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 1
2059 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 1
2060 p
.append(graph
.vgeodesic_el(*vfixed2
))
2061 vfixed2
[sharedata
.index1
] = 1
2062 vfixed2
[sharedata
.index2
] = 1
2063 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 0
2064 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 1
2065 p
.append(graph
.vgeodesic_el(*vfixed2
))
2066 vfixed2
[sharedata
.index1
] = 0
2067 vfixed2
[sharedata
.index2
] = 1
2068 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 0
2069 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 0
2070 p
.append(graph
.vgeodesic_el(*vfixed2
))
2071 p
.append(path
.closepath())
2073 c
= canvas
.canvas([canvas
.clip(p
)])
2074 b
= bitmapmodule
.bitmap_trafo(t
, i
)