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 if columnname
in [c
+ "min" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_min
]:
267 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
268 if columnname
in [c
+ "max" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_max
]:
269 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
271 # delta handling: fill rangeposdeltacolumns
272 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
273 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
274 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
275 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
276 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
277 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
278 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
279 if columnname
== "d" + usename
and mask
& self
.mask_d
:
280 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
282 # delta handling: process rangeposdeltacolumns
283 for a
, d
in privatedata
.rangeposdeltacolumns
.items():
284 if d
.has_key(self
.mask_value
):
286 if k
!= self
.mask_value
:
287 if k
& (self
.mask_dmin | self
.mask_d
):
289 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
291 mindata
.append(value
-delta
)
294 graph
.axes
[a
].adjustaxis(mindata
)
295 if k
& (self
.mask_dmax | self
.mask_d
):
297 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
299 maxdata
.append(value
+delta
)
302 graph
.axes
[a
].adjustaxis(maxdata
)
305 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
306 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
307 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
308 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
309 for missing
in sharedata
.vrangemissing
:
310 for rangepostmp
in privatedata
.rangepostmplist
:
311 if rangepostmp
[2] >= missing
:
314 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
315 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
317 if mask
& self
.mask_min
:
318 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
319 if mask
& self
.mask_dmin
:
320 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
321 if mask
& self
.mask_d
:
322 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
323 except (ArithmeticError, ValueError, TypeError):
324 sharedata
.vrange
[index
][0] = None
326 if mask
& self
.mask_max
:
327 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
328 if mask
& self
.mask_dmax
:
329 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
330 if mask
& self
.mask_d
:
331 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
332 except (ArithmeticError, ValueError, TypeError):
333 sharedata
.vrange
[index
][1] = None
335 # some range checks for data consistency
336 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
337 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
338 raise ValueError("inverse range")
339 # disabled due to missing vpos access:
340 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
341 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
342 # raise ValueError("negative minimum errorbar")
343 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
344 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
345 # raise ValueError("negative maximum errorbar")
348 registerdefaultprovider(range(), range.providesdata
)
351 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
352 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
353 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
354 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
355 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
357 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
358 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
359 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
360 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
361 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
363 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
364 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
365 path
.lineto_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
.closepath()), attrs
)
370 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
371 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
372 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
373 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
374 path
.closepath()), attrs
)
376 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
377 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
378 path
.closepath()), attrs
)
380 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
381 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
382 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
383 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
384 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
385 path
.closepath()), attrs
)
388 class _styleneedingpointpos(_style
):
390 needsdata
= ["vposmissing"]
392 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
393 if len(sharedata
.vposmissing
):
394 raise ValueError("incomplete position information")
398 class symbol(_styleneedingpointpos
):
400 needsdata
= ["vpos", "vposmissing", "vposvalid"]
402 # "inject" the predefinied symbols into the class:
404 # Note, that statements like cross = _crosssymbol are
405 # invalid, since the would lead to unbound methods, but
406 # a single entry changeable list does the trick.
408 # Once we require Python 2.2+ we should use staticmethods
409 # to implement the default symbols inplace.
411 cross
= attr
.changelist([_crosssymbol
])
412 plus
= attr
.changelist([_plussymbol
])
413 square
= attr
.changelist([_squaresymbol
])
414 triangle
= attr
.changelist([_trianglesymbol
])
415 circle
= attr
.changelist([_circlesymbol
])
416 diamond
= attr
.changelist([_diamondsymbol
])
418 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
419 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
420 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
421 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
422 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
423 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
424 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
425 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
426 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
427 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
429 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
430 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
432 defaultsymbolattrs
= [deco
.stroked
]
434 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
437 self
.symbolattrs
= symbolattrs
439 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
440 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
441 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
442 if self
.symbolattrs
is not None:
443 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
445 privatedata
.symbolattrs
= None
447 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
448 privatedata
.symbolcanvas
= canvas
.canvas()
450 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
451 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
452 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
453 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
455 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
456 graph
.insert(privatedata
.symbolcanvas
)
458 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
459 if privatedata
.symbolattrs
is not None:
460 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
463 class _line(_styleneedingpointpos
):
465 # this style is not a complete style, but it provides the basic functionality to
466 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
468 def initpointstopath(self
, privatedata
):
469 privatedata
.path
= path
.path()
470 privatedata
.linebasepoints
= []
471 privatedata
.lastvpos
= None
473 def addpointstopath(self
, privatedata
):
474 # add baselinepoints to privatedata.path
475 if len(privatedata
.linebasepoints
) > 1:
476 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
477 if len(privatedata
.linebasepoints
) > 2:
478 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
480 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
481 privatedata
.linebasepoints
= []
483 def addpoint(self
, privatedata
, graphvpos_pt
, vposavailable
, vposvalid
, vpos
):
484 # append linebasepoints
486 if len(privatedata
.linebasepoints
):
487 # the last point was inside the graph
488 if vposvalid
: # shortcut for the common case
489 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
493 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
496 # 1 = vstart + (vend - vstart) * cut
498 newcut
= (1 - vstart
)/(vend
- vstart
)
499 except (ArithmeticError, TypeError):
502 # 0 = vstart + (vend - vstart) * cut
504 newcut
= - vstart
/(vend
- vstart
)
505 except (ArithmeticError, TypeError):
507 if newcut
is not None and newcut
< cut
:
511 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
512 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
513 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
514 self
.addpointstopath(privatedata
)
516 # the last point was outside the graph
517 if privatedata
.lastvpos
is not None:
521 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
524 # 1 = vstart + (vend - vstart) * cut
526 newcut
= (1 - vstart
)/(vend
- vstart
)
527 except (ArithmeticError, TypeError):
530 # 0 = vstart + (vend - vstart) * cut
532 newcut
= - vstart
/(vend
- vstart
)
533 except (ArithmeticError, TypeError):
535 if newcut
is not None and newcut
> cut
:
539 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
540 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
541 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
542 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
544 # sometimes cut beginning and end
547 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
552 # 1 = vstart + (vend - vstart) * cutfrom
554 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
555 except (ArithmeticError, TypeError):
560 # 0 = vstart + (vend - vstart) * cutfrom
562 newcutfrom
= - vstart
/(vend
- vstart
)
563 except (ArithmeticError, TypeError):
565 if newcutfrom
is not None and newcutfrom
> cutfrom
:
569 # 1 = vstart + (vend - vstart) * cutto
571 newcutto
= (1 - vstart
)/(vend
- vstart
)
572 except (ArithmeticError, TypeError):
575 # 0 = vstart + (vend - vstart) * cutto
577 newcutto
= - vstart
/(vend
- vstart
)
578 except (ArithmeticError, TypeError):
580 if newcutto
is not None and newcutto
< cutto
:
586 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
587 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
588 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
589 privatedata
.linebasepoints
.append(graphvpos_pt(*cutfromvpos
))
590 privatedata
.linebasepoints
.append(graphvpos_pt(*cuttovpos
))
591 self
.addpointstopath(privatedata
)
592 privatedata
.lastvpos
= vpos
[:]
594 if len(privatedata
.linebasepoints
) > 1:
595 self
.addpointstopath(privatedata
)
596 privatedata
.lastvpos
= None
598 def addinvalid(self
, privatedata
):
599 if len(privatedata
.linebasepoints
) > 1:
600 self
.addpointstopath(privatedata
)
601 privatedata
.lastvpos
= None
603 def donepointstopath(self
, privatedata
):
604 if len(privatedata
.linebasepoints
) > 1:
605 self
.addpointstopath(privatedata
)
606 return privatedata
.path
611 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
613 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
614 style
.linestyle
.dashed
,
615 style
.linestyle
.dotted
,
616 style
.linestyle
.dashdotted
])
618 defaultlineattrs
= [changelinestyle
]
620 def __init__(self
, lineattrs
=[]):
621 self
.lineattrs
= lineattrs
623 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
624 if self
.lineattrs
is not None:
625 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
627 privatedata
.lineattrs
= None
629 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
630 self
.initpointstopath(privatedata
)
632 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
633 self
.addpoint(privatedata
, graph
.vpos_pt
, sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
)
635 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
636 path
= self
.donepointstopath(privatedata
)
637 if privatedata
.lineattrs
is not None and len(path
):
638 graph
.stroke(path
, privatedata
.lineattrs
)
640 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
641 if privatedata
.lineattrs
is not None:
642 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
)
645 class impulses(_styleneedingpointpos
):
647 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
649 defaultlineattrs
= [line
.changelinestyle
]
650 defaultfrompathattrs
= []
652 def __init__(self
, lineattrs
=[], fromvalue
=0, frompathattrs
=[], valueaxisindex
=1):
653 self
.lineattrs
= lineattrs
654 self
.fromvalue
= fromvalue
655 self
.frompathattrs
= frompathattrs
656 self
.valueaxisindex
= valueaxisindex
658 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
659 privatedata
.insertfrompath
= selectindex
== 0
660 if self
.lineattrs
is not None:
661 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
663 privatedata
.lineattrs
= None
665 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
666 if self
.fromvalue
is not None:
668 i
= sharedata
.poscolumnnames
.index(columnname
)
672 if i
== self
.valueaxisindex
:
673 graph
.axes
[sharedata
.poscolumnnames
[i
]].adjustaxis([self
.fromvalue
])
675 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
676 privatedata
.impulsescanvas
= canvas
.canvas()
677 if self
.fromvalue
is not None:
678 valueaxisname
= sharedata
.poscolumnnames
[self
.valueaxisindex
]
679 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
680 privatedata
.vfromvaluecut
= 0
681 if privatedata
.vfromvalue
< 0:
682 privatedata
.vfromvalue
= 0
683 if privatedata
.vfromvalue
> 1:
684 privatedata
.vfromvalue
= 1
685 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
686 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
687 self
.defaultfrompathattrs
+ self
.frompathattrs
)
689 privatedata
.vfromvalue
= 0
691 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
692 if sharedata
.vposvalid
and privatedata
.lineattrs
is not None:
693 vpos
= sharedata
.vpos
[:]
694 vpos
[self
.valueaxisindex
] = privatedata
.vfromvalue
695 privatedata
.impulsescanvas
.stroke(graph
.vgeodesic(*(vpos
+ sharedata
.vpos
)), privatedata
.lineattrs
)
697 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
698 graph
.insert(privatedata
.impulsescanvas
)
700 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
701 if privatedata
.lineattrs
is not None:
702 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
)
705 class errorbar(_style
):
707 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
709 defaulterrorbarattrs
= []
711 def __init__(self
, size
=0.1*unit
.v_cm
,
715 self
.errorbarattrs
= errorbarattrs
716 self
.epsilon
= epsilon
718 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
719 for i
in sharedata
.vposmissing
:
720 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
721 raise ValueError("position and range for a graph dimension missing")
724 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
725 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
726 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
728 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
729 if privatedata
.errorbarattrs
is not None:
730 privatedata
.errorbarcanvas
= canvas
.canvas(privatedata
.errorbarattrs
)
731 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
733 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
734 if privatedata
.errorbarattrs
is not None:
735 for i
in privatedata
.dimensionlist
:
736 for j
in privatedata
.dimensionlist
:
738 (sharedata
.vpos
[j
] is None or
739 sharedata
.vpos
[j
] < -self
.epsilon
or
740 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
743 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
744 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
745 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
747 vminpos
= sharedata
.vpos
[:]
748 if sharedata
.vrange
[i
][0] is not None:
749 vminpos
[i
] = sharedata
.vrange
[i
][0]
753 if vminpos
[i
] > 1+self
.epsilon
:
755 if vminpos
[i
] < -self
.epsilon
:
758 vmaxpos
= sharedata
.vpos
[:]
759 if sharedata
.vrange
[i
][1] is not None:
760 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
764 if vmaxpos
[i
] < -self
.epsilon
:
766 if vmaxpos
[i
] > 1+self
.epsilon
:
769 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
770 for j
in privatedata
.dimensionlist
:
773 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
775 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
777 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
778 if privatedata
.errorbarattrs
is not None:
779 graph
.insert(privatedata
.errorbarcanvas
)
782 class text(_styleneedingpointpos
):
784 needsdata
= ["vpos", "vposmissing", "vposvalid"]
786 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
788 def __init__(self
, textname
="text", dxname
=None, dyname
=None,
789 dxunit
=0.3*unit
.v_cm
, dyunit
=0.3*unit
.v_cm
,
790 textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
791 self
.textname
= textname
798 self
.textattrs
= textattrs
800 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
801 if self
.textname
not in columnnames
:
802 raise ValueError("column '%s' missing" % self
.textname
)
803 names
= [self
.textname
]
804 if self
.dxname
is not None:
805 if self
.dxname
not in columnnames
:
806 raise ValueError("column '%s' missing" % self
.dxname
)
807 names
.append(self
.dxname
)
808 if self
.dyname
is not None:
809 if self
.dyname
not in columnnames
:
810 raise ValueError("column '%s' missing" % self
.dyname
)
811 names
.append(self
.dyname
)
812 return names
+ _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
814 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
815 if self
.textattrs
is not None:
816 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
818 privatedata
.textattrs
= None
820 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
821 if self
.dxname
is None:
822 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
824 privatedata
.dxunit_pt
= unit
.topt(self
.dxunit
)
825 if self
.dyname
is None:
826 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
828 privatedata
.dyunit_pt
= unit
.topt(self
.dyunit
)
830 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
831 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
832 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
834 text
= str(point
[self
.textname
])
838 if self
.dxname
is None:
839 dx_pt
= privatedata
.textdx_pt
841 dx_pt
= float(point
[self
.dxname
]) * privatedata
.dxunit_pt
842 if self
.dyname
is None:
843 dy_pt
= privatedata
.textdy_pt
845 dy_pt
= float(point
[self
.dyname
]) * privatedata
.dyunit_pt
846 graph
.text_pt(x_pt
+ dx_pt
, y_pt
+ dy_pt
, text
, privatedata
.textattrs
)
848 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
849 raise RuntimeError("Style currently doesn't provide a graph key")
852 class arrow(_styleneedingpointpos
):
854 needsdata
= ["vpos", "vposmissing", "vposvalid"]
856 defaultlineattrs
= []
857 defaultarrowattrs
= []
859 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
):
860 self
.linelength
= linelength
861 self
.arrowsize
= arrowsize
862 self
.lineattrs
= lineattrs
863 self
.arrowattrs
= arrowattrs
864 self
.arrowpos
= arrowpos
865 self
.epsilon
= epsilon
866 self
.decorator
= decorator
868 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
869 if len(graph
.axesnames
) != 2:
870 raise ValueError("arrow style restricted on two-dimensional graphs")
871 if "size" not in columnnames
:
872 raise ValueError("size missing")
873 if "angle" not in columnnames
:
874 raise ValueError("angle missing")
875 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
877 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
878 if self
.lineattrs
is not None:
879 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
881 privatedata
.lineattrs
= None
882 if self
.arrowattrs
is not None:
883 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
885 privatedata
.arrowattrs
= None
887 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
888 privatedata
.arrowcanvas
= canvas
.canvas()
890 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
891 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
892 linelength_pt
= unit
.topt(self
.linelength
)
893 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
895 angle
= point
["angle"] + 0.0
896 size
= point
["size"] + 0.0
900 if point
["size"] > self
.epsilon
:
901 dx
= math
.cos(angle
*math
.pi
/180)
902 dy
= math
.sin(angle
*math
.pi
/180)
903 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
904 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
905 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
906 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
908 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
),
909 privatedata
.lineattrs
+[self
.decorator(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
911 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
)
913 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
914 graph
.insert(privatedata
.arrowcanvas
)
916 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
917 raise RuntimeError("Style currently doesn't provide a graph key")
922 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
924 def __init__(self
, gradient
=color
.gradient
.Grey
):
925 self
.gradient
= gradient
927 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
928 if len(graph
.axesnames
) != 2:
929 raise TypeError("arrow style restricted on two-dimensional graphs")
930 if "color" not in columnnames
:
931 raise ValueError("color missing")
932 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
933 raise ValueError("incomplete range")
936 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
937 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
939 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
940 xvmin
= sharedata
.vrange
[0][0]
941 xvmax
= sharedata
.vrange
[0][1]
942 yvmin
= sharedata
.vrange
[1][0]
943 yvmax
= sharedata
.vrange
[1][1]
944 if (xvmin
is not None and xvmin
< 1 and
945 xvmax
is not None and xvmax
> 0 and
946 yvmin
is not None and yvmin
< 1 and
947 yvmax
is not None and yvmax
> 0):
956 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
957 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
958 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
959 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
960 p
.append(path
.closepath())
961 privatedata
.rectcanvas
.fill(p
, [self
.gradient
.getcolor(point
["color"])])
963 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
964 raise RuntimeError("Style currently doesn't provide a graph key")
967 class histogram(_style
):
969 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
971 defaultlineattrs
= [deco
.stroked
]
972 defaultfrompathattrs
= []
974 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0, rectkey
=0,
975 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
976 self
.lineattrs
= lineattrs
978 self
.fromvalue
= fromvalue
979 self
.frompathattrs
= frompathattrs
980 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
981 self
.rectkey
= rectkey
982 self
.autohistogramaxisindex
= autohistogramaxisindex
983 self
.autohistogrampointpos
= autohistogrampointpos
984 self
.epsilon
= epsilon
986 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
987 if len(graph
.axesnames
) != 2:
988 raise TypeError("histogram style restricted on two-dimensional graphs")
989 privatedata
.rangeaxisindex
= None
990 for i
in builtinrange(len(graph
.axesnames
)):
991 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
992 if i
in sharedata
.vposmissing
:
993 raise ValueError("pos and range missing")
995 if privatedata
.rangeaxisindex
is not None:
996 raise ValueError("multiple ranges")
997 privatedata
.rangeaxisindex
= i
998 if privatedata
.rangeaxisindex
is None:
999 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
1000 privatedata
.autohistogram
= 1
1002 privatedata
.autohistogram
= 0
1005 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1006 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
1008 raise ValueError("several data points needed for automatic histogram width calculation")
1010 delta
= data
[1] - data
[0]
1011 min = data
[0] - self
.autohistogrampointpos
* delta
1012 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
1013 graph
.axes
[columnname
].adjustaxis([min, max])
1014 elif self
.fromvalue
is not None and columnname
== sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]:
1015 graph
.axes
[columnname
].adjustaxis([self
.fromvalue
])
1017 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1018 privatedata
.insertfrompath
= selectindex
== 0
1019 if self
.lineattrs
is not None:
1020 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
1022 privatedata
.lineattrs
= None
1024 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
1025 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
1026 if privatedata
.rangeaxisindex
:
1027 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
1029 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
1031 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
1032 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
1047 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
1048 if vvalue1cut
and not self
.fillable
:
1049 if privatedata
.rangeaxisindex
:
1050 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
1052 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
1053 if privatedata
.rangeaxisindex
:
1054 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
1056 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
1058 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
1060 if vvalue
< -self
.epsilon
:
1062 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1063 if vvalue
> 1+self
.epsilon
:
1065 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1066 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1081 if abs(vpos1cut
+ vpos2cut
) <= 1:
1084 if privatedata
.rangeaxisindex
:
1085 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1086 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1088 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1089 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1091 if privatedata
.rangeaxisindex
:
1092 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1094 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1095 if privatedata
.rangeaxisindex
:
1096 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1098 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1099 if self
.fillable
and vpos2cut
:
1100 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1101 if privatedata
.rangeaxisindex
:
1102 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1104 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1106 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1107 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1108 if self
.fillable
and not self
.steps
:
1109 if not currentvalid
:
1112 if vmin
< -self
.epsilon
:
1115 elif vmin
> 1+self
.epsilon
:
1119 if vmax
< -self
.epsilon
:
1122 if vmax
> 1+self
.epsilon
:
1126 if vvalue
< -self
.epsilon
:
1129 if vvalue
> 1+self
.epsilon
:
1133 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1134 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1137 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1140 if privatedata
.rangeaxisindex
:
1141 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1142 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1143 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1144 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1146 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1147 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1148 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1149 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1152 if privatedata
.rangeaxisindex
:
1153 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1154 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1155 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1156 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1158 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1159 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1160 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1161 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1162 elif privatedata
.vfromvaluecut
:
1164 if privatedata
.rangeaxisindex
:
1165 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1166 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1167 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1168 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1170 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1171 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1172 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1173 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1176 if privatedata
.rangeaxisindex
:
1177 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1178 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1179 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1180 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1182 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1183 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1184 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1185 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1187 if privatedata
.rangeaxisindex
:
1188 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1189 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1190 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1191 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1192 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1193 privatedata
.path
.append(path
.closepath())
1195 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1196 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1197 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1198 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1199 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1200 privatedata
.path
.append(path
.closepath())
1203 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1204 except (ArithmeticError, ValueError, TypeError):
1206 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1207 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1208 self
.vposline(privatedata
, sharedata
, graph
,
1209 vmin
, privatedata
.lastvvalue
, vvalue
)
1211 if privatedata
.lastvvalue
is not None and currentvalid
:
1212 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1213 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1214 self
.vposline(privatedata
, sharedata
, graph
,
1215 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1217 self
.vmoveto(privatedata
, sharedata
, graph
,
1219 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1220 self
.vmoveto(privatedata
, sharedata
, graph
,
1221 vmin
, privatedata
.vfromvalue
)
1222 self
.vposline(privatedata
, sharedata
, graph
,
1223 vmin
, privatedata
.vfromvalue
, vvalue
)
1225 self
.vvalueline(privatedata
, sharedata
, graph
,
1227 privatedata
.lastvvalue
= vvalue
1228 privatedata
.lastvmax
= vmax
1230 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1232 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1233 privatedata
.path
= path
.path()
1234 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1235 privatedata
.vcurrentpoint
= None
1236 privatedata
.count
= 0
1237 if self
.fromvalue
is not None:
1238 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1239 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1240 privatedata
.vfromvaluecut
= 0
1241 if privatedata
.vfromvalue
< 0:
1242 privatedata
.vfromvalue
= 0
1243 privatedata
.vfromvaluecut
= -1
1244 if privatedata
.vfromvalue
> 1:
1245 privatedata
.vfromvalue
= 1
1246 privatedata
.vfromvaluecut
= 1
1247 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1248 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1249 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1251 privatedata
.vfromvalue
= 0
1253 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1254 if privatedata
.autohistogram
:
1255 # automatic range handling
1256 privatedata
.count
+= 1
1257 if privatedata
.count
== 2:
1258 if privatedata
.rangeaxisindex
:
1259 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1260 self
.drawvalue(privatedata
, sharedata
, graph
,
1261 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1262 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1263 privatedata
.lastvpos
[0])
1265 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1266 self
.drawvalue(privatedata
, sharedata
, graph
,
1267 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1268 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1269 privatedata
.lastvpos
[1])
1270 elif privatedata
.count
> 2:
1271 if privatedata
.rangeaxisindex
:
1272 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1274 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1275 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1276 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1277 if privatedata
.count
> 1:
1278 if privatedata
.rangeaxisindex
:
1279 self
.drawvalue(privatedata
, sharedata
, graph
,
1280 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1281 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1284 self
.drawvalue(privatedata
, sharedata
, graph
,
1285 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1286 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1288 privatedata
.lastvpos
= sharedata
.vpos
[:]
1290 if privatedata
.rangeaxisindex
:
1291 self
.drawvalue(privatedata
, sharedata
, graph
,
1292 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1294 self
.drawvalue(privatedata
, sharedata
, graph
,
1295 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1297 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1298 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1299 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1300 graph
.draw(privatedata
.path
, privatedata
.lineattrs
)
1302 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1303 if privatedata
.lineattrs
is not None:
1305 p
= path
.rect_pt(x_pt
, y_pt
, width_pt
, height_pt
)
1307 p
= path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
)
1308 graph
.draw(p
, privatedata
.lineattrs
)
1311 class barpos(_style
):
1313 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1315 defaultfrompathattrs
= []
1317 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1318 self
.fromvalue
= fromvalue
1319 self
.frompathattrs
= frompathattrs
1320 self
.epsilon
= epsilon
1322 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1323 sharedata
.barposcolumnnames
= []
1324 sharedata
.barvalueindex
= None
1325 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1327 for axisname
in axisnames
:
1328 if axisname
in columnnames
:
1329 if sharedata
.barvalueindex
is not None:
1330 raise ValueError("multiple values")
1331 sharedata
.barvalueindex
= dimension
1332 sharedata
.barposcolumnnames
.append(axisname
)
1334 if (axisname
+ "name") in columnnames
:
1335 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1338 raise ValueError("multiple names and value")
1340 raise ValueError("value/name missing")
1341 if sharedata
.barvalueindex
is None:
1342 raise ValueError("missing value")
1343 sharedata
.vposmissing
= []
1344 return sharedata
.barposcolumnnames
1346 def addsubvalue(self
, value
, subvalue
):
1351 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1353 return value
, subvalue
1355 return value
, subvalue
1357 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1359 i
= sharedata
.barposcolumnnames
.index(columnname
)
1363 if i
== sharedata
.barvalueindex
:
1364 if self
.fromvalue
is not None:
1365 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1366 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1368 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1369 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1371 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1372 privatedata
.insertfrompath
= selectindex
== 0
1374 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1375 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1376 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1377 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1379 if self
.fromvalue
is not None:
1380 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(self
.fromvalue
)
1381 if privatedata
.vfromvalue
< 0:
1382 privatedata
.vfromvalue
= 0
1383 if privatedata
.vfromvalue
> 1:
1384 privatedata
.vfromvalue
= 1
1385 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1386 graph
.stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].vgridpath(privatedata
.vfromvalue
),
1387 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1389 privatedata
.vfromvalue
= 0
1391 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1392 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1393 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1394 if i
== sharedata
.barvalueindex
:
1395 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1396 sharedata
.lastbarvalue
= point
[barname
]
1398 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1399 except (ArithmeticError, ValueError, TypeError):
1400 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1404 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1405 except (ArithmeticError, ValueError, TypeError):
1406 sharedata
.vbarrange
[i
][j
] = None
1408 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1409 except (ArithmeticError, ValueError, TypeError):
1410 sharedata
.vpos
[i
] = None
1411 if sharedata
.vpos
[i
] is None:
1412 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1413 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1414 sharedata
.vposvalid
= 0
1416 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1419 class stackedbarpos(_style
):
1421 # provides no additional data, but needs some data (and modifies some of them)
1422 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1424 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1425 self
.stackname
= stackname
1426 self
.epsilon
= epsilon
1427 self
.addontop
= addontop
1429 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1430 if self
.stackname
not in columnnames
:
1431 raise ValueError("column '%s' missing" % self
.stackname
)
1432 return [self
.stackname
]
1434 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1435 if columnname
== self
.stackname
:
1436 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1438 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1439 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1440 sharedata
.stackedbar
+= 1
1442 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1443 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1446 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1447 except (ArithmeticError, ValueError, TypeError):
1448 sharedata
.lastbarvalue
= None
1450 sharedata
.lastbarvalue
= point
[self
.stackname
]
1452 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1453 except (ArithmeticError, ValueError, TypeError):
1454 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1455 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1457 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1458 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1459 for v
in sharedata
.vpos
:
1461 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1463 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1464 sharedata
.vposvalid
= 0
1469 needsdata
= ["vbarrange"]
1471 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1473 def __init__(self
, barattrs
=[], epsilon
=1e-10, gradient
=color
.gradient
.RedBlack
):
1474 self
.barattrs
= barattrs
1475 self
.epsilon
= epsilon
1476 self
.gradient
= gradient
1478 def lighting(self
, angle
, zindex
):
1479 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1481 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1484 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1485 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1487 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1488 privatedata
.barcanvas
= graph
.insert(canvas
.canvas())
1489 sharedata
.stackedbardraw
= 1
1490 privatedata
.stackedbar
= sharedata
.stackedbar
1491 privatedata
.todraw
= []
1493 def drawpointfill(self
, privatedata
, p
):
1495 privatedata
.barcanvas
.fill(p
, privatedata
.barattrs
)
1497 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1499 for vmin
, vmax
in sharedata
.vbarrange
:
1500 if vmin
is None or vmax
is None:
1501 self
.drawpointfill(privatedata
, None)
1504 vmin
, vmax
= vmax
, vmin
1505 if vmin
> 1 or vmax
< 0:
1506 self
.drawpointfill(privatedata
, None)
1512 vbarrange
.append((vmin
, vmax
))
1513 if len(vbarrange
) == 2:
1514 p
= graph
.vgeodesic(vbarrange
[0][0], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][0])
1515 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][1]))
1516 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][1]))
1517 p
.append(graph
.vgeodesic_el(vbarrange
[0][0], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][0]))
1518 p
.append(path
.closepath())
1519 self
.drawpointfill(privatedata
, p
)
1520 elif len(vbarrange
) == 3:
1522 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[1][0] - vbarrange
[1][1]):
1523 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1524 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1525 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1526 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0]))
1527 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1528 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1529 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1530 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1]))
1531 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1532 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1533 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1534 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1535 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0]))
1536 planes
.append((vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1537 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1538 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1539 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1]))
1540 if abs(vbarrange
[1][0] - vbarrange
[1][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1541 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1542 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1543 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1544 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1]))
1545 planes
.append((vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1546 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1547 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1548 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0]))
1549 v
= [0.5 * (vbarrange
[0][0] + vbarrange
[0][1]),
1550 0.5 * (vbarrange
[1][0] + vbarrange
[1][1]),
1551 0.5 * (vbarrange
[2][0] + vbarrange
[2][1])]
1552 v
[sharedata
.barvalueindex
] = 0.5
1553 zindex
= graph
.vzindex(*v
)
1554 for v11
, v12
, v13
, v21
, v22
, v23
, v31
, v32
, v33
, v41
, v42
, v43
in planes
:
1555 angle
= graph
.vangle(v11
, v12
, v13
, v21
, v22
, v23
, v41
, v42
, v43
)
1557 p
= graph
.vgeodesic(v11
, v12
, v13
, v21
, v22
, v23
)
1558 p
.append(graph
.vgeodesic_el(v21
, v22
, v23
, v31
, v32
, v33
))
1559 p
.append(graph
.vgeodesic_el(v31
, v32
, v33
, v41
, v42
, v43
))
1560 p
.append(graph
.vgeodesic_el(v41
, v42
, v43
, v11
, v12
, v13
))
1561 p
.append(path
.closepath())
1563 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
+ [self
.lighting(angle
, zindex
)]))
1565 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
))
1567 raise TypeError("bar style restricted to two- and three dimensional graphs")
1569 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1570 privatedata
.todraw
.sort()
1571 for vzindex
, p
, a
in privatedata
.todraw
:
1572 privatedata
.barcanvas
.fill(p
, a
)
1574 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1575 selectindex
= privatedata
.stackedbar
1576 selecttotal
= sharedata
.stackedbar
+ 1
1577 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1580 class changebar(bar
):
1582 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1583 if selecttotal
!= 1:
1584 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1586 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1587 if len(graph
.axesnames
) != 2:
1588 raise TypeError("changebar style restricted on two-dimensional graphs (at least for the moment)")
1589 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1590 privatedata
.bars
= []
1592 def drawpointfill(self
, privatedata
, p
):
1593 privatedata
.bars
.append(p
)
1595 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1596 selecttotal
= len(privatedata
.bars
)
1597 for selectindex
, p
in enumerate(privatedata
.bars
):
1599 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1600 privatedata
.barcanvas
.fill(p
, barattrs
)
1602 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1603 raise RuntimeError("Style currently doesn't provide a graph key")
1606 class gridpos(_style
):
1608 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1609 providesdata
= ["values1", "values2", "data12", "data21", "index1", "index2"]
1611 def __init__(self
, index1
=0, index2
=1, epsilon
=1e-10):
1612 self
.index1
= index1
1613 self
.index2
= index2
1614 self
.epsilon
= epsilon
1616 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1617 sharedata
.index1
= self
.index1
1618 sharedata
.index2
= self
.index2
1619 sharedata
.values1
= {}
1620 sharedata
.values2
= {}
1621 sharedata
.data12
= {}
1622 sharedata
.data21
= {}
1624 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1625 if sharedata
.vposavailable
:
1626 sharedata
.value1
= sharedata
.vpos
[self
.index1
]
1627 sharedata
.value2
= sharedata
.vpos
[self
.index2
]
1628 if not sharedata
.values1
.has_key(sharedata
.value1
):
1629 for hasvalue
in sharedata
.values1
.keys():
1630 if hasvalue
- self
.epsilon
<= sharedata
.value1
<= hasvalue
+ self
.epsilon
:
1631 sharedata
.value1
= hasvalue
1634 sharedata
.values1
[sharedata
.value1
] = 1
1635 if not sharedata
.values2
.has_key(sharedata
.value2
):
1636 for hasvalue
in sharedata
.values2
.keys():
1637 if hasvalue
- self
.epsilon
<= sharedata
.value2
<= hasvalue
+ self
.epsilon
:
1638 sharedata
.value2
= hasvalue
1641 sharedata
.values2
[sharedata
.value2
] = 1
1642 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1643 sharedata
.data12
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = data
1644 sharedata
.data21
.setdefault(sharedata
.value2
, {})[sharedata
.value1
] = data
1646 registerdefaultprovider(gridpos(), gridpos
.providesdata
)
1649 class grid(_line
, _style
):
1651 needsdata
= ["values1", "values2", "data12", "data21"]
1653 defaultgridattrs
= [line
.changelinestyle
]
1655 def __init__(self
, gridlines1
=1, gridlines2
=1, gridattrs
=[]):
1656 self
.gridlines1
= gridlines1
1657 self
.gridlines2
= gridlines2
1658 self
.gridattrs
= gridattrs
1660 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1661 if self
.gridattrs
is not None:
1662 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1664 privatedata
.gridattrs
= None
1666 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1667 values1
= sharedata
.values1
.keys()
1669 values2
= sharedata
.values2
.keys()
1672 for value2
in values2
:
1673 data1
= sharedata
.data21
[value2
]
1674 self
.initpointstopath(privatedata
)
1675 for value1
in values1
:
1677 data
= data1
[value1
]
1679 self
.addinvalid(privatedata
)
1681 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1682 p
= self
.donepointstopath(privatedata
)
1684 graph
.stroke(p
, privatedata
.gridattrs
)
1686 for value1
in values1
:
1687 data2
= sharedata
.data12
[value1
]
1688 self
.initpointstopath(privatedata
)
1689 for value2
in values2
:
1691 data
= data2
[value2
]
1693 self
.addinvalid(privatedata
)
1695 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1696 p
= self
.donepointstopath(privatedata
)
1698 graph
.stroke(p
, privatedata
.gridattrs
)
1701 class surface(_style
):
1703 needsdata
= ["values1", "values2", "data12", "data21"]
1705 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Grey
, mincolor
=None, maxcolor
=None,
1706 gridlines1
=0.05, gridlines2
=0.05, gridcolor
=None,
1707 backcolor
=color
.gray
.black
):
1708 self
.colorname
= colorname
1709 self
.gradient
= gradient
1710 self
.mincolor
= mincolor
1711 self
.maxcolor
= maxcolor
1712 self
.gridlines1
= gridlines1
1713 self
.gridlines2
= gridlines2
1714 self
.gridcolor
= gridcolor
1715 self
.backcolor
= backcolor
1717 colorspacestring
= gradient
.getcolor(0).colorspacestring()
1718 if self
.gridcolor
is not None and self
.gridcolor
.colorspacestring() != colorspacestring
:
1719 raise RuntimeError("colorspace mismatch (gradient/grid)")
1720 if self
.backcolor
is not None and self
.backcolor
.colorspacestring() != colorspacestring
:
1721 raise RuntimeError("colorspace mismatch (gradient/back)")
1723 def midvalue(self
, v1
, v2
, v3
, v4
):
1724 return [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1726 def midcolor(self
, c1
, c2
, c3
, c4
):
1727 return 0.25*(c1
+c2
+c3
+c4
)
1729 def lighting(self
, angle
, zindex
):
1730 if angle
< 0 and self
.backcolor
is not None:
1731 return self
.backcolor
1732 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1734 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1735 privatedata
.colorize
= self
.colorname
in columnnames
1736 if privatedata
.colorize
:
1737 return [self
.colorname
]
1740 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1741 privatedata
.colors
= {}
1742 privatedata
.mincolor
= privatedata
.maxcolor
= None
1744 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1745 if privatedata
.colorize
:
1747 color
= point
[self
.colorname
] + 0
1751 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1752 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1753 privatedata
.mincolor
= color
1754 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1755 privatedata
.maxcolor
= color
1757 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1758 v1
= [0]*len(graph
.axesnames
)
1759 v2
= [0]*len(graph
.axesnames
)
1760 v3
= [0]*len(graph
.axesnames
)
1761 v4
= [0]*len(graph
.axesnames
)
1762 v1
[sharedata
.index2
] = 0.5
1763 v2
[sharedata
.index1
] = 0.5
1764 v3
[sharedata
.index1
] = 0.5
1765 v3
[sharedata
.index2
] = 1
1766 v4
[sharedata
.index1
] = 1
1767 v4
[sharedata
.index2
] = 0.5
1768 sortElements
= [-graph
.vzindex(*v1
),
1769 -graph
.vzindex(*v2
),
1770 -graph
.vzindex(*v3
),
1771 -graph
.vzindex(*v4
)]
1773 values1
= sharedata
.values1
.keys()
1775 v1
= [0]*len(graph
.axesnames
)
1776 v2
= [0]*len(graph
.axesnames
)
1777 v1
[sharedata
.index1
] = -1
1778 v2
[sharedata
.index1
] = 1
1780 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1783 sortElements
= [sortElements
[3], sortElements
[1], sortElements
[2], sortElements
[0]]
1785 values2
= sharedata
.values2
.keys()
1787 v1
= [0]*len(graph
.axesnames
)
1788 v2
= [0]*len(graph
.axesnames
)
1789 v1
[sharedata
.index2
] = -1
1790 v2
[sharedata
.index2
] = 1
1791 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1794 sortElements
= [sortElements
[0], sortElements
[2], sortElements
[1], sortElements
[3]]
1796 sortElements
= [(zindex
, i
) for i
, zindex
in enumerate(sortElements
)]
1799 mincolor
, maxcolor
= privatedata
.mincolor
, privatedata
.maxcolor
1800 if self
.mincolor
is not None:
1801 mincolor
= self
.mincolor
1802 if self
.maxcolor
is not None:
1803 maxcolor
= self
.maxcolor
1806 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1807 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1809 available1
, valid1
, v1
= sharedata
.data12
[value1a
][value2a
]
1810 available2
, valid2
, v2
= sharedata
.data12
[value1a
][value2b
]
1811 available3
, valid3
, v3
= sharedata
.data12
[value1b
][value2a
]
1812 available4
, valid4
, v4
= sharedata
.data12
[value1b
][value2b
]
1815 if not available1
or not available2
or not available3
or not available4
:
1817 if not valid1
or not valid2
or not valid3
or not valid4
:
1818 warnings
.warn("surface elements partially outside of the graph are (currently) skipped completely")
1820 def shrink(index
, v1
, v2
, by
):
1823 for i
in builtinrange(3):
1825 v1
[i
], v2
[i
] = v1
[i
] + by
*(v2
[i
]-v1
[i
]), v2
[i
] + by
*(v1
[i
]-v2
[i
])
1827 v1f
, v2f
, v3f
, v4f
= v1
, v2
, v3
, v4
1828 if self
.gridcolor
is not None and self
.gridlines1
:
1829 v1
, v2
= shrink(sharedata
.index1
, v1
, v2
, self
.gridlines1
)
1830 v3
, v4
= shrink(sharedata
.index1
, v3
, v4
, self
.gridlines1
)
1831 if self
.gridcolor
is not None and self
.gridlines2
:
1832 v1
, v3
= shrink(sharedata
.index2
, v1
, v3
, self
.gridlines2
)
1833 v2
, v4
= shrink(sharedata
.index2
, v2
, v4
, self
.gridlines2
)
1834 v5
= self
.midvalue(v1
, v2
, v3
, v4
)
1835 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1836 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1837 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1838 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1839 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1840 if privatedata
.colorize
:
1841 def colorfromgradient(c
):
1842 vc
= (c
- mincolor
) / float(maxcolor
- mincolor
)
1844 warnings
.warn("gradiend color range is exceeded due to mincolor setting")
1847 warnings
.warn("gradiend color range is exceeded due to maxcolor setting")
1849 return self
.gradient
.getcolor(vc
)
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
= colorfromgradient(c1
)
1856 c2a
= c2c
= colorfromgradient(c2
)
1857 c3b
= c3d
= colorfromgradient(c3
)
1858 c4c
= c4d
= colorfromgradient(c4
)
1859 c5a
= c5b
= c5c
= c5d
= colorfromgradient(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)
1923 class bitmap(_style
):
1925 needsdata
= ["values1", "values2", "data12", "data21"]
1927 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Grey
, mincolor
=None, maxcolor
=None, epsilon
=1e-10):
1928 self
.colorname
= colorname
1929 self
.gradient
= gradient
1930 self
.mincolor
= mincolor
1931 self
.maxcolor
= maxcolor
1932 self
.epsilon
= epsilon
1934 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1935 return [self
.colorname
]
1937 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1938 privatedata
.colors
= {}
1939 privatedata
.mincolor
= privatedata
.maxcolor
= None
1940 privatedata
.vfixed
= [None]*len(graph
.axesnames
)
1942 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1944 color
= point
[self
.colorname
] + 0
1948 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1949 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1950 privatedata
.mincolor
= color
1951 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1952 privatedata
.maxcolor
= color
1953 if len(privatedata
.vfixed
) > 2 and sharedata
.vposavailable
:
1954 for i
, (v1
, v2
) in enumerate(zip(privatedata
.vfixed
, sharedata
.vpos
)):
1955 if i
!= sharedata
.index1
and i
!= sharedata
.index2
:
1957 privatedata
.vfixed
[i
] = v2
1958 elif abs(v1
-v2
) > self
.epsilon
:
1959 raise ValueError("data must be in a plane for the bitmap style")
1961 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1962 mincolor
, maxcolor
= privatedata
.mincolor
, privatedata
.maxcolor
1963 if self
.mincolor
is not None:
1964 mincolor
= self
.mincolor
1965 if self
.maxcolor
is not None:
1966 maxcolor
= self
.maxcolor
1968 values1
= pycompat
.sorted(sharedata
.values1
.keys())
1969 values2
= pycompat
.sorted(sharedata
.values2
.keys())
1970 def equidistant(values
):
1973 raise ValueError("several data points required by the bitmap style in each dimension")
1974 range = values
[-1] - values
[0]
1975 for i
, value
in enumerate(values
):
1976 if abs(value
- values
[0] - i
* range / l
) > self
.epsilon
:
1977 raise ValueError("data must be equidistant for the bitmap style")
1978 equidistant(values1
)
1979 equidistant(values2
)
1981 for value2
in values2
:
1982 for value1
in values1
:
1984 available
, valid
, v
= sharedata
.data12
[value1
][value2
]
1994 mode
= {"/DeviceGray": "L",
1995 "/DeviceRGB": "RGB",
1996 "/DeviceCMYK": "CMYK"}[self
.gradient
.getcolor(0).colorspacestring()]
1999 empty
= "\0"*len(mode
)
2000 data
= cStringIO
.StringIO()
2001 for value2
in values2
:
2002 for value1
in values1
:
2004 available
, valid
, v
= sharedata
.data12
[value1
][value2
]
2011 c
= privatedata
.colors
[value1
][value2
]
2012 vc
= (c
- mincolor
) / float(maxcolor
- mincolor
)
2014 warnings
.warn("gradiend color range is exceeded due to mincolor setting")
2017 warnings
.warn("gradiend color range is exceeded due to maxcolor setting")
2019 c
= self
.gradient
.getcolor(vc
)
2021 data
.write(chr(255))
2022 data
.write(c
.to8bitstring())
2023 i
= bitmapmodule
.image(len(values1
), len(values2
), mode
, data
.getvalue())
2025 v1enlargement
= (values1
[-1]-values1
[0])*0.5/len(values1
)
2026 v2enlargement
= (values2
[-1]-values2
[0])*0.5/len(values2
)
2028 privatedata
.vfixed
[sharedata
.index1
] = values1
[0]-v1enlargement
2029 privatedata
.vfixed
[sharedata
.index2
] = values2
[-1]+v2enlargement
2030 x1_pt
, y1_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2031 privatedata
.vfixed
[sharedata
.index1
] = values1
[-1]+v1enlargement
2032 privatedata
.vfixed
[sharedata
.index2
] = values2
[-1]+v2enlargement
2033 x2_pt
, y2_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2034 privatedata
.vfixed
[sharedata
.index1
] = values1
[0]-v1enlargement
2035 privatedata
.vfixed
[sharedata
.index2
] = values2
[0]-v2enlargement
2036 x3_pt
, y3_pt
= graph
.vpos_pt(*privatedata
.vfixed
)
2037 t
= trafo
.trafo_pt(((x2_pt
-x1_pt
, x3_pt
-x1_pt
), (y2_pt
-y1_pt
, y3_pt
-y1_pt
)), (x1_pt
, y1_pt
))
2039 privatedata
.vfixed
[sharedata
.index1
] = values1
[-1]+v1enlargement
2040 privatedata
.vfixed
[sharedata
.index2
] = values2
[0]-v2enlargement
2041 vx4
, vy4
= t
.inverse().apply_pt(*graph
.vpos_pt(*privatedata
.vfixed
))
2042 if abs(vx4
- 1) > self
.epsilon
or abs(vy4
- 1) > self
.epsilon
:
2043 raise ValueError("invalid graph layout for bitmap style (bitmap positioning by affine transformation failed)")
2046 privatedata
.vfixed
[sharedata
.index1
] = 0
2047 privatedata
.vfixed
[sharedata
.index2
] = 0
2048 p
.append(path
.moveto_pt(*graph
.vpos_pt(*privatedata
.vfixed
)))
2049 vfixed2
= privatedata
.vfixed
+ privatedata
.vfixed
2050 vfixed2
[sharedata
.index1
] = 0
2051 vfixed2
[sharedata
.index2
] = 0
2052 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 1
2053 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 0
2054 p
.append(graph
.vgeodesic_el(*vfixed2
))
2055 vfixed2
[sharedata
.index1
] = 1
2056 vfixed2
[sharedata
.index2
] = 0
2057 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 1
2058 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 1
2059 p
.append(graph
.vgeodesic_el(*vfixed2
))
2060 vfixed2
[sharedata
.index1
] = 1
2061 vfixed2
[sharedata
.index2
] = 1
2062 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 0
2063 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 1
2064 p
.append(graph
.vgeodesic_el(*vfixed2
))
2065 vfixed2
[sharedata
.index1
] = 0
2066 vfixed2
[sharedata
.index2
] = 1
2067 vfixed2
[sharedata
.index1
+ len(graph
.axesnames
)] = 0
2068 vfixed2
[sharedata
.index2
+ len(graph
.axesnames
)] = 0
2069 p
.append(graph
.vgeodesic_el(*vfixed2
))
2070 p
.append(path
.closepath())
2072 c
= canvas
.canvas([canvas
.clip(p
)])
2073 b
= bitmapmodule
.bitmap_trafo(t
, i
)