1 # -*- coding: ISO-8859-1 -*-
4 # Copyright (C) 2002-2004 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
6 # Copyright (C) 2002-2006 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26 from pyx
import attr
, deco
, style
, color
, unit
, canvas
, path
27 from pyx
import text
as textmodule
34 # fallback implementation for Python 2.2. and below
36 return zip(xrange(len(list)), list)
39 """Interface class for graph styles
41 Each graph style must support the methods described in this
42 class. However, since a graph style might not need to perform
43 actions on all the various events, it does not need to overwrite
44 all methods of this base class (e.g. this class is not an abstract
45 class in any respect).
47 A style should never store private data by istance variables
48 (i.e. accessing self), but it should use the sharedata and privatedata
49 instances instead. A style instance can be used multiple times with
50 different sharedata and privatedata instances at the very same time.
51 The sharedata and privatedata instances act as data containers and
52 sharedata allows for sharing information across several styles.
54 Every style contains two class variables, which are not to be
56 - providesdata is a list of variable names a style offers via
57 the sharedata instance. This list is used to determine whether
58 all needs of subsequent styles are fullfilled. Otherwise
59 getdefaultprovider should return a proper style to be used.
60 - needsdata is a list of variable names the style needs to access in the
64 providesdata
= [] # by default, we provide nothing
65 needsdata
= [] # and do not depend on anything
67 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
68 """Set column information
70 This method is used setup the column name information to be
71 accessible to the style later on. The style should analyse
72 the list of column names. The method should return a list of
73 column names which the style will make use of."""
76 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
79 This method is called in order to adjust the axis range to
80 the provided data. columnname is the column name (each style
81 is subsequently called for all column names)."""
84 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
85 """Select stroke/fill attributes
87 This method is called to allow for the selection of
88 changable attributes of a style."""
91 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
92 """Initialize drawing of data
94 This method might be used to initialize the drawing of data."""
97 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
100 This method is called for each data point. The data is
101 available in the dictionary point. The dictionary
102 keys are the column names."""
105 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
106 """Finalize drawing of data
108 This method is called after the last data point was
109 drawn using the drawpoint method above."""
112 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
116 # The following two methods are used to register and get a default provider
117 # for keys. A key is a variable name in sharedata. A provider is a style
118 # which creates variables in sharedata.
120 _defaultprovider
= {}
122 def registerdefaultprovider(style
, keys
):
123 """sets a style as a default creator for sharedata variables 'keys'"""
124 assert not len(style
.needsdata
), "currently we state, that a style should not depend on other sharedata variables"
126 assert key
in style
.providesdata
, "key not provided by style"
127 # we might allow for overwriting the defaults, i.e. the following is not checked:
128 # assert key in _defaultprovider.keys(), "default provider already registered for key"
129 _defaultprovider
[key
] = style
131 def getdefaultprovider(key
):
132 """returns a style, which acts as a default creator for the
133 sharedata variable 'key'"""
134 return _defaultprovider
[key
]
139 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
141 def __init__(self
, epsilon
=1e-10):
142 self
.epsilon
= epsilon
144 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
145 sharedata
.poscolumnnames
= []
146 sharedata
.vposmissing
= []
147 for count
, axisnames
in enumerate(graph
.axesnames
):
148 for axisname
in axisnames
:
149 for columnname
in columnnames
:
150 if axisname
== columnname
:
151 sharedata
.poscolumnnames
.append(columnname
)
152 if len(sharedata
.poscolumnnames
) > count
+1:
153 raise ValueError("multiple axes per graph dimension")
154 elif len(sharedata
.poscolumnnames
) < count
+1:
155 sharedata
.vposmissing
.append(count
)
156 sharedata
.poscolumnnames
.append(None)
157 return [columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None]
159 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
160 if columnname
in sharedata
.poscolumnnames
:
161 graph
.axes
[columnname
].adjustaxis(data
)
163 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
164 sharedata
.vpos
= [None]*(len(graph
.axesnames
))
165 privatedata
.pointpostmplist
= [[columnname
, index
, graph
.axes
[columnname
]] # temporarily used by drawpoint only
166 for index
, columnname
in enumerate([columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None])]
167 for missing
in sharedata
.vposmissing
:
168 for pointpostmp
in privatedata
.pointpostmplist
:
169 if pointpostmp
[1] >= missing
:
172 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
173 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
174 sharedata
.vposvalid
= 1 # valid position inside the graph
175 for columnname
, index
, axis
in privatedata
.pointpostmplist
:
177 v
= axis
.convert(point
[columnname
])
178 except (ArithmeticError, ValueError, TypeError):
179 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
180 sharedata
.vpos
[index
] = None
182 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
183 sharedata
.vposvalid
= 0
184 sharedata
.vpos
[index
] = v
187 registerdefaultprovider(pos(), pos
.providesdata
)
192 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
202 def __init__(self
, usenames
={}, epsilon
=1e-10):
203 self
.usenames
= usenames
204 self
.epsilon
= epsilon
206 def _numberofbits(self
, mask
):
210 return self
._numberofbits
(mask
>> 1) + 1
212 return self
._numberofbits
(mask
>> 1)
214 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
216 privatedata
.rangeposcolumns
= []
217 sharedata
.vrangemissing
= []
218 sharedata
.vrangeminmissing
= []
219 sharedata
.vrangemaxmissing
= []
220 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
221 for count
, axisnames
in enumerate(graph
.axesnames
):
222 for axisname
in axisnames
:
224 usename
= self
.usenames
[axisname
]
228 for columnname
in columnnames
:
230 if usename
== columnname
:
231 mask
+= self
.mask_value
232 elif usename
+ "min" == columnname
:
233 mask
+= self
.mask_min
234 elif usename
+ "max" == columnname
:
235 mask
+= self
.mask_max
236 elif "d" + usename
+ "min" == columnname
:
237 mask
+= self
.mask_dmin
238 elif "d" + usename
+ "max" == columnname
:
239 mask
+= self
.mask_dmax
240 elif "d" + usename
== columnname
:
245 usecolumns
.append(columnname
)
246 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
247 if (self
._numberofbits
(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
248 self
._numberofbits
(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
249 raise ValueError("multiple range definition")
250 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
251 if not (mask
& self
.mask_value
):
252 raise ValueError("missing value for delta")
253 privatedata
.rangeposdeltacolumns
[axisname
] = {}
254 privatedata
.rangeposcolumns
.append((axisname
, usename
, mask
))
255 elif mask
== self
.mask_value
:
256 usecolumns
= usecolumns
[:-1]
257 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
258 raise ValueError("multiple axes per graph dimension")
259 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
260 sharedata
.vrangemissing
.append(count
)
261 sharedata
.vrangeminmissing
.append(count
)
262 sharedata
.vrangemaxmissing
.append(count
)
264 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
265 sharedata
.vrangeminmissing
.append(count
)
266 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
267 sharedata
.vrangemaxmissing
.append(count
)
270 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
271 if columnname
in [c
+ "min" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_min
]:
272 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
273 if columnname
in [c
+ "max" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_max
]:
274 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
276 # delta handling: fill rangeposdeltacolumns
277 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
278 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
279 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
280 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
281 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
282 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
283 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
284 if columnname
== "d" + usename
and mask
& self
.mask_d
:
285 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
287 # delta handling: process rangeposdeltacolumns
288 for a
, d
in privatedata
.rangeposdeltacolumns
.items():
289 if d
.has_key(self
.mask_value
):
291 if k
!= self
.mask_value
:
292 if k
& (self
.mask_dmin | self
.mask_d
):
295 mindata
.append(d
[self
.mask_value
] - d
[k
])
298 graph
.axes
[a
].adjustaxis(mindata
)
299 if k
& (self
.mask_dmax | self
.mask_d
):
302 maxdata
.append(d
[self
.mask_value
] + d
[k
])
305 graph
.axes
[a
].adjustaxis(maxdata
)
308 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
309 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
310 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
311 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
312 for missing
in sharedata
.vrangemissing
:
313 for rangepostmp
in privatedata
.rangepostmplist
:
314 if rangepostmp
[2] >= missing
:
317 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
318 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
320 if mask
& self
.mask_min
:
321 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
322 if mask
& self
.mask_dmin
:
323 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
324 if mask
& self
.mask_d
:
325 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
326 except (ArithmeticError, ValueError, TypeError):
327 sharedata
.vrange
[index
][0] = None
329 if mask
& self
.mask_max
:
330 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
331 if mask
& self
.mask_dmax
:
332 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
333 if mask
& self
.mask_d
:
334 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
335 except (ArithmeticError, ValueError, TypeError):
336 sharedata
.vrange
[index
][1] = None
338 # some range checks for data consistency
339 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
340 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
341 raise ValueError("inverse range")
342 # disabled due to missing vpos access:
343 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
344 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
345 # raise ValueError("negative minimum errorbar")
346 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
347 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
348 # raise ValueError("negative maximum errorbar")
351 registerdefaultprovider(range(), range.providesdata
)
354 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
355 c
.draw(path
.path(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
),
357 path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
358 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
)), attrs
)
360 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
361 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
362 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
363 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
364 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
366 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
367 c
.draw(path
.path(path
.moveto_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
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
370 path
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
371 path
.closepath()), attrs
)
373 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
374 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
375 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
376 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
377 path
.closepath()), attrs
)
379 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
380 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
381 path
.closepath()), attrs
)
383 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
384 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
385 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
386 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
387 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
388 path
.closepath()), attrs
)
391 class _styleneedingpointpos(_style
):
393 needsdata
= ["vposmissing"]
395 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
396 if len(sharedata
.vposmissing
):
397 raise ValueError("incomplete position information")
401 class symbol(_styleneedingpointpos
):
403 needsdata
= ["vpos", "vposmissing", "vposvalid"]
405 # "inject" the predefinied symbols into the class:
407 # Note, that statements like cross = _crosssymbol are
408 # invalid, since the would lead to unbound methods, but
409 # a single entry changeable list does the trick.
411 # Once we require Python 2.2+ we should use staticmethods
412 # to implement the default symbols inplace.
414 cross
= attr
.changelist([_crosssymbol
])
415 plus
= attr
.changelist([_plussymbol
])
416 square
= attr
.changelist([_squaresymbol
])
417 triangle
= attr
.changelist([_trianglesymbol
])
418 circle
= attr
.changelist([_circlesymbol
])
419 diamond
= attr
.changelist([_diamondsymbol
])
421 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
422 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
423 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
424 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
425 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
426 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
427 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
428 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
429 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
430 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
432 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
433 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
435 defaultsymbolattrs
= [deco
.stroked
]
437 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
440 self
.symbolattrs
= symbolattrs
442 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
443 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
444 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
445 if self
.symbolattrs
is not None:
446 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
448 privatedata
.symbolattrs
= None
450 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
451 privatedata
.symbolcanvas
= canvas
.canvas()
453 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
454 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
455 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
456 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
458 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
459 graph
.insert(privatedata
.symbolcanvas
)
461 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
462 if privatedata
.symbolattrs
is not None:
463 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
466 class _line(_styleneedingpointpos
):
468 # this style is not a complete style, but it provides the basic functionality to
469 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
471 needsdata
= ["vposmissing"]
473 def initpointstopath(self
, privatedata
):
474 privatedata
.path
= path
.path()
475 privatedata
.linebasepoints
= []
476 privatedata
.lastvpos
= None
478 def addpointstopath(self
, privatedata
):
479 # add baselinepoints to privatedata.path
480 if len(privatedata
.linebasepoints
) > 1:
481 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
482 if len(privatedata
.linebasepoints
) > 2:
483 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
485 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
486 privatedata
.linebasepoints
= []
488 def addpoint(self
, privatedata
, graphvpos_pt
, vposavailable
, vposvalid
, vpos
):
489 # append linebasepoints
491 if len(privatedata
.linebasepoints
):
492 # the last point was inside the graph
493 if vposvalid
: # shortcut for the common case
494 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
498 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
501 # 1 = vstart + (vend - vstart) * cut
503 newcut
= (1 - vstart
)/(vend
- vstart
)
504 except (ArithmeticError, TypeError):
507 # 0 = vstart + (vend - vstart) * cut
509 newcut
= - vstart
/(vend
- vstart
)
510 except (ArithmeticError, TypeError):
512 if newcut
is not None and newcut
< cut
:
516 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
517 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
518 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
519 self
.addpointstopath(privatedata
)
521 # the last point was outside the graph
522 if privatedata
.lastvpos
is not None:
526 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
529 # 1 = vstart + (vend - vstart) * cut
531 newcut
= (1 - vstart
)/(vend
- vstart
)
532 except (ArithmeticError, TypeError):
535 # 0 = vstart + (vend - vstart) * cut
537 newcut
= - vstart
/(vend
- vstart
)
538 except (ArithmeticError, TypeError):
540 if newcut
is not None and newcut
> cut
:
544 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
545 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
546 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
547 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
549 # sometimes cut beginning and end
552 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
557 # 1 = vstart + (vend - vstart) * cutfrom
559 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
560 except (ArithmeticError, TypeError):
565 # 0 = vstart + (vend - vstart) * cutfrom
567 newcutfrom
= - vstart
/(vend
- vstart
)
568 except (ArithmeticError, TypeError):
570 if newcutfrom
is not None and newcutfrom
> cutfrom
:
574 # 1 = vstart + (vend - vstart) * cutto
576 newcutto
= (1 - vstart
)/(vend
- vstart
)
577 except (ArithmeticError, TypeError):
580 # 0 = vstart + (vend - vstart) * cutto
582 newcutto
= - vstart
/(vend
- vstart
)
583 except (ArithmeticError, TypeError):
585 if newcutto
is not None and newcutto
< cutto
:
591 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
592 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
593 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
594 privatedata
.linebasepoints
.append(graphvpos_pt(*cutfromvpos
))
595 privatedata
.linebasepoints
.append(graphvpos_pt(*cuttovpos
))
596 self
.addpointstopath(privatedata
)
597 privatedata
.lastvpos
= vpos
[:]
599 if len(privatedata
.linebasepoints
) > 1:
600 self
.addpointstopath(privatedata
)
601 privatedata
.lastvpos
= None
603 def addinvalid(self
, privatedata
):
604 if len(privatedata
.linebasepoints
) > 1:
605 self
.addpointstopath(privatedata
)
606 privatedata
.lastvpos
= None
608 def donepointstopath(self
, privatedata
):
609 if len(privatedata
.linebasepoints
) > 1:
610 self
.addpointstopath(privatedata
)
611 return privatedata
.path
616 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
618 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
619 style
.linestyle
.dashed
,
620 style
.linestyle
.dotted
,
621 style
.linestyle
.dashdotted
])
623 defaultlineattrs
= [changelinestyle
]
625 def __init__(self
, lineattrs
=[]):
626 self
.lineattrs
= lineattrs
628 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
629 if self
.lineattrs
is not None:
630 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
632 privatedata
.lineattrs
= None
634 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
635 self
.initpointstopath(privatedata
)
637 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
638 self
.addpoint(privatedata
, graph
.vpos_pt
, sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
)
640 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
641 path
= self
.donepointstopath(privatedata
)
642 if privatedata
.lineattrs
is not None and len(path
):
643 graph
.stroke(path
, privatedata
.lineattrs
)
645 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
646 if privatedata
.lineattrs
is not None:
647 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
)
650 class errorbar(_style
):
652 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
654 defaulterrorbarattrs
= []
656 def __init__(self
, size
=0.1*unit
.v_cm
,
660 self
.errorbarattrs
= errorbarattrs
661 self
.epsilon
= epsilon
663 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
664 for i
in sharedata
.vposmissing
:
665 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
666 raise ValueError("position and range for a graph dimension missing")
669 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
670 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
671 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
673 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
674 if privatedata
.errorbarattrs
is not None:
675 privatedata
.errorbarcanvas
= canvas
.canvas(privatedata
.errorbarattrs
)
676 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
678 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
679 if privatedata
.errorbarattrs
is not None:
680 for i
in privatedata
.dimensionlist
:
681 for j
in privatedata
.dimensionlist
:
683 (sharedata
.vpos
[j
] is None or
684 sharedata
.vpos
[j
] < -self
.epsilon
or
685 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
688 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
689 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
690 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
692 vminpos
= sharedata
.vpos
[:]
693 if sharedata
.vrange
[i
][0] is not None:
694 vminpos
[i
] = sharedata
.vrange
[i
][0]
698 if vminpos
[i
] > 1+self
.epsilon
:
700 if vminpos
[i
] < -self
.epsilon
:
703 vmaxpos
= sharedata
.vpos
[:]
704 if sharedata
.vrange
[i
][1] is not None:
705 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
709 if vmaxpos
[i
] < -self
.epsilon
:
711 if vmaxpos
[i
] > 1+self
.epsilon
:
714 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
715 for j
in privatedata
.dimensionlist
:
718 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
720 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
722 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
723 if privatedata
.errorbarattrs
is not None:
724 graph
.insert(privatedata
.errorbarcanvas
)
727 class text(_styleneedingpointpos
):
729 needsdata
= ["vpos", "vposmissing", "vposvalid"]
731 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
733 def __init__(self
, textname
="text", dxname
=None, dyname
=None,
734 dxunit
=0.3*unit
.v_cm
, dyunit
=0.3*unit
.v_cm
,
735 textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
736 self
.textname
= textname
743 self
.textattrs
= textattrs
745 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
746 if self
.textname
not in columnnames
:
747 raise ValueError("column '%s' missing" % self
.textname
)
748 names
= [self
.textname
]
749 if self
.dxname
is not None:
750 if self
.dxname
not in columnnames
:
751 raise ValueError("column '%s' missing" % self
.dxname
)
752 names
.append(self
.dxname
)
753 if self
.dyname
is not None:
754 if self
.dyname
not in columnnames
:
755 raise ValueError("column '%s' missing" % self
.dyname
)
756 names
.append(self
.dyname
)
757 return names
+ _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
759 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
760 if self
.textattrs
is not None:
761 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
763 privatedata
.textattrs
= None
765 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
766 if self
.dxname
is None:
767 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
769 privatedata
.dxunit_pt
= unit
.topt(self
.dxunit
)
770 if self
.dyname
is None:
771 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
773 privatedata
.dyunit_pt
= unit
.topt(self
.dyunit
)
775 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
776 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
777 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
779 text
= str(point
[self
.textname
])
783 if self
.dxname
is None:
784 dx_pt
= privatedata
.textdx_pt
786 dx_pt
= float(point
[self
.dxname
]) * privatedata
.dxunit_pt
787 if self
.dyname
is None:
788 dy_pt
= privatedata
.textdy_pt
790 dy_pt
= float(point
[self
.dyname
]) * privatedata
.dyunit_pt
791 graph
.text_pt(x_pt
+ dx_pt
, y_pt
+ dy_pt
, text
, privatedata
.textattrs
)
793 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
794 raise RuntimeError("Style currently doesn't provide a graph key")
797 class arrow(_styleneedingpointpos
):
799 needsdata
= ["vpos", "vposmissing", "vposvalid"]
801 defaultlineattrs
= []
802 defaultarrowattrs
= []
804 def __init__(self
, linelength
=0.25*unit
.v_cm
, arrowsize
=0.15*unit
.v_cm
, lineattrs
=[], arrowattrs
=[], arrowpos
=0.5, epsilon
=1e-5):
805 self
.linelength
= linelength
806 self
.arrowsize
= arrowsize
807 self
.lineattrs
= lineattrs
808 self
.arrowattrs
= arrowattrs
809 self
.arrowpos
= arrowpos
810 self
.epsilon
= epsilon
812 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
813 if len(graph
.axesnames
) != 2:
814 raise ValueError("arrow style restricted on two-dimensional graphs")
815 if "size" not in columnnames
:
816 raise ValueError("size missing")
817 if "angle" not in columnnames
:
818 raise ValueError("angle missing")
819 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
821 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
822 if self
.lineattrs
is not None:
823 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
825 privatedata
.lineattrs
= None
826 if self
.arrowattrs
is not None:
827 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
829 privatedata
.arrowattrs
= None
831 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
832 privatedata
.arrowcanvas
= canvas
.canvas()
834 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
835 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
836 linelength_pt
= unit
.topt(self
.linelength
)
837 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
839 angle
= point
["angle"] + 0.0
840 size
= point
["size"] + 0.0
844 if point
["size"] > self
.epsilon
:
845 dx
= math
.cos(angle
*math
.pi
/180)
846 dy
= math
.sin(angle
*math
.pi
/180)
847 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
848 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
849 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
850 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
851 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
+
852 [deco
.earrow(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
854 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
855 graph
.insert(privatedata
.arrowcanvas
)
857 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
858 raise RuntimeError("Style currently doesn't provide a graph key")
863 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
865 def __init__(self
, gradient
=color
.gradient
.Grey
):
866 self
.gradient
= gradient
868 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
869 if len(graph
.axesnames
) != 2:
870 raise TypeError("arrow style restricted on two-dimensional graphs")
871 if "color" not in columnnames
:
872 raise ValueError("color missing")
873 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
874 raise ValueError("incomplete range")
877 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
878 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
880 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
881 xvmin
= sharedata
.vrange
[0][0]
882 xvmax
= sharedata
.vrange
[0][1]
883 yvmin
= sharedata
.vrange
[1][0]
884 yvmax
= sharedata
.vrange
[1][1]
885 if (xvmin
is not None and xvmin
< 1 and
886 xvmax
is not None and xvmax
> 0 and
887 yvmin
is not None and yvmin
< 1 and
888 yvmax
is not None and yvmax
> 0):
897 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
898 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
899 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
900 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
901 p
.append(path
.closepath())
902 privatedata
.rectcanvas
.fill(p
, [self
.gradient
.getcolor(point
["color"])])
904 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
905 raise RuntimeError("Style currently doesn't provide a graph key")
908 class histogram(_style
):
910 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
912 defaultlineattrs
= [deco
.stroked
]
913 defaultfrompathattrs
= []
915 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0,
916 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
917 self
.lineattrs
= lineattrs
919 self
.fromvalue
= fromvalue
920 self
.frompathattrs
= frompathattrs
921 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
922 self
.autohistogramaxisindex
= autohistogramaxisindex
923 self
.autohistogrampointpos
= autohistogrampointpos
924 self
.epsilon
= epsilon
926 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
927 if len(graph
.axesnames
) != 2:
928 raise TypeError("histogram style restricted on two-dimensional graphs")
929 privatedata
.rangeaxisindex
= None
930 for i
in builtinrange(len(graph
.axesnames
)):
931 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
932 if i
in sharedata
.vposmissing
:
933 raise ValueError("pos and range missing")
935 if privatedata
.rangeaxisindex
is not None:
936 raise ValueError("multiple ranges")
937 privatedata
.rangeaxisindex
= i
938 if privatedata
.rangeaxisindex
is None:
939 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
940 privatedata
.autohistogram
= 1
942 privatedata
.autohistogram
= 0
945 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
946 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
948 raise ValueError("several data points needed for automatic histogram width calculation")
950 delta
= data
[1] - data
[0]
951 min = data
[0] - self
.autohistogrampointpos
* delta
952 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
953 graph
.axes
[columnname
].adjustaxis([min, max])
955 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
956 privatedata
.insertfrompath
= selectindex
== 0
957 if self
.lineattrs
is not None:
958 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
960 privatedata
.lineattrs
= None
962 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
963 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
964 if privatedata
.rangeaxisindex
:
965 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
967 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
969 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
970 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
985 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
986 if vvalue1cut
and not self
.fillable
:
987 if privatedata
.rangeaxisindex
:
988 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
990 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
991 if privatedata
.rangeaxisindex
:
992 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
994 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
996 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
998 if vvalue
< -self
.epsilon
:
1000 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1001 if vvalue
> 1+self
.epsilon
:
1003 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1004 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1019 if abs(vpos1cut
+ vpos2cut
) <= 1:
1022 if privatedata
.rangeaxisindex
:
1023 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1024 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1026 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1027 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1029 if privatedata
.rangeaxisindex
:
1030 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1032 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1033 if privatedata
.rangeaxisindex
:
1034 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1036 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1037 if self
.fillable
and vpos2cut
:
1038 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1039 if privatedata
.rangeaxisindex
:
1040 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1042 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1044 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1045 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1046 if self
.fillable
and not self
.steps
:
1047 if not currentvalid
:
1050 if vmin
< -self
.epsilon
:
1053 elif vmin
> 1+self
.epsilon
:
1057 if vmax
< -self
.epsilon
:
1060 if vmax
> 1+self
.epsilon
:
1064 if vvalue
< -self
.epsilon
:
1067 if vvalue
> 1+self
.epsilon
:
1071 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1072 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1075 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1078 if privatedata
.rangeaxisindex
:
1079 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1080 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1081 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1082 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1084 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1085 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1086 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1087 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1090 if privatedata
.rangeaxisindex
:
1091 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1092 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1093 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1094 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1096 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1097 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1098 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1099 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1100 elif privatedata
.vfromvaluecut
:
1102 if privatedata
.rangeaxisindex
:
1103 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1104 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1105 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1106 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1108 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1109 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1110 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1111 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1114 if privatedata
.rangeaxisindex
:
1115 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1116 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1117 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1118 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1120 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1121 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1122 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1123 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1125 if privatedata
.rangeaxisindex
:
1126 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1127 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1128 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1129 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1130 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1131 privatedata
.path
.append(path
.closepath())
1133 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1134 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1135 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1136 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1137 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1138 privatedata
.path
.append(path
.closepath())
1141 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1142 except (ArithmeticError, ValueError, TypeError):
1144 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1145 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1146 self
.vposline(privatedata
, sharedata
, graph
,
1147 vmin
, privatedata
.lastvvalue
, vvalue
)
1149 if privatedata
.lastvvalue
is not None and currentvalid
:
1150 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1151 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1152 self
.vposline(privatedata
, sharedata
, graph
,
1153 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1155 self
.vmoveto(privatedata
, sharedata
, graph
,
1157 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1158 self
.vmoveto(privatedata
, sharedata
, graph
,
1159 vmin
, privatedata
.vfromvalue
)
1160 self
.vposline(privatedata
, sharedata
, graph
,
1161 vmin
, privatedata
.vfromvalue
, vvalue
)
1163 self
.vvalueline(privatedata
, sharedata
, graph
,
1165 privatedata
.lastvvalue
= vvalue
1166 privatedata
.lastvmax
= vmax
1168 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1170 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1171 privatedata
.path
= path
.path()
1172 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1173 privatedata
.vcurrentpoint
= None
1174 privatedata
.count
= 0
1175 if self
.fromvalue
is not None:
1176 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1177 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1178 privatedata
.vfromvaluecut
= 0
1179 if privatedata
.vfromvalue
< 0:
1180 privatedata
.vfromvalue
= 0
1181 privatedata
.vfromvaluecut
= -1
1182 if privatedata
.vfromvalue
> 1:
1183 privatedata
.vfromvalue
= 1
1184 privatedata
.vfromvaluecut
= 1
1185 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1186 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1187 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1189 privatedata
.vfromvalue
= 0
1191 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1192 if privatedata
.autohistogram
:
1193 # automatic range handling
1194 privatedata
.count
+= 1
1195 if privatedata
.count
== 2:
1196 if privatedata
.rangeaxisindex
:
1197 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1198 self
.drawvalue(privatedata
, sharedata
, graph
,
1199 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1200 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1201 privatedata
.lastvpos
[0])
1203 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1204 self
.drawvalue(privatedata
, sharedata
, graph
,
1205 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1206 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1207 privatedata
.lastvpos
[1])
1208 elif privatedata
.count
> 2:
1209 if privatedata
.rangeaxisindex
:
1210 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1212 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1213 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1214 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1215 if privatedata
.count
> 1:
1216 if privatedata
.rangeaxisindex
:
1217 self
.drawvalue(privatedata
, sharedata
, graph
,
1218 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1219 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1222 self
.drawvalue(privatedata
, sharedata
, graph
,
1223 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1224 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1226 privatedata
.lastvpos
= sharedata
.vpos
[:]
1228 if privatedata
.rangeaxisindex
:
1229 self
.drawvalue(privatedata
, sharedata
, graph
,
1230 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1232 self
.drawvalue(privatedata
, sharedata
, graph
,
1233 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1235 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1236 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1237 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1238 graph
.draw(privatedata
.path
, privatedata
.lineattrs
)
1240 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1241 if privatedata
.lineattrs
is not None:
1242 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
)
1245 class barpos(_style
):
1247 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1249 defaultfrompathattrs
= []
1251 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1252 self
.fromvalue
= fromvalue
1253 self
.frompathattrs
= frompathattrs
1254 self
.epsilon
= epsilon
1256 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1257 sharedata
.barposcolumnnames
= []
1258 sharedata
.barvalueindex
= None
1259 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1261 for axisname
in axisnames
:
1262 if axisname
in columnnames
:
1263 if sharedata
.barvalueindex
is not None:
1264 raise ValueError("multiple values")
1265 sharedata
.barvalueindex
= dimension
1266 sharedata
.barposcolumnnames
.append(axisname
)
1268 if (axisname
+ "name") in columnnames
:
1269 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1272 raise ValueError("multiple names and value")
1274 raise ValueError("value/name missing")
1275 if sharedata
.barvalueindex
is None:
1276 raise ValueError("missing value")
1277 sharedata
.vposmissing
= []
1278 return sharedata
.barposcolumnnames
1280 def addsubvalue(self
, value
, subvalue
):
1285 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1287 return value
, subvalue
1289 return value
, subvalue
1291 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1293 i
= sharedata
.barposcolumnnames
.index(columnname
)
1297 if i
== sharedata
.barvalueindex
:
1298 if self
.fromvalue
is not None:
1299 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1300 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1302 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1303 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1305 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1306 privatedata
.insertfrompath
= selectindex
== 0
1308 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1309 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1310 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1311 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1313 if self
.fromvalue
is not None:
1314 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].convert(self
.fromvalue
)
1315 if privatedata
.vfromvalue
< 0:
1316 privatedata
.vfromvalue
= 0
1317 if privatedata
.vfromvalue
> 1:
1318 privatedata
.vfromvalue
= 1
1319 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1320 graph
.stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].vgridpath(privatedata
.vfromvalue
),
1321 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1323 privatedata
.vfromvalue
= 0
1325 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1326 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1327 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1328 if i
== sharedata
.barvalueindex
:
1329 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1330 sharedata
.lastbarvalue
= point
[barname
]
1332 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1333 except (ArithmeticError, ValueError, TypeError):
1334 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1338 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1339 except (ArithmeticError, ValueError, TypeError):
1340 sharedata
.vbarrange
[i
][j
] = None
1342 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1343 except (ArithmeticError, ValueError, TypeError):
1344 sharedata
.vpos
[i
] = None
1345 if sharedata
.vpos
[i
] is None:
1346 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1347 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1348 sharedata
.vposvalid
= 0
1350 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1353 class stackedbarpos(_style
):
1355 # provides no additional data, but needs some data (and modifies some of them)
1356 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1358 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1359 self
.stackname
= stackname
1360 self
.epsilon
= epsilon
1361 self
.addontop
= addontop
1363 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1364 if self
.stackname
not in columnnames
:
1365 raise ValueError("column '%s' missing" % self
.stackname
)
1366 return [self
.stackname
]
1368 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1369 if columnname
== self
.stackname
:
1370 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1372 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1373 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1374 sharedata
.stackedbar
+= 1
1376 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1377 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1380 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1381 except (ArithmeticError, ValueError, TypeError):
1382 sharedata
.lastbarvalue
= None
1384 sharedata
.lastbarvalue
= point
[self
.stackname
]
1386 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1387 except (ArithmeticError, ValueError, TypeError):
1388 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1389 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1391 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1392 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1393 for v
in sharedata
.vpos
:
1395 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1397 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1398 sharedata
.vposvalid
= 0
1403 needsdata
= ["vbarrange"]
1405 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1407 def __init__(self
, barattrs
=[]):
1408 self
.barattrs
= barattrs
1410 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1411 if len(graph
.axesnames
) != 2:
1412 raise TypeError("bar style restricted on two-dimensional graphs")
1415 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1416 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1418 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1419 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
1420 sharedata
.stackedbardraw
= 1
1421 privatedata
.stackedbar
= sharedata
.stackedbar
1423 def drawpointfill(self
, privatedata
, p
):
1425 privatedata
.rectcanvas
.fill(p
, privatedata
.barattrs
)
1427 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1428 xvmin
= sharedata
.vbarrange
[0][0]
1429 xvmax
= sharedata
.vbarrange
[0][1]
1430 yvmin
= sharedata
.vbarrange
[1][0]
1431 yvmax
= sharedata
.vbarrange
[1][1]
1434 xvmin
, xvmax
= xvmax
, xvmin
1439 yvmin
, yvmax
= yvmax
, yvmin
1442 if (xvmin
is not None and xvmin
< 1 and
1443 xvmax
is not None and xvmax
> 0 and
1444 yvmin
is not None and yvmin
< 1 and
1445 yvmax
is not None and yvmax
> 0):
1454 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
1455 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
1456 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
1457 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
1458 p
.append(path
.closepath())
1459 self
.drawpointfill(privatedata
, p
)
1461 self
.drawpointfill(privatedata
, None)
1463 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1464 selectindex
= privatedata
.stackedbar
1465 selecttotal
= sharedata
.stackedbar
+ 1
1466 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1469 class changebar(bar
):
1471 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1472 if selecttotal
!= 1:
1473 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1475 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1476 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1477 privatedata
.bars
= []
1479 def drawpointfill(self
, privatedata
, p
):
1480 privatedata
.bars
.append(p
)
1482 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1483 selecttotal
= len(privatedata
.bars
)
1484 for selectindex
, p
in enumerate(privatedata
.bars
):
1486 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1487 privatedata
.rectcanvas
.fill(p
, barattrs
)
1489 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1490 raise RuntimeError("Style currently doesn't provide a graph key")
1493 class surface(_line
):
1495 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1497 defaultgridattrs
= []
1499 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Rainbow
,
1500 mincolor
=None, maxcolor
=None,
1501 index1
=0, index2
=1, strokelines1
=1, strokelines2
=1, gridattrs
=[],
1503 self
.colorname
= colorname
1504 self
.gradient
= gradient
1505 self
.mincolor
= mincolor
1506 self
.maxcolor
= maxcolor
1507 self
.index1
= index1
1508 self
.index2
= index2
1509 self
.strokelines1
= strokelines1
1510 self
.strokelines2
= strokelines2
1511 self
.gridattrs
= gridattrs
1512 self
.epsilon
= epsilon
1514 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1515 privatedata
.colorize
= self
.colorname
in columnnames
1516 return privatedata
.colorize
and [self
.colorname
] or [] + _line
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
1518 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1519 if self
.gridattrs
is not None:
1520 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1522 privatedata
.gridattrs
= None
1525 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1526 privatedata
.values1
= {}
1527 privatedata
.values2
= {}
1528 privatedata
.data12
= {}
1529 privatedata
.data21
= {}
1530 privatedata
.colors
= {}
1531 privatedata
.mincolor
= privatedata
.maxcolor
= None
1533 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1534 if sharedata
.vposavailable
:
1535 value1
= sharedata
.vpos
[self
.index1
]
1536 value2
= sharedata
.vpos
[self
.index2
]
1537 if not privatedata
.values1
.has_key(value1
):
1538 for hasvalue
in privatedata
.values1
.keys():
1539 if hasvalue
- self
.epsilon
<= value1
<= hasvalue
+ self
.epsilon
:
1543 privatedata
.values1
[value1
] = 1
1544 if not privatedata
.values2
.has_key(value2
):
1545 for hasvalue
in privatedata
.values2
.keys():
1546 if hasvalue
- self
.epsilon
<= value2
<= hasvalue
+ self
.epsilon
:
1550 privatedata
.values2
[value2
] = 1
1551 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1552 privatedata
.data12
.setdefault(value1
, {})[value2
] = data
1553 privatedata
.data21
.setdefault(value2
, {})[value1
] = data
1554 if privatedata
.colorize
:
1556 color
= point
[self
.colorname
] + 0
1560 privatedata
.colors
.setdefault(value1
, {})[value2
] = color
1561 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1562 privatedata
.mincolor
= color
1563 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1564 privatedata
.maxcolor
= color
1566 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1567 values1
= privatedata
.values1
.keys()
1569 values2
= privatedata
.values2
.keys()
1571 if self
.mincolor
is not None:
1572 mincolor
= self
.mincolor
1573 if self
.maxcolor
is not None:
1574 maxcolor
= self
.maxcolor
1575 if self
.strokelines2
:
1576 for value1
in values1
:
1577 data2
= privatedata
.data12
[value1
]
1578 self
.initpointstopath(privatedata
)
1579 for value2
in values2
:
1581 data
= data2
[value2
]
1583 self
.addinvalid(privatedata
)
1585 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1586 p
= self
.donepointstopath(privatedata
)
1588 graph
.stroke(p
, privatedata
.gridattrs
)
1589 if self
.strokelines1
:
1590 for value2
in values2
:
1591 data1
= privatedata
.data21
[value2
]
1592 self
.initpointstopath(privatedata
)
1593 for value1
in values1
:
1595 data
= data1
[value1
]
1597 self
.addinvalid(privatedata
)
1599 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1600 p
= self
.donepointstopath(privatedata
)
1602 graph
.stroke(p
, privatedata
.gridattrs
)
1603 if privatedata
.colorize
:
1604 from pyx
import mesh
1607 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1608 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1610 available1
, valid1
, v1
= privatedata
.data12
[value1a
][value2a
]
1611 available2
, valid2
, v2
= privatedata
.data12
[value1a
][value2b
]
1612 available3
, valid3
, v3
= privatedata
.data12
[value1b
][value2a
]
1613 available4
, valid4
, v4
= privatedata
.data12
[value1b
][value2b
]
1616 if not available1
or not available2
or not available3
or not available4
:
1618 if not valid1
or not valid2
or not valid3
or not valid4
:
1620 v5
= [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1621 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1622 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1623 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1624 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1625 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1626 c1
= privatedata
.colors
[value1a
][value2a
]
1627 c2
= privatedata
.colors
[value1a
][value2b
]
1628 c3
= privatedata
.colors
[value1b
][value2a
]
1629 c4
= privatedata
.colors
[value1b
][value2b
]
1630 c5
= 0.25*(c1
+c2
+c3
+c4
)
1632 return self
.gradient
.getcolor((c
- privatedata
.mincolor
) / float(privatedata
.maxcolor
- privatedata
.mincolor
)).rgb()
1638 n1
= mesh
.node_pt((x1_pt
, y1_pt
), color1
)
1639 n2
= mesh
.node_pt((x2_pt
, y2_pt
), color2
)
1640 n3
= mesh
.node_pt((x3_pt
, y3_pt
), color3
)
1641 n4
= mesh
.node_pt((x4_pt
, y4_pt
), color4
)
1642 n5
= mesh
.node_pt((x5_pt
, y5_pt
), color5
)
1643 e1
= mesh
.element((n1
, n2
, n5
))
1644 e2
= mesh
.element((n1
, n3
, n5
))
1645 e3
= mesh
.element((n2
, n4
, n5
))
1646 e4
= mesh
.element((n3
, n4
, n5
))
1647 nodes
.extend([n1
, n2
, n3
, n4
, n5
])
1648 elements
.extend([e1
, e2
, e3
, e4
])
1649 m
= mesh
.canvasmesh(elements
, nodes
)
1652 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1653 raise NotImplementedError