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
25 from __future__
import nested_scopes
28 from pyx
import attr
, deco
, style
, color
, unit
, canvas
, path
, mesh
29 from pyx
import text
as textmodule
36 # fallback implementation for Python 2.2 and below
38 return reduce(lambda x
, y
: x
+y
, list, 0)
43 # fallback implementation for Python 2.2. and below
45 return zip(xrange(len(list)), list)
48 """Interface class for graph styles
50 Each graph style must support the methods described in this
51 class. However, since a graph style might not need to perform
52 actions on all the various events, it does not need to overwrite
53 all methods of this base class (e.g. this class is not an abstract
54 class in any respect).
56 A style should never store private data by istance variables
57 (i.e. accessing self), but it should use the sharedata and privatedata
58 instances instead. A style instance can be used multiple times with
59 different sharedata and privatedata instances at the very same time.
60 The sharedata and privatedata instances act as data containers and
61 sharedata allows for sharing information across several styles.
63 Every style contains two class variables, which are not to be
65 - providesdata is a list of variable names a style offers via
66 the sharedata instance. This list is used to determine whether
67 all needs of subsequent styles are fullfilled. Otherwise
68 getdefaultprovider should return a proper style to be used.
69 - needsdata is a list of variable names the style needs to access in the
73 providesdata
= [] # by default, we provide nothing
74 needsdata
= [] # and do not depend on anything
76 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
77 """Set column information
79 This method is used setup the column name information to be
80 accessible to the style later on. The style should analyse
81 the list of column names. The method should return a list of
82 column names which the style will make use of."""
85 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
88 This method is called in order to adjust the axis range to
89 the provided data. columnname is the column name (each style
90 is subsequently called for all column names)."""
93 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
94 """Select stroke/fill attributes
96 This method is called to allow for the selection of
97 changable attributes of a style."""
100 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
101 """Initialize drawing of data
103 This method might be used to initialize the drawing of data."""
106 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
109 This method is called for each data point. The data is
110 available in the dictionary point. The dictionary
111 keys are the column names."""
114 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
115 """Finalize drawing of data
117 This method is called after the last data point was
118 drawn using the drawpoint method above."""
121 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
125 # The following two methods are used to register and get a default provider
126 # for keys. A key is a variable name in sharedata. A provider is a style
127 # which creates variables in sharedata.
129 _defaultprovider
= {}
131 def registerdefaultprovider(style
, keys
):
132 """sets a style as a default creator for sharedata variables 'keys'"""
134 assert key
in style
.providesdata
, "key not provided by style"
135 # we might allow for overwriting the defaults, i.e. the following is not checked:
136 # assert key in _defaultprovider.keys(), "default provider already registered for key"
137 _defaultprovider
[key
] = style
139 def getdefaultprovider(key
):
140 """returns a style, which acts as a default creator for the
141 sharedata variable 'key'"""
142 return _defaultprovider
[key
]
147 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
149 def __init__(self
, epsilon
=1e-10):
150 self
.epsilon
= epsilon
152 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
153 sharedata
.poscolumnnames
= []
154 sharedata
.vposmissing
= []
155 for count
, axisnames
in enumerate(graph
.axesnames
):
156 for axisname
in axisnames
:
157 for columnname
in columnnames
:
158 if axisname
== columnname
:
159 sharedata
.poscolumnnames
.append(columnname
)
160 if len(sharedata
.poscolumnnames
) > count
+1:
161 raise ValueError("multiple axes per graph dimension")
162 elif len(sharedata
.poscolumnnames
) < count
+1:
163 sharedata
.vposmissing
.append(count
)
164 sharedata
.poscolumnnames
.append(None)
165 return [columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None]
167 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
168 if columnname
in sharedata
.poscolumnnames
:
169 graph
.axes
[columnname
].adjustaxis(data
)
171 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
172 sharedata
.vpos
= [None]*(len(graph
.axesnames
))
173 privatedata
.pointpostmplist
= [[columnname
, index
, graph
.axes
[columnname
]] # temporarily used by drawpoint only
174 for index
, columnname
in enumerate([columnname
for columnname
in sharedata
.poscolumnnames
if columnname
is not None])]
175 for missing
in sharedata
.vposmissing
:
176 for pointpostmp
in privatedata
.pointpostmplist
:
177 if pointpostmp
[1] >= missing
:
180 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
181 sharedata
.vposavailable
= 1 # valid position (but might be outside of the graph)
182 sharedata
.vposvalid
= 1 # valid position inside the graph
183 for columnname
, index
, axis
in privatedata
.pointpostmplist
:
185 v
= axis
.convert(point
[columnname
])
186 except (ArithmeticError, ValueError, TypeError):
187 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
188 sharedata
.vpos
[index
] = None
190 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
191 sharedata
.vposvalid
= 0
192 sharedata
.vpos
[index
] = v
195 registerdefaultprovider(pos(), pos
.providesdata
)
200 providesdata
= ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
210 def __init__(self
, usenames
={}, epsilon
=1e-10):
211 self
.usenames
= usenames
212 self
.epsilon
= epsilon
214 def _numberofbits(self
, mask
):
218 return self
._numberofbits
(mask
>> 1) + 1
220 return self
._numberofbits
(mask
>> 1)
222 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
224 privatedata
.rangeposcolumns
= []
225 sharedata
.vrangemissing
= []
226 sharedata
.vrangeminmissing
= []
227 sharedata
.vrangemaxmissing
= []
228 privatedata
.rangeposdeltacolumns
= {} # temporarily used by adjustaxis only
229 for count
, axisnames
in enumerate(graph
.axesnames
):
230 for axisname
in axisnames
:
232 usename
= self
.usenames
[axisname
]
236 for columnname
in columnnames
:
238 if usename
== columnname
:
239 mask
+= self
.mask_value
240 elif usename
+ "min" == columnname
:
241 mask
+= self
.mask_min
242 elif usename
+ "max" == columnname
:
243 mask
+= self
.mask_max
244 elif "d" + usename
+ "min" == columnname
:
245 mask
+= self
.mask_dmin
246 elif "d" + usename
+ "max" == columnname
:
247 mask
+= self
.mask_dmax
248 elif "d" + usename
== columnname
:
253 usecolumns
.append(columnname
)
254 if mask
& (self
.mask_min | self
.mask_max | self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
255 if (self
._numberofbits
(mask
& (self
.mask_min | self
.mask_dmin | self
.mask_d
)) > 1 or
256 self
._numberofbits
(mask
& (self
.mask_max | self
.mask_dmax | self
.mask_d
)) > 1):
257 raise ValueError("multiple range definition")
258 if mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
259 if not (mask
& self
.mask_value
):
260 raise ValueError("missing value for delta")
261 privatedata
.rangeposdeltacolumns
[axisname
] = {}
262 privatedata
.rangeposcolumns
.append((axisname
, usename
, mask
))
263 elif mask
== self
.mask_value
:
264 usecolumns
= usecolumns
[:-1]
265 if len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) > count
+1:
266 raise ValueError("multiple axes per graph dimension")
267 elif len(privatedata
.rangeposcolumns
) + len(sharedata
.vrangemissing
) < count
+1:
268 sharedata
.vrangemissing
.append(count
)
269 sharedata
.vrangeminmissing
.append(count
)
270 sharedata
.vrangemaxmissing
.append(count
)
272 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_min | self
.mask_dmin | self
.mask_d
)):
273 sharedata
.vrangeminmissing
.append(count
)
274 if not (privatedata
.rangeposcolumns
[-1][2] & (self
.mask_max | self
.mask_dmax | self
.mask_d
)):
275 sharedata
.vrangemaxmissing
.append(count
)
278 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
279 if columnname
in [c
+ "min" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_min
]:
280 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
281 if columnname
in [c
+ "max" for a
, c
, m
in privatedata
.rangeposcolumns
if m
& self
.mask_max
]:
282 graph
.axes
[columnname
[:-3]].adjustaxis(data
)
284 # delta handling: fill rangeposdeltacolumns
285 for axisname
, usename
, mask
in privatedata
.rangeposcolumns
:
286 if columnname
== usename
and mask
& (self
.mask_dmin | self
.mask_dmax | self
.mask_d
):
287 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_value
] = data
288 if columnname
== "d" + usename
+ "min" and mask
& self
.mask_dmin
:
289 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmin
] = data
290 if columnname
== "d" + usename
+ "max" and mask
& self
.mask_dmax
:
291 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_dmax
] = data
292 if columnname
== "d" + usename
and mask
& self
.mask_d
:
293 privatedata
.rangeposdeltacolumns
[axisname
][self
.mask_d
] = data
295 # delta handling: process rangeposdeltacolumns
296 for a
, d
in privatedata
.rangeposdeltacolumns
.items():
297 if d
.has_key(self
.mask_value
):
299 if k
!= self
.mask_value
:
300 if k
& (self
.mask_dmin | self
.mask_d
):
302 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
304 mindata
.append(value
-delta
)
307 graph
.axes
[a
].adjustaxis(mindata
)
308 if k
& (self
.mask_dmax | self
.mask_d
):
310 for value
, delta
in zip(d
[self
.mask_value
], d
[k
]):
312 maxdata
.append(value
+delta
)
315 graph
.axes
[a
].adjustaxis(maxdata
)
318 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
319 sharedata
.vrange
= [[None for x
in xrange(2)] for y
in privatedata
.rangeposcolumns
+ sharedata
.vrangemissing
]
320 privatedata
.rangepostmplist
= [[usename
, mask
, index
, graph
.axes
[axisname
]] # temporarily used by drawpoint only
321 for index
, (axisname
, usename
, mask
) in enumerate(privatedata
.rangeposcolumns
)]
322 for missing
in sharedata
.vrangemissing
:
323 for rangepostmp
in privatedata
.rangepostmplist
:
324 if rangepostmp
[2] >= missing
:
327 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
328 for usename
, mask
, index
, axis
in privatedata
.rangepostmplist
:
330 if mask
& self
.mask_min
:
331 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
+ "min"])
332 if mask
& self
.mask_dmin
:
333 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
+ "min"])
334 if mask
& self
.mask_d
:
335 sharedata
.vrange
[index
][0] = axis
.convert(point
[usename
] - point
["d" + usename
])
336 except (ArithmeticError, ValueError, TypeError):
337 sharedata
.vrange
[index
][0] = None
339 if mask
& self
.mask_max
:
340 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
+ "max"])
341 if mask
& self
.mask_dmax
:
342 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
+ "max"])
343 if mask
& self
.mask_d
:
344 sharedata
.vrange
[index
][1] = axis
.convert(point
[usename
] + point
["d" + usename
])
345 except (ArithmeticError, ValueError, TypeError):
346 sharedata
.vrange
[index
][1] = None
348 # some range checks for data consistency
349 if (sharedata
.vrange
[index
][0] is not None and sharedata
.vrange
[index
][1] is not None and
350 sharedata
.vrange
[index
][0] > sharedata
.vrange
[index
][1] + self
.epsilon
):
351 raise ValueError("inverse range")
352 # disabled due to missing vpos access:
353 # if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
354 # sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
355 # raise ValueError("negative minimum errorbar")
356 # if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
357 # sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
358 # raise ValueError("negative maximum errorbar")
361 registerdefaultprovider(range(), range.providesdata
)
364 def _crosssymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
365 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
366 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
367 path
.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
)), attrs
)
370 def _plussymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
371 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.707106781*size_pt
, y_pt
),
372 path
.lineto_pt(x_pt
+0.707106781*size_pt
, y_pt
),
373 path
.moveto_pt(x_pt
, y_pt
-0.707106781*size_pt
),
374 path
.lineto_pt(x_pt
, y_pt
+0.707106781*size_pt
)), attrs
)
376 def _squaresymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
377 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.5*size_pt
, y_pt
-0.5*size_pt
),
378 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
-0.5*size_pt
),
379 path
.lineto_pt(x_pt
+0.5*size_pt
, y_pt
+0.5*size_pt
),
380 path
.lineto_pt(x_pt
-0.5*size_pt
, y_pt
+0.5*size_pt
),
381 path
.closepath()), attrs
)
383 def _trianglesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
384 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
385 path
.lineto_pt(x_pt
+0.759835685*size_pt
, y_pt
-0.438691337*size_pt
),
386 path
.lineto_pt(x_pt
, y_pt
+0.877382675*size_pt
),
387 path
.closepath()), attrs
)
389 def _circlesymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
390 c
.draw(path
.path(path
.arc_pt(x_pt
, y_pt
, 0.564189583*size_pt
, 0, 360),
391 path
.closepath()), attrs
)
393 def _diamondsymbol(c
, x_pt
, y_pt
, size_pt
, attrs
):
394 c
.draw(path
.path(path
.moveto_pt(x_pt
-0.537284965*size_pt
, y_pt
),
395 path
.lineto_pt(x_pt
, y_pt
-0.930604859*size_pt
),
396 path
.lineto_pt(x_pt
+0.537284965*size_pt
, y_pt
),
397 path
.lineto_pt(x_pt
, y_pt
+0.930604859*size_pt
),
398 path
.closepath()), attrs
)
401 class _styleneedingpointpos(_style
):
403 needsdata
= ["vposmissing"]
405 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
406 if len(sharedata
.vposmissing
):
407 raise ValueError("incomplete position information")
411 class symbol(_styleneedingpointpos
):
413 needsdata
= ["vpos", "vposmissing", "vposvalid"]
415 # "inject" the predefinied symbols into the class:
417 # Note, that statements like cross = _crosssymbol are
418 # invalid, since the would lead to unbound methods, but
419 # a single entry changeable list does the trick.
421 # Once we require Python 2.2+ we should use staticmethods
422 # to implement the default symbols inplace.
424 cross
= attr
.changelist([_crosssymbol
])
425 plus
= attr
.changelist([_plussymbol
])
426 square
= attr
.changelist([_squaresymbol
])
427 triangle
= attr
.changelist([_trianglesymbol
])
428 circle
= attr
.changelist([_circlesymbol
])
429 diamond
= attr
.changelist([_diamondsymbol
])
431 changecross
= attr
.changelist([_crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
])
432 changeplus
= attr
.changelist([_plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
])
433 changesquare
= attr
.changelist([_squaresymbol
, _trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
])
434 changetriangle
= attr
.changelist([_trianglesymbol
, _circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
])
435 changecircle
= attr
.changelist([_circlesymbol
, _diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
])
436 changediamond
= attr
.changelist([_diamondsymbol
, _crosssymbol
, _plussymbol
, _squaresymbol
, _trianglesymbol
, _circlesymbol
])
437 changesquaretwice
= attr
.changelist([_squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
])
438 changetriangletwice
= attr
.changelist([_trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
])
439 changecircletwice
= attr
.changelist([_circlesymbol
, _circlesymbol
, _diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
])
440 changediamondtwice
= attr
.changelist([_diamondsymbol
, _diamondsymbol
, _squaresymbol
, _squaresymbol
, _trianglesymbol
, _trianglesymbol
, _circlesymbol
, _circlesymbol
])
442 changestrokedfilled
= attr
.changelist([deco
.stroked
, deco
.filled
])
443 changefilledstroked
= attr
.changelist([deco
.filled
, deco
.stroked
])
445 defaultsymbolattrs
= [deco
.stroked
]
447 def __init__(self
, symbol
=changecross
, size
=0.2*unit
.v_cm
, symbolattrs
=[]):
450 self
.symbolattrs
= symbolattrs
452 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
453 privatedata
.symbol
= attr
.selectattr(self
.symbol
, selectindex
, selecttotal
)
454 privatedata
.size_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
455 if self
.symbolattrs
is not None:
456 privatedata
.symbolattrs
= attr
.selectattrs(self
.defaultsymbolattrs
+ self
.symbolattrs
, selectindex
, selecttotal
)
458 privatedata
.symbolattrs
= None
460 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
461 privatedata
.symbolcanvas
= canvas
.canvas()
463 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
464 if sharedata
.vposvalid
and privatedata
.symbolattrs
is not None:
465 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
466 privatedata
.symbol(privatedata
.symbolcanvas
, x_pt
, y_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
468 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
469 graph
.insert(privatedata
.symbolcanvas
)
471 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
472 if privatedata
.symbolattrs
is not None:
473 privatedata
.symbol(graph
, x_pt
+0.5*width_pt
, y_pt
+0.5*height_pt
, privatedata
.size_pt
, privatedata
.symbolattrs
)
476 class _line(_styleneedingpointpos
):
478 # this style is not a complete style, but it provides the basic functionality to
479 # create a line, which is cut at the graph boundaries (or at otherwise invalid points)
481 def initpointstopath(self
, privatedata
):
482 privatedata
.path
= path
.path()
483 privatedata
.linebasepoints
= []
484 privatedata
.lastvpos
= None
486 def addpointstopath(self
, privatedata
):
487 # add baselinepoints to privatedata.path
488 if len(privatedata
.linebasepoints
) > 1:
489 privatedata
.path
.append(path
.moveto_pt(*privatedata
.linebasepoints
[0]))
490 if len(privatedata
.linebasepoints
) > 2:
491 privatedata
.path
.append(path
.multilineto_pt(privatedata
.linebasepoints
[1:]))
493 privatedata
.path
.append(path
.lineto_pt(*privatedata
.linebasepoints
[1]))
494 privatedata
.linebasepoints
= []
496 def addpoint(self
, privatedata
, graphvpos_pt
, vposavailable
, vposvalid
, vpos
):
497 # append linebasepoints
499 if len(privatedata
.linebasepoints
):
500 # the last point was inside the graph
501 if vposvalid
: # shortcut for the common case
502 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
506 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
509 # 1 = vstart + (vend - vstart) * cut
511 newcut
= (1 - vstart
)/(vend
- vstart
)
512 except (ArithmeticError, TypeError):
515 # 0 = vstart + (vend - vstart) * cut
517 newcut
= - vstart
/(vend
- vstart
)
518 except (ArithmeticError, TypeError):
520 if newcut
is not None and newcut
< cut
:
524 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
525 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
526 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
527 self
.addpointstopath(privatedata
)
529 # the last point was outside the graph
530 if privatedata
.lastvpos
is not None:
534 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
537 # 1 = vstart + (vend - vstart) * cut
539 newcut
= (1 - vstart
)/(vend
- vstart
)
540 except (ArithmeticError, TypeError):
543 # 0 = vstart + (vend - vstart) * cut
545 newcut
= - vstart
/(vend
- vstart
)
546 except (ArithmeticError, TypeError):
548 if newcut
is not None and newcut
> cut
:
552 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
553 cutvpos
.append(vstart
+ (vend
- vstart
) * cut
)
554 privatedata
.linebasepoints
.append(graphvpos_pt(*cutvpos
))
555 privatedata
.linebasepoints
.append(graphvpos_pt(*vpos
))
557 # sometimes cut beginning and end
560 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
565 # 1 = vstart + (vend - vstart) * cutfrom
567 newcutfrom
= (1 - vstart
)/(vend
- vstart
)
568 except (ArithmeticError, TypeError):
573 # 0 = vstart + (vend - vstart) * cutfrom
575 newcutfrom
= - vstart
/(vend
- vstart
)
576 except (ArithmeticError, TypeError):
578 if newcutfrom
is not None and newcutfrom
> cutfrom
:
582 # 1 = vstart + (vend - vstart) * cutto
584 newcutto
= (1 - vstart
)/(vend
- vstart
)
585 except (ArithmeticError, TypeError):
588 # 0 = vstart + (vend - vstart) * cutto
590 newcutto
= - vstart
/(vend
- vstart
)
591 except (ArithmeticError, TypeError):
593 if newcutto
is not None and newcutto
< cutto
:
599 for vstart
, vend
in zip(privatedata
.lastvpos
, vpos
):
600 cutfromvpos
.append(vstart
+ (vend
- vstart
) * cutfrom
)
601 cuttovpos
.append(vstart
+ (vend
- vstart
) * cutto
)
602 privatedata
.linebasepoints
.append(graphvpos_pt(*cutfromvpos
))
603 privatedata
.linebasepoints
.append(graphvpos_pt(*cuttovpos
))
604 self
.addpointstopath(privatedata
)
605 privatedata
.lastvpos
= vpos
[:]
607 if len(privatedata
.linebasepoints
) > 1:
608 self
.addpointstopath(privatedata
)
609 privatedata
.lastvpos
= None
611 def addinvalid(self
, privatedata
):
612 if len(privatedata
.linebasepoints
) > 1:
613 self
.addpointstopath(privatedata
)
614 privatedata
.lastvpos
= None
616 def donepointstopath(self
, privatedata
):
617 if len(privatedata
.linebasepoints
) > 1:
618 self
.addpointstopath(privatedata
)
619 return privatedata
.path
624 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
626 changelinestyle
= attr
.changelist([style
.linestyle
.solid
,
627 style
.linestyle
.dashed
,
628 style
.linestyle
.dotted
,
629 style
.linestyle
.dashdotted
])
631 defaultlineattrs
= [changelinestyle
]
633 def __init__(self
, lineattrs
=[]):
634 self
.lineattrs
= lineattrs
636 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
637 if self
.lineattrs
is not None:
638 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
640 privatedata
.lineattrs
= None
642 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
643 self
.initpointstopath(privatedata
)
645 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
646 self
.addpoint(privatedata
, graph
.vpos_pt
, sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
)
648 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
649 path
= self
.donepointstopath(privatedata
)
650 if privatedata
.lineattrs
is not None and len(path
):
651 graph
.stroke(path
, privatedata
.lineattrs
)
653 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
654 if privatedata
.lineattrs
is not None:
655 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
)
658 class impulses(_styleneedingpointpos
):
660 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
662 defaultlineattrs
= [line
.changelinestyle
]
663 defaultfrompathattrs
= []
665 def __init__(self
, lineattrs
=[], fromvalue
=0, frompathattrs
=[], valueaxisindex
=1):
666 self
.lineattrs
= lineattrs
667 self
.fromvalue
= fromvalue
668 self
.frompathattrs
= frompathattrs
669 self
.valueaxisindex
= valueaxisindex
671 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
672 privatedata
.insertfrompath
= selectindex
== 0
673 if self
.lineattrs
is not None:
674 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
676 privatedata
.lineattrs
= None
678 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
679 if self
.fromvalue
is not None:
681 i
= sharedata
.poscolumnnames
.index(columnname
)
685 if i
== self
.valueaxisindex
:
686 graph
.axes
[sharedata
.poscolumnnames
[i
]].adjustaxis([self
.fromvalue
])
688 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
689 privatedata
.impulsescanvas
= canvas
.canvas()
690 if self
.fromvalue
is not None:
691 valueaxisname
= sharedata
.poscolumnnames
[self
.valueaxisindex
]
692 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
693 privatedata
.vfromvaluecut
= 0
694 if privatedata
.vfromvalue
< 0:
695 privatedata
.vfromvalue
= 0
696 if privatedata
.vfromvalue
> 1:
697 privatedata
.vfromvalue
= 1
698 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
699 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
700 self
.defaultfrompathattrs
+ self
.frompathattrs
)
702 privatedata
.vfromvalue
= 0
704 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
705 if sharedata
.vposvalid
and privatedata
.lineattrs
is not None:
706 vpos
= sharedata
.vpos
[:]
707 vpos
[self
.valueaxisindex
] = privatedata
.vfromvalue
708 privatedata
.impulsescanvas
.stroke(graph
.vgeodesic(*(vpos
+ sharedata
.vpos
)), privatedata
.lineattrs
)
710 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
711 graph
.insert(privatedata
.impulsescanvas
)
713 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
714 if privatedata
.lineattrs
is not None:
715 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
)
718 class errorbar(_style
):
720 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
722 defaulterrorbarattrs
= []
724 def __init__(self
, size
=0.1*unit
.v_cm
,
728 self
.errorbarattrs
= errorbarattrs
729 self
.epsilon
= epsilon
731 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
732 for i
in sharedata
.vposmissing
:
733 if i
in sharedata
.vrangeminmissing
and i
in sharedata
.vrangemaxmissing
:
734 raise ValueError("position and range for a graph dimension missing")
737 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
738 privatedata
.errorsize_pt
= unit
.topt(attr
.selectattr(self
.size
, selectindex
, selecttotal
))
739 privatedata
.errorbarattrs
= attr
.selectattrs(self
.defaulterrorbarattrs
+ self
.errorbarattrs
, selectindex
, selecttotal
)
741 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
742 if privatedata
.errorbarattrs
is not None:
743 privatedata
.errorbarcanvas
= canvas
.canvas(privatedata
.errorbarattrs
)
744 privatedata
.dimensionlist
= list(xrange(len(sharedata
.vpos
)))
746 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
747 if privatedata
.errorbarattrs
is not None:
748 for i
in privatedata
.dimensionlist
:
749 for j
in privatedata
.dimensionlist
:
751 (sharedata
.vpos
[j
] is None or
752 sharedata
.vpos
[j
] < -self
.epsilon
or
753 sharedata
.vpos
[j
] > 1+self
.epsilon
)):
756 if ((sharedata
.vrange
[i
][0] is None and sharedata
.vpos
[i
] is None) or
757 (sharedata
.vrange
[i
][1] is None and sharedata
.vpos
[i
] is None) or
758 (sharedata
.vrange
[i
][0] is None and sharedata
.vrange
[i
][1] is None)):
760 vminpos
= sharedata
.vpos
[:]
761 if sharedata
.vrange
[i
][0] is not None:
762 vminpos
[i
] = sharedata
.vrange
[i
][0]
766 if vminpos
[i
] > 1+self
.epsilon
:
768 if vminpos
[i
] < -self
.epsilon
:
771 vmaxpos
= sharedata
.vpos
[:]
772 if sharedata
.vrange
[i
][1] is not None:
773 vmaxpos
[i
] = sharedata
.vrange
[i
][1]
777 if vmaxpos
[i
] < -self
.epsilon
:
779 if vmaxpos
[i
] > 1+self
.epsilon
:
782 privatedata
.errorbarcanvas
.stroke(graph
.vgeodesic(*(vminpos
+ vmaxpos
)))
783 for j
in privatedata
.dimensionlist
:
786 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vminpos
))
788 privatedata
.errorbarcanvas
.stroke(graph
.vcap_pt(j
, privatedata
.errorsize_pt
, *vmaxpos
))
790 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
791 if privatedata
.errorbarattrs
is not None:
792 graph
.insert(privatedata
.errorbarcanvas
)
795 class text(_styleneedingpointpos
):
797 needsdata
= ["vpos", "vposmissing", "vposvalid"]
799 defaulttextattrs
= [textmodule
.halign
.center
, textmodule
.vshift
.mathaxis
]
801 def __init__(self
, textname
="text", dxname
=None, dyname
=None,
802 dxunit
=0.3*unit
.v_cm
, dyunit
=0.3*unit
.v_cm
,
803 textdx
=0*unit
.v_cm
, textdy
=0.3*unit
.v_cm
, textattrs
=[]):
804 self
.textname
= textname
811 self
.textattrs
= textattrs
813 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
814 if self
.textname
not in columnnames
:
815 raise ValueError("column '%s' missing" % self
.textname
)
816 names
= [self
.textname
]
817 if self
.dxname
is not None:
818 if self
.dxname
not in columnnames
:
819 raise ValueError("column '%s' missing" % self
.dxname
)
820 names
.append(self
.dxname
)
821 if self
.dyname
is not None:
822 if self
.dyname
not in columnnames
:
823 raise ValueError("column '%s' missing" % self
.dyname
)
824 names
.append(self
.dyname
)
825 return names
+ _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
827 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
828 if self
.textattrs
is not None:
829 privatedata
.textattrs
= attr
.selectattrs(self
.defaulttextattrs
+ self
.textattrs
, selectindex
, selecttotal
)
831 privatedata
.textattrs
= None
833 def initdrawpoints(self
, privatedata
, sharedata
, grap
):
834 if self
.dxname
is None:
835 privatedata
.textdx_pt
= unit
.topt(self
.textdx
)
837 privatedata
.dxunit_pt
= unit
.topt(self
.dxunit
)
838 if self
.dyname
is None:
839 privatedata
.textdy_pt
= unit
.topt(self
.textdy
)
841 privatedata
.dyunit_pt
= unit
.topt(self
.dyunit
)
843 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
844 if privatedata
.textattrs
is not None and sharedata
.vposvalid
:
845 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
847 text
= str(point
[self
.textname
])
851 if self
.dxname
is None:
852 dx_pt
= privatedata
.textdx_pt
854 dx_pt
= float(point
[self
.dxname
]) * privatedata
.dxunit_pt
855 if self
.dyname
is None:
856 dy_pt
= privatedata
.textdy_pt
858 dy_pt
= float(point
[self
.dyname
]) * privatedata
.dyunit_pt
859 graph
.text_pt(x_pt
+ dx_pt
, y_pt
+ dy_pt
, text
, privatedata
.textattrs
)
861 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
862 raise RuntimeError("Style currently doesn't provide a graph key")
865 class arrow(_styleneedingpointpos
):
867 needsdata
= ["vpos", "vposmissing", "vposvalid"]
869 defaultlineattrs
= []
870 defaultarrowattrs
= []
872 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
):
873 self
.linelength
= linelength
874 self
.arrowsize
= arrowsize
875 self
.lineattrs
= lineattrs
876 self
.arrowattrs
= arrowattrs
877 self
.arrowpos
= arrowpos
878 self
.epsilon
= epsilon
879 self
.decorator
= decorator
881 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
882 if len(graph
.axesnames
) != 2:
883 raise ValueError("arrow style restricted on two-dimensional graphs")
884 if "size" not in columnnames
:
885 raise ValueError("size missing")
886 if "angle" not in columnnames
:
887 raise ValueError("angle missing")
888 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
890 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
891 if self
.lineattrs
is not None:
892 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
894 privatedata
.lineattrs
= None
895 if self
.arrowattrs
is not None:
896 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
898 privatedata
.arrowattrs
= None
900 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
901 privatedata
.arrowcanvas
= canvas
.canvas()
903 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
904 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
905 linelength_pt
= unit
.topt(self
.linelength
)
906 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
908 angle
= point
["angle"] + 0.0
909 size
= point
["size"] + 0.0
913 if point
["size"] > self
.epsilon
:
914 dx
= math
.cos(angle
*math
.pi
/180)
915 dy
= math
.sin(angle
*math
.pi
/180)
916 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
917 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
918 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
919 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
921 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
),
922 privatedata
.lineattrs
+[self
.decorator(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
924 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
)
926 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
927 graph
.insert(privatedata
.arrowcanvas
)
929 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
930 raise RuntimeError("Style currently doesn't provide a graph key")
935 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
937 def __init__(self
, gradient
=color
.gradient
.Grey
):
938 self
.gradient
= gradient
940 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
941 if len(graph
.axesnames
) != 2:
942 raise TypeError("arrow style restricted on two-dimensional graphs")
943 if "color" not in columnnames
:
944 raise ValueError("color missing")
945 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
946 raise ValueError("incomplete range")
949 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
950 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
952 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
953 xvmin
= sharedata
.vrange
[0][0]
954 xvmax
= sharedata
.vrange
[0][1]
955 yvmin
= sharedata
.vrange
[1][0]
956 yvmax
= sharedata
.vrange
[1][1]
957 if (xvmin
is not None and xvmin
< 1 and
958 xvmax
is not None and xvmax
> 0 and
959 yvmin
is not None and yvmin
< 1 and
960 yvmax
is not None and yvmax
> 0):
969 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
970 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
971 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
972 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
973 p
.append(path
.closepath())
974 privatedata
.rectcanvas
.fill(p
, [self
.gradient
.getcolor(point
["color"])])
976 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
977 raise RuntimeError("Style currently doesn't provide a graph key")
980 class histogram(_style
):
982 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
984 defaultlineattrs
= [deco
.stroked
]
985 defaultfrompathattrs
= []
987 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0, rectkey
=0,
988 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
989 self
.lineattrs
= lineattrs
991 self
.fromvalue
= fromvalue
992 self
.frompathattrs
= frompathattrs
993 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
994 self
.rectkey
= rectkey
995 self
.autohistogramaxisindex
= autohistogramaxisindex
996 self
.autohistogrampointpos
= autohistogrampointpos
997 self
.epsilon
= epsilon
999 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1000 if len(graph
.axesnames
) != 2:
1001 raise TypeError("histogram style restricted on two-dimensional graphs")
1002 privatedata
.rangeaxisindex
= None
1003 for i
in builtinrange(len(graph
.axesnames
)):
1004 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
1005 if i
in sharedata
.vposmissing
:
1006 raise ValueError("pos and range missing")
1008 if privatedata
.rangeaxisindex
is not None:
1009 raise ValueError("multiple ranges")
1010 privatedata
.rangeaxisindex
= i
1011 if privatedata
.rangeaxisindex
is None:
1012 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
1013 privatedata
.autohistogram
= 1
1015 privatedata
.autohistogram
= 0
1018 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1019 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
1021 raise ValueError("several data points needed for automatic histogram width calculation")
1023 delta
= data
[1] - data
[0]
1024 min = data
[0] - self
.autohistogrampointpos
* delta
1025 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
1026 graph
.axes
[columnname
].adjustaxis([min, max])
1027 elif self
.fromvalue
is not None and columnname
== sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]:
1028 graph
.axes
[columnname
].adjustaxis([self
.fromvalue
])
1030 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1031 privatedata
.insertfrompath
= selectindex
== 0
1032 if self
.lineattrs
is not None:
1033 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
1035 privatedata
.lineattrs
= None
1037 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
1038 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
1039 if privatedata
.rangeaxisindex
:
1040 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
1042 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
1044 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
1045 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
1060 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
1061 if vvalue1cut
and not self
.fillable
:
1062 if privatedata
.rangeaxisindex
:
1063 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
1065 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
1066 if privatedata
.rangeaxisindex
:
1067 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
1069 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
1071 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
1073 if vvalue
< -self
.epsilon
:
1075 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1076 if vvalue
> 1+self
.epsilon
:
1078 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1079 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1094 if abs(vpos1cut
+ vpos2cut
) <= 1:
1097 if privatedata
.rangeaxisindex
:
1098 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1099 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1101 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1102 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1104 if privatedata
.rangeaxisindex
:
1105 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1107 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1108 if privatedata
.rangeaxisindex
:
1109 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1111 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1112 if self
.fillable
and vpos2cut
:
1113 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1114 if privatedata
.rangeaxisindex
:
1115 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1117 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1119 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1120 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1121 if self
.fillable
and not self
.steps
:
1122 if not currentvalid
:
1125 if vmin
< -self
.epsilon
:
1128 elif vmin
> 1+self
.epsilon
:
1132 if vmax
< -self
.epsilon
:
1135 if vmax
> 1+self
.epsilon
:
1139 if vvalue
< -self
.epsilon
:
1142 if vvalue
> 1+self
.epsilon
:
1146 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1147 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1150 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1153 if privatedata
.rangeaxisindex
:
1154 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1155 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1156 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1157 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1159 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1160 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1161 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1162 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1165 if privatedata
.rangeaxisindex
:
1166 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(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
))
1169 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1171 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(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
))
1174 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1175 elif privatedata
.vfromvaluecut
:
1177 if privatedata
.rangeaxisindex
:
1178 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1179 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1180 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1181 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1183 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1184 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1185 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1186 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1189 if privatedata
.rangeaxisindex
:
1190 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1191 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1192 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1193 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1195 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1196 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1197 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1198 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1200 if privatedata
.rangeaxisindex
:
1201 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1202 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1203 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1204 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1205 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1206 privatedata
.path
.append(path
.closepath())
1208 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1209 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1210 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1211 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1212 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1213 privatedata
.path
.append(path
.closepath())
1216 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1217 except (ArithmeticError, ValueError, TypeError):
1219 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1220 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1221 self
.vposline(privatedata
, sharedata
, graph
,
1222 vmin
, privatedata
.lastvvalue
, vvalue
)
1224 if privatedata
.lastvvalue
is not None and currentvalid
:
1225 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1226 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1227 self
.vposline(privatedata
, sharedata
, graph
,
1228 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1230 self
.vmoveto(privatedata
, sharedata
, graph
,
1232 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1233 self
.vmoveto(privatedata
, sharedata
, graph
,
1234 vmin
, privatedata
.vfromvalue
)
1235 self
.vposline(privatedata
, sharedata
, graph
,
1236 vmin
, privatedata
.vfromvalue
, vvalue
)
1238 self
.vvalueline(privatedata
, sharedata
, graph
,
1240 privatedata
.lastvvalue
= vvalue
1241 privatedata
.lastvmax
= vmax
1243 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1245 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1246 privatedata
.path
= path
.path()
1247 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1248 privatedata
.vcurrentpoint
= None
1249 privatedata
.count
= 0
1250 if self
.fromvalue
is not None:
1251 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1252 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1253 privatedata
.vfromvaluecut
= 0
1254 if privatedata
.vfromvalue
< 0:
1255 privatedata
.vfromvalue
= 0
1256 privatedata
.vfromvaluecut
= -1
1257 if privatedata
.vfromvalue
> 1:
1258 privatedata
.vfromvalue
= 1
1259 privatedata
.vfromvaluecut
= 1
1260 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1261 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1262 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1264 privatedata
.vfromvalue
= 0
1266 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1267 if privatedata
.autohistogram
:
1268 # automatic range handling
1269 privatedata
.count
+= 1
1270 if privatedata
.count
== 2:
1271 if privatedata
.rangeaxisindex
:
1272 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1273 self
.drawvalue(privatedata
, sharedata
, graph
,
1274 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1275 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1276 privatedata
.lastvpos
[0])
1278 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1279 self
.drawvalue(privatedata
, sharedata
, graph
,
1280 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1281 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1282 privatedata
.lastvpos
[1])
1283 elif privatedata
.count
> 2:
1284 if privatedata
.rangeaxisindex
:
1285 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1287 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1288 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1289 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1290 if privatedata
.count
> 1:
1291 if privatedata
.rangeaxisindex
:
1292 self
.drawvalue(privatedata
, sharedata
, graph
,
1293 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1294 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1297 self
.drawvalue(privatedata
, sharedata
, graph
,
1298 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1299 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1301 privatedata
.lastvpos
= sharedata
.vpos
[:]
1303 if privatedata
.rangeaxisindex
:
1304 self
.drawvalue(privatedata
, sharedata
, graph
,
1305 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1307 self
.drawvalue(privatedata
, sharedata
, graph
,
1308 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1310 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1311 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1312 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1313 graph
.draw(privatedata
.path
, privatedata
.lineattrs
)
1315 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1316 if privatedata
.lineattrs
is not None:
1318 p
= path
.rect_pt(x_pt
, y_pt
, width_pt
, height_pt
)
1320 p
= path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
)
1321 graph
.draw(p
, privatedata
.lineattrs
)
1324 class barpos(_style
):
1326 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1328 defaultfrompathattrs
= []
1330 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1331 self
.fromvalue
= fromvalue
1332 self
.frompathattrs
= frompathattrs
1333 self
.epsilon
= epsilon
1335 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1336 sharedata
.barposcolumnnames
= []
1337 sharedata
.barvalueindex
= None
1338 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1340 for axisname
in axisnames
:
1341 if axisname
in columnnames
:
1342 if sharedata
.barvalueindex
is not None:
1343 raise ValueError("multiple values")
1344 sharedata
.barvalueindex
= dimension
1345 sharedata
.barposcolumnnames
.append(axisname
)
1347 if (axisname
+ "name") in columnnames
:
1348 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1351 raise ValueError("multiple names and value")
1353 raise ValueError("value/name missing")
1354 if sharedata
.barvalueindex
is None:
1355 raise ValueError("missing value")
1356 sharedata
.vposmissing
= []
1357 return sharedata
.barposcolumnnames
1359 def addsubvalue(self
, value
, subvalue
):
1364 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1366 return value
, subvalue
1368 return value
, subvalue
1370 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1372 i
= sharedata
.barposcolumnnames
.index(columnname
)
1376 if i
== sharedata
.barvalueindex
:
1377 if self
.fromvalue
is not None:
1378 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1379 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1381 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1382 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1384 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1385 privatedata
.insertfrompath
= selectindex
== 0
1387 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1388 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1389 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1390 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1392 if self
.fromvalue
is not None:
1393 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(self
.fromvalue
)
1394 if privatedata
.vfromvalue
< 0:
1395 privatedata
.vfromvalue
= 0
1396 if privatedata
.vfromvalue
> 1:
1397 privatedata
.vfromvalue
= 1
1398 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1399 graph
.stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].vgridpath(privatedata
.vfromvalue
),
1400 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1402 privatedata
.vfromvalue
= 0
1404 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1405 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1406 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1407 if i
== sharedata
.barvalueindex
:
1408 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1409 sharedata
.lastbarvalue
= point
[barname
]
1411 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1412 except (ArithmeticError, ValueError, TypeError):
1413 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1417 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1418 except (ArithmeticError, ValueError, TypeError):
1419 sharedata
.vbarrange
[i
][j
] = None
1421 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1422 except (ArithmeticError, ValueError, TypeError):
1423 sharedata
.vpos
[i
] = None
1424 if sharedata
.vpos
[i
] is None:
1425 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1426 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1427 sharedata
.vposvalid
= 0
1429 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1432 class stackedbarpos(_style
):
1434 # provides no additional data, but needs some data (and modifies some of them)
1435 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1437 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1438 self
.stackname
= stackname
1439 self
.epsilon
= epsilon
1440 self
.addontop
= addontop
1442 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1443 if self
.stackname
not in columnnames
:
1444 raise ValueError("column '%s' missing" % self
.stackname
)
1445 return [self
.stackname
]
1447 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1448 if columnname
== self
.stackname
:
1449 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1451 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1452 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1453 sharedata
.stackedbar
+= 1
1455 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1456 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1459 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1460 except (ArithmeticError, ValueError, TypeError):
1461 sharedata
.lastbarvalue
= None
1463 sharedata
.lastbarvalue
= point
[self
.stackname
]
1465 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1466 except (ArithmeticError, ValueError, TypeError):
1467 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1468 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1470 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1471 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1472 for v
in sharedata
.vpos
:
1474 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1476 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1477 sharedata
.vposvalid
= 0
1482 needsdata
= ["vbarrange"]
1484 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1486 def __init__(self
, barattrs
=[], epsilon
=1e-10, gradient
=color
.gradient
.RedBlack
):
1487 self
.barattrs
= barattrs
1488 self
.epsilon
= epsilon
1489 self
.gradient
= gradient
1491 def lighting(self
, angle
, zindex
):
1492 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1494 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1497 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1498 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1500 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1501 privatedata
.barcanvas
= graph
.insert(canvas
.canvas())
1502 sharedata
.stackedbardraw
= 1
1503 privatedata
.stackedbar
= sharedata
.stackedbar
1504 privatedata
.todraw
= []
1506 def drawpointfill(self
, privatedata
, p
):
1508 privatedata
.barcanvas
.fill(p
, privatedata
.barattrs
)
1510 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1512 for vmin
, vmax
in sharedata
.vbarrange
:
1513 if vmin
is None or vmax
is None:
1514 self
.drawpointfill(privatedata
, None)
1517 vmin
, vmax
= vmax
, vmin
1518 if vmin
> 1 or vmax
< 0:
1519 self
.drawpointfill(privatedata
, None)
1525 vbarrange
.append((vmin
, vmax
))
1526 if len(vbarrange
) == 2:
1527 p
= graph
.vgeodesic(vbarrange
[0][0], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][0])
1528 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][1]))
1529 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][1]))
1530 p
.append(graph
.vgeodesic_el(vbarrange
[0][0], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][0]))
1531 p
.append(path
.closepath())
1532 self
.drawpointfill(privatedata
, p
)
1533 elif len(vbarrange
) == 3:
1535 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[1][0] - vbarrange
[1][1]):
1536 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1537 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1538 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1539 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0]))
1540 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1541 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1542 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1543 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1]))
1544 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1545 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1546 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1547 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1548 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0]))
1549 planes
.append((vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1550 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1551 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1552 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1]))
1553 if abs(vbarrange
[1][0] - vbarrange
[1][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1554 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1555 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1556 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1557 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1]))
1558 planes
.append((vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1559 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1560 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1561 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0]))
1562 v
= [0.5 * (vbarrange
[0][0] + vbarrange
[0][1]),
1563 0.5 * (vbarrange
[1][0] + vbarrange
[1][1]),
1564 0.5 * (vbarrange
[2][0] + vbarrange
[2][1])]
1565 v
[sharedata
.barvalueindex
] = 0.5
1566 zindex
= graph
.vzindex(*v
)
1567 for v11
, v12
, v13
, v21
, v22
, v23
, v31
, v32
, v33
, v41
, v42
, v43
in planes
:
1568 angle
= graph
.vangle(v11
, v12
, v13
, v21
, v22
, v23
, v41
, v42
, v43
)
1570 p
= graph
.vgeodesic(v11
, v12
, v13
, v21
, v22
, v23
)
1571 p
.append(graph
.vgeodesic_el(v21
, v22
, v23
, v31
, v32
, v33
))
1572 p
.append(graph
.vgeodesic_el(v31
, v32
, v33
, v41
, v42
, v43
))
1573 p
.append(graph
.vgeodesic_el(v41
, v42
, v43
, v11
, v12
, v13
))
1574 p
.append(path
.closepath())
1576 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
+ [self
.lighting(angle
, zindex
)]))
1578 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
))
1580 raise TypeError("bar style restricted to two- and three dimensional graphs")
1582 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1583 privatedata
.todraw
.sort()
1584 for vzindex
, p
, a
in privatedata
.todraw
:
1585 privatedata
.barcanvas
.fill(p
, a
)
1587 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1588 selectindex
= privatedata
.stackedbar
1589 selecttotal
= sharedata
.stackedbar
+ 1
1590 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1593 class changebar(bar
):
1595 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1596 if selecttotal
!= 1:
1597 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1599 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1600 if len(graph
.axesnames
) != 2:
1601 raise TypeError("changebar style restricted on two-dimensional graphs (at least for the moment)")
1602 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1603 privatedata
.bars
= []
1605 def drawpointfill(self
, privatedata
, p
):
1606 privatedata
.bars
.append(p
)
1608 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1609 selecttotal
= len(privatedata
.bars
)
1610 for selectindex
, p
in enumerate(privatedata
.bars
):
1612 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1613 privatedata
.barcanvas
.fill(p
, barattrs
)
1615 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1616 raise RuntimeError("Style currently doesn't provide a graph key")
1619 class gridpos(_style
):
1621 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1622 providesdata
= ["values1", "values2", "data12", "data21", "index1", "index2"]
1624 def __init__(self
, index1
=0, index2
=1, epsilon
=1e-10):
1625 self
.index1
= index1
1626 self
.index2
= index2
1627 self
.epsilon
= epsilon
1629 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1630 sharedata
.index1
= self
.index1
1631 sharedata
.index2
= self
.index2
1632 sharedata
.values1
= {}
1633 sharedata
.values2
= {}
1634 sharedata
.data12
= {}
1635 sharedata
.data21
= {}
1637 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1638 if sharedata
.vposavailable
:
1639 sharedata
.value1
= sharedata
.vpos
[self
.index1
]
1640 sharedata
.value2
= sharedata
.vpos
[self
.index2
]
1641 if not sharedata
.values1
.has_key(sharedata
.value1
):
1642 for hasvalue
in sharedata
.values1
.keys():
1643 if hasvalue
- self
.epsilon
<= sharedata
.value1
<= hasvalue
+ self
.epsilon
:
1644 sharedata
.value1
= hasvalue
1647 sharedata
.values1
[sharedata
.value1
] = 1
1648 if not sharedata
.values2
.has_key(sharedata
.value2
):
1649 for hasvalue
in sharedata
.values2
.keys():
1650 if hasvalue
- self
.epsilon
<= sharedata
.value2
<= hasvalue
+ self
.epsilon
:
1651 sharedata
.value2
= hasvalue
1654 sharedata
.values2
[sharedata
.value2
] = 1
1655 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1656 sharedata
.data12
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = data
1657 sharedata
.data21
.setdefault(sharedata
.value2
, {})[sharedata
.value1
] = data
1659 registerdefaultprovider(gridpos(), gridpos
.providesdata
)
1662 class grid(_line
, _style
):
1664 needsdata
= ["values1", "values2", "data12", "data21"]
1666 defaultgridattrs
= [line
.changelinestyle
]
1668 def __init__(self
, gridlines1
=1, gridlines2
=1, gridattrs
=[]):
1669 self
.gridlines1
= gridlines1
1670 self
.gridlines2
= gridlines2
1671 self
.gridattrs
= gridattrs
1673 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1674 if self
.gridattrs
is not None:
1675 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1677 privatedata
.gridattrs
= None
1679 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1680 values1
= sharedata
.values1
.keys()
1682 values2
= sharedata
.values2
.keys()
1685 for value2
in values2
:
1686 data1
= sharedata
.data21
[value2
]
1687 self
.initpointstopath(privatedata
)
1688 for value1
in values1
:
1690 data
= data1
[value1
]
1692 self
.addinvalid(privatedata
)
1694 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1695 p
= self
.donepointstopath(privatedata
)
1697 graph
.stroke(p
, privatedata
.gridattrs
)
1699 for value1
in values1
:
1700 data2
= sharedata
.data12
[value1
]
1701 self
.initpointstopath(privatedata
)
1702 for value2
in values2
:
1704 data
= data2
[value2
]
1706 self
.addinvalid(privatedata
)
1708 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1709 p
= self
.donepointstopath(privatedata
)
1711 graph
.stroke(p
, privatedata
.gridattrs
)
1714 class surface(_style
):
1716 needsdata
= ["values1", "values2", "data12", "data21"]
1718 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Grey
, mincolor
=None, maxcolor
=None,
1719 gridlines1
=0.05, gridlines2
=0.05, gridcolor
=None,
1720 backcolor
=color
.gray
.black
):
1721 self
.colorname
= colorname
1722 self
.gradient
= gradient
1723 self
.mincolor
= mincolor
1724 self
.maxcolor
= maxcolor
1725 self
.gridlines1
= gridlines1
1726 self
.gridlines2
= gridlines2
1727 self
.gridcolor
= gridcolor
1728 self
.backcolor
= backcolor
1730 colorspacestring
= gradient
.getcolor(0).colorspacestring()
1731 if self
.gridcolor
is not None and self
.gridcolor
.colorspacestring() != colorspacestring
:
1732 raise RuntimeError("colorspace mismatch (gradient/grid)")
1733 if self
.backcolor
is not None and self
.backcolor
.colorspacestring() != colorspacestring
:
1734 raise RuntimeError("colorspace mismatch (gradient/back)")
1736 def midvalue(self
, v1
, v2
, v3
, v4
):
1737 return [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1739 def midcolor(self
, c1
, c2
, c3
, c4
):
1740 return 0.25*(c1
+c2
+c3
+c4
)
1742 def lighting(self
, angle
, zindex
):
1743 if angle
< 0 and self
.backcolor
is not None:
1744 return self
.backcolor
1745 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1747 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1748 privatedata
.colorize
= self
.colorname
in columnnames
1749 if privatedata
.colorize
:
1750 return [self
.colorname
]
1753 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1754 privatedata
.colors
= {}
1755 privatedata
.mincolor
= privatedata
.maxcolor
= None
1757 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1758 if privatedata
.colorize
:
1760 color
= point
[self
.colorname
] + 0
1764 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1765 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1766 privatedata
.mincolor
= color
1767 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1768 privatedata
.maxcolor
= color
1770 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1771 v1
= [0]*len(graph
.axesnames
)
1772 v2
= [0]*len(graph
.axesnames
)
1773 v3
= [0]*len(graph
.axesnames
)
1774 v4
= [0]*len(graph
.axesnames
)
1775 v1
[sharedata
.index2
] = 0.5
1776 v2
[sharedata
.index1
] = 0.5
1777 v3
[sharedata
.index1
] = 0.5
1778 v3
[sharedata
.index2
] = 1
1779 v4
[sharedata
.index1
] = 1
1780 v4
[sharedata
.index2
] = 0.5
1781 sortElements
= [-graph
.vzindex(*v1
),
1782 -graph
.vzindex(*v2
),
1783 -graph
.vzindex(*v3
),
1784 -graph
.vzindex(*v4
)]
1786 values1
= sharedata
.values1
.keys()
1788 v1
= [0]*len(graph
.axesnames
)
1789 v2
= [0]*len(graph
.axesnames
)
1790 v1
[sharedata
.index1
] = -1
1791 v2
[sharedata
.index1
] = 1
1793 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1796 sortElements
= [sortElements
[3], sortElements
[1], sortElements
[2], sortElements
[0]]
1798 values2
= sharedata
.values2
.keys()
1800 v1
= [0]*len(graph
.axesnames
)
1801 v2
= [0]*len(graph
.axesnames
)
1802 v1
[sharedata
.index2
] = -1
1803 v2
[sharedata
.index2
] = 1
1804 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1807 sortElements
= [sortElements
[0], sortElements
[2], sortElements
[1], sortElements
[3]]
1809 sortElements
= [(zindex
, i
) for i
, zindex
in enumerate(sortElements
)]
1812 mincolor
, maxcolor
= privatedata
.mincolor
, privatedata
.maxcolor
1813 if self
.mincolor
is not None:
1814 mincolor
= self
.mincolor
1815 if self
.maxcolor
is not None:
1816 maxcolor
= self
.maxcolor
1819 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1820 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1822 available1
, valid1
, v1
= sharedata
.data12
[value1a
][value2a
]
1823 available2
, valid2
, v2
= sharedata
.data12
[value1a
][value2b
]
1824 available3
, valid3
, v3
= sharedata
.data12
[value1b
][value2a
]
1825 available4
, valid4
, v4
= sharedata
.data12
[value1b
][value2b
]
1828 if not available1
or not available2
or not available3
or not available4
:
1830 if not valid1
or not valid2
or not valid3
or not valid4
:
1831 warnings
.warn("surface elements partially outside of the graph are (currently) skipped completely")
1833 def shrink(index
, v1
, v2
, by
):
1836 for i
in builtinrange(3):
1838 v1
[i
], v2
[i
] = v1
[i
] + by
*(v2
[i
]-v1
[i
]), v2
[i
] + by
*(v1
[i
]-v2
[i
])
1840 v1f
, v2f
, v3f
, v4f
= v1
, v2
, v3
, v4
1841 if self
.gridcolor
is not None and self
.gridlines1
:
1842 v1
, v2
= shrink(sharedata
.index1
, v1
, v2
, self
.gridlines1
)
1843 v3
, v4
= shrink(sharedata
.index1
, v3
, v4
, self
.gridlines1
)
1844 if self
.gridcolor
is not None and self
.gridlines2
:
1845 v1
, v3
= shrink(sharedata
.index2
, v1
, v3
, self
.gridlines2
)
1846 v2
, v4
= shrink(sharedata
.index2
, v2
, v4
, self
.gridlines2
)
1847 v5
= self
.midvalue(v1
, v2
, v3
, v4
)
1848 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1849 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1850 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1851 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1852 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1853 if privatedata
.colorize
:
1854 def colorfromgradient(c
):
1855 vc
= (c
- mincolor
) / float(maxcolor
- mincolor
)
1857 warnings
.warn("gradiend color range is exceeded due to mincolor setting")
1860 warnings
.warn("gradiend color range is exceeded due to maxcolor setting")
1862 return self
.gradient
.getcolor(vc
)
1863 c1
= privatedata
.colors
[value1a
][value2a
]
1864 c2
= privatedata
.colors
[value1a
][value2b
]
1865 c3
= privatedata
.colors
[value1b
][value2a
]
1866 c4
= privatedata
.colors
[value1b
][value2b
]
1867 c5
= self
.midcolor(c1
, c2
, c3
, c4
)
1868 c1a
= c1b
= colorfromgradient(c1
)
1869 c2a
= c2c
= colorfromgradient(c2
)
1870 c3b
= c3d
= colorfromgradient(c3
)
1871 c4c
= c4d
= colorfromgradient(c4
)
1872 c5a
= c5b
= c5c
= c5d
= colorfromgradient(c5
)
1873 if self
.backcolor
is not None and sign
*graph
.vangle(*(v1
+v2
+v5
)) < 0:
1874 c1a
= c2a
= c5a
= self
.backcolor
1875 if self
.backcolor
is not None and sign
*graph
.vangle(*(v3
+v1
+v5
)) < 0:
1876 c3b
= c1b
= c5b
= self
.backcolor
1877 if self
.backcolor
is not None and sign
*graph
.vangle(*(v2
+v4
+v5
)) < 0:
1878 c2c
= c4c
= c5c
= self
.backcolor
1879 if self
.backcolor
is not None and sign
*graph
.vangle(*(v4
+v3
+v5
)) < 0:
1880 c4d
= c3d
= c5d
= self
.backcolor
1882 zindex
= graph
.vzindex(*v5
)
1883 c1a
= c2a
= c5a
= self
.lighting(sign
*graph
.vangle(*(v1
+v2
+v5
)), zindex
)
1884 c3b
= c1b
= c5b
= self
.lighting(sign
*graph
.vangle(*(v3
+v1
+v5
)), zindex
)
1885 c2c
= c4c
= c5c
= self
.lighting(sign
*graph
.vangle(*(v2
+v4
+v5
)), zindex
)
1886 c4d
= c3d
= c5d
= self
.lighting(sign
*graph
.vangle(*(v4
+v3
+v5
)), zindex
)
1887 for zindex
, i
in sortElements
:
1889 elements
.append(mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), c1a
),
1890 mesh
.node_pt((x2_pt
, y2_pt
), c2a
),
1891 mesh
.node_pt((x5_pt
, y5_pt
), c5a
))))
1892 if self
.gridcolor
is not None and self
.gridlines2
:
1893 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1894 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1895 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1896 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1897 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1898 mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
))))
1900 elements
.append(mesh
.element((mesh
.node_pt((x3_pt
, y3_pt
), c3b
),
1901 mesh
.node_pt((x1_pt
, y1_pt
), c1b
),
1902 mesh
.node_pt((x5_pt
, y5_pt
), c5b
))))
1903 if self
.gridcolor
is not None and self
.gridlines1
:
1904 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1905 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1906 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1907 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1908 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1909 mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
))))
1911 elements
.append(mesh
.element((mesh
.node_pt((x2_pt
, y2_pt
), c2c
),
1912 mesh
.node_pt((x4_pt
, y4_pt
), c4c
),
1913 mesh
.node_pt((x5_pt
, y5_pt
), c5c
))))
1914 if self
.gridcolor
is not None and self
.gridlines1
:
1915 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1916 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1917 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
))))
1918 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1919 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1920 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1922 elements
.append(mesh
.element((mesh
.node_pt((x4_pt
, y4_pt
), c4d
),
1923 mesh
.node_pt((x3_pt
, y3_pt
), c3d
),
1924 mesh
.node_pt((x5_pt
, y5_pt
), c5d
))))
1925 if self
.gridcolor
is not None and self
.gridlines2
:
1926 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1927 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1928 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
))))
1929 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1930 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1931 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1932 m
= mesh
.mesh(elements
, check
=0)