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):
873 self
.linelength
= linelength
874 self
.arrowsize
= arrowsize
875 self
.lineattrs
= lineattrs
876 self
.arrowattrs
= arrowattrs
877 self
.arrowpos
= arrowpos
878 self
.epsilon
= epsilon
880 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
881 if len(graph
.axesnames
) != 2:
882 raise ValueError("arrow style restricted on two-dimensional graphs")
883 if "size" not in columnnames
:
884 raise ValueError("size missing")
885 if "angle" not in columnnames
:
886 raise ValueError("angle missing")
887 return ["size", "angle"] + _styleneedingpointpos
.columnnames(self
, privatedata
, sharedata
, graph
, columnnames
)
889 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
890 if self
.lineattrs
is not None:
891 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
893 privatedata
.lineattrs
= None
894 if self
.arrowattrs
is not None:
895 privatedata
.arrowattrs
= attr
.selectattrs(self
.defaultarrowattrs
+ self
.arrowattrs
, selectindex
, selecttotal
)
897 privatedata
.arrowattrs
= None
899 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
900 privatedata
.arrowcanvas
= canvas
.canvas()
902 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
903 if privatedata
.lineattrs
is not None and privatedata
.arrowattrs
is not None and sharedata
.vposvalid
:
904 linelength_pt
= unit
.topt(self
.linelength
)
905 x_pt
, y_pt
= graph
.vpos_pt(*sharedata
.vpos
)
907 angle
= point
["angle"] + 0.0
908 size
= point
["size"] + 0.0
912 if point
["size"] > self
.epsilon
:
913 dx
= math
.cos(angle
*math
.pi
/180)
914 dy
= math
.sin(angle
*math
.pi
/180)
915 x1
= x_pt
-self
.arrowpos
*dx
*linelength_pt
*size
916 y1
= y_pt
-self
.arrowpos
*dy
*linelength_pt
*size
917 x2
= x_pt
+(1-self
.arrowpos
)*dx
*linelength_pt
*size
918 y2
= y_pt
+(1-self
.arrowpos
)*dy
*linelength_pt
*size
919 privatedata
.arrowcanvas
.stroke(path
.line_pt(x1
, y1
, x2
, y2
), privatedata
.lineattrs
+
920 [deco
.earrow(privatedata
.arrowattrs
, size
=self
.arrowsize
*size
)])
922 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
923 graph
.insert(privatedata
.arrowcanvas
)
925 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
926 raise RuntimeError("Style currently doesn't provide a graph key")
931 needsdata
= ["vrange", "vrangeminmissing", "vrangemaxmissing"]
933 def __init__(self
, gradient
=color
.gradient
.Grey
):
934 self
.gradient
= gradient
936 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
937 if len(graph
.axesnames
) != 2:
938 raise TypeError("arrow style restricted on two-dimensional graphs")
939 if "color" not in columnnames
:
940 raise ValueError("color missing")
941 if len(sharedata
.vrangeminmissing
) + len(sharedata
.vrangemaxmissing
):
942 raise ValueError("incomplete range")
945 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
946 privatedata
.rectcanvas
= graph
.insert(canvas
.canvas())
948 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
949 xvmin
= sharedata
.vrange
[0][0]
950 xvmax
= sharedata
.vrange
[0][1]
951 yvmin
= sharedata
.vrange
[1][0]
952 yvmax
= sharedata
.vrange
[1][1]
953 if (xvmin
is not None and xvmin
< 1 and
954 xvmax
is not None and xvmax
> 0 and
955 yvmin
is not None and yvmin
< 1 and
956 yvmax
is not None and yvmax
> 0):
965 p
= graph
.vgeodesic(xvmin
, yvmin
, xvmax
, yvmin
)
966 p
.append(graph
.vgeodesic_el(xvmax
, yvmin
, xvmax
, yvmax
))
967 p
.append(graph
.vgeodesic_el(xvmax
, yvmax
, xvmin
, yvmax
))
968 p
.append(graph
.vgeodesic_el(xvmin
, yvmax
, xvmin
, yvmin
))
969 p
.append(path
.closepath())
970 privatedata
.rectcanvas
.fill(p
, [self
.gradient
.getcolor(point
["color"])])
972 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
973 raise RuntimeError("Style currently doesn't provide a graph key")
976 class histogram(_style
):
978 needsdata
= ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
980 defaultlineattrs
= [deco
.stroked
]
981 defaultfrompathattrs
= []
983 def __init__(self
, lineattrs
=[], steps
=0, fromvalue
=0, frompathattrs
=[], fillable
=0, rectkey
=0,
984 autohistogramaxisindex
=0, autohistogrampointpos
=0.5, epsilon
=1e-10):
985 self
.lineattrs
= lineattrs
987 self
.fromvalue
= fromvalue
988 self
.frompathattrs
= frompathattrs
989 self
.fillable
= fillable
# TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
990 self
.rectkey
= rectkey
991 self
.autohistogramaxisindex
= autohistogramaxisindex
992 self
.autohistogrampointpos
= autohistogrampointpos
993 self
.epsilon
= epsilon
995 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
996 if len(graph
.axesnames
) != 2:
997 raise TypeError("histogram style restricted on two-dimensional graphs")
998 privatedata
.rangeaxisindex
= None
999 for i
in builtinrange(len(graph
.axesnames
)):
1000 if i
in sharedata
.vrangeminmissing
or i
in sharedata
.vrangemaxmissing
:
1001 if i
in sharedata
.vposmissing
:
1002 raise ValueError("pos and range missing")
1004 if privatedata
.rangeaxisindex
is not None:
1005 raise ValueError("multiple ranges")
1006 privatedata
.rangeaxisindex
= i
1007 if privatedata
.rangeaxisindex
is None:
1008 privatedata
.rangeaxisindex
= self
.autohistogramaxisindex
1009 privatedata
.autohistogram
= 1
1011 privatedata
.autohistogram
= 0
1014 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1015 if privatedata
.autohistogram
and columnname
== sharedata
.poscolumnnames
[privatedata
.rangeaxisindex
]:
1017 raise ValueError("several data points needed for automatic histogram width calculation")
1019 delta
= data
[1] - data
[0]
1020 min = data
[0] - self
.autohistogrampointpos
* delta
1021 max = data
[-1] + (1-self
.autohistogrampointpos
) * delta
1022 graph
.axes
[columnname
].adjustaxis([min, max])
1023 elif self
.fromvalue
is not None and columnname
== sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]:
1024 graph
.axes
[columnname
].adjustaxis([self
.fromvalue
])
1026 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1027 privatedata
.insertfrompath
= selectindex
== 0
1028 if self
.lineattrs
is not None:
1029 privatedata
.lineattrs
= attr
.selectattrs(self
.defaultlineattrs
+ self
.lineattrs
, selectindex
, selecttotal
)
1031 privatedata
.lineattrs
= None
1033 def vmoveto(self
, privatedata
, sharedata
, graph
, vpos
, vvalue
):
1034 if -self
.epsilon
< vpos
< 1+self
.epsilon
and -self
.epsilon
< vvalue
< 1+self
.epsilon
:
1035 if privatedata
.rangeaxisindex
:
1036 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos
)))
1038 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue
)))
1040 def vposline(self
, privatedata
, sharedata
, graph
, vpos
, vvalue1
, vvalue2
):
1041 if -self
.epsilon
< vpos
< 1+self
.epsilon
:
1056 if abs(vvalue1cut
+ vvalue2cut
) <= 1:
1057 if vvalue1cut
and not self
.fillable
:
1058 if privatedata
.rangeaxisindex
:
1059 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue1
, vpos
)))
1061 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos
, vvalue1
)))
1062 if privatedata
.rangeaxisindex
:
1063 privatedata
.path
.append(graph
.vgeodesic_el(vvalue1
, vpos
, vvalue2
, vpos
))
1065 privatedata
.path
.append(graph
.vgeodesic_el(vpos
, vvalue1
, vpos
, vvalue2
))
1067 def vvalueline(self
, privatedata
, sharedata
, graph
, vvalue
, vpos1
, vpos2
):
1069 if vvalue
< -self
.epsilon
:
1071 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1072 if vvalue
> 1+self
.epsilon
:
1074 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1075 if self
.fillable
or (-self
.epsilon
< vvalue
< 1+self
.epsilon
):
1090 if abs(vpos1cut
+ vpos2cut
) <= 1:
1093 if privatedata
.rangeaxisindex
:
1094 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vpos1
)))
1095 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vpos1
, vvalue
, vpos1
))
1097 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, privatedata
.vfromvalue
)))
1098 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, privatedata
.vfromvalue
, vpos1
, vvalue
))
1100 if privatedata
.rangeaxisindex
:
1101 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vpos1
)))
1103 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vpos1
, vvalue
)))
1104 if privatedata
.rangeaxisindex
:
1105 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos1
, vvalue
, vpos2
))
1107 privatedata
.path
.append(graph
.vgeodesic_el(vpos1
, vvalue
, vpos2
, vvalue
))
1108 if self
.fillable
and vpos2cut
:
1109 warnings
.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
1110 if privatedata
.rangeaxisindex
:
1111 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vpos2
, privatedata
.vfromvalue
, vpos2
))
1113 privatedata
.path
.append(graph
.vgeodesic_el(vpos2
, vvalue
, vpos2
, privatedata
.vfromvalue
))
1115 def drawvalue(self
, privatedata
, sharedata
, graph
, vmin
, vmax
, vvalue
):
1116 currentvalid
= vmin
is not None and vmax
is not None and vvalue
is not None
1117 if self
.fillable
and not self
.steps
:
1118 if not currentvalid
:
1121 if vmin
< -self
.epsilon
:
1124 elif vmin
> 1+self
.epsilon
:
1128 if vmax
< -self
.epsilon
:
1131 if vmax
> 1+self
.epsilon
:
1135 if vvalue
< -self
.epsilon
:
1138 if vvalue
> 1+self
.epsilon
:
1142 if abs(vmincut
) + abs(vmaxcut
) + abs(vvaluecut
) + abs(privatedata
.vfromvaluecut
) > 1:
1143 if abs(vmincut
+ vmaxcut
) > 1 or abs(vvaluecut
+privatedata
.vfromvaluecut
) > 1:
1146 warnings
.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
1149 if privatedata
.rangeaxisindex
:
1150 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1151 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1152 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1153 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1155 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1156 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1157 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1158 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1161 if privatedata
.rangeaxisindex
:
1162 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmax
)))
1163 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1164 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1165 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1167 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, vvalue
)))
1168 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1169 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1170 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1171 elif privatedata
.vfromvaluecut
:
1173 if privatedata
.rangeaxisindex
:
1174 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmax
)))
1175 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1176 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1177 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1179 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmax
, privatedata
.vfromvalue
)))
1180 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1181 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1182 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1185 if privatedata
.rangeaxisindex
:
1186 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vvalue
, vmin
)))
1187 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1188 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1189 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1191 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, vvalue
)))
1192 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1193 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1194 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1196 if privatedata
.rangeaxisindex
:
1197 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(privatedata
.vfromvalue
, vmin
)))
1198 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmin
, privatedata
.vfromvalue
, vmax
))
1199 privatedata
.path
.append(graph
.vgeodesic_el(privatedata
.vfromvalue
, vmax
, vvalue
, vmax
))
1200 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmax
, vvalue
, vmin
))
1201 privatedata
.path
.append(graph
.vgeodesic_el(vvalue
, vmin
, privatedata
.vfromvalue
, vmin
))
1202 privatedata
.path
.append(path
.closepath())
1204 privatedata
.path
.append(path
.moveto_pt(*graph
.vpos_pt(vmin
, privatedata
.vfromvalue
)))
1205 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, privatedata
.vfromvalue
, vmax
, privatedata
.vfromvalue
))
1206 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, privatedata
.vfromvalue
, vmax
, vvalue
))
1207 privatedata
.path
.append(graph
.vgeodesic_el(vmax
, vvalue
, vmin
, vvalue
))
1208 privatedata
.path
.append(graph
.vgeodesic_el(vmin
, vvalue
, vmin
, privatedata
.vfromvalue
))
1209 privatedata
.path
.append(path
.closepath())
1212 gap
= abs(vmin
- privatedata
.lastvmax
) > self
.epsilon
1213 except (ArithmeticError, ValueError, TypeError):
1215 if (privatedata
.lastvvalue
is not None and currentvalid
and not gap
and
1216 (self
.steps
or (privatedata
.lastvvalue
-privatedata
.vfromvalue
)*(vvalue
-privatedata
.vfromvalue
) < 0)):
1217 self
.vposline(privatedata
, sharedata
, graph
,
1218 vmin
, privatedata
.lastvvalue
, vvalue
)
1220 if privatedata
.lastvvalue
is not None and currentvalid
:
1221 currentbigger
= abs(privatedata
.lastvvalue
-privatedata
.vfromvalue
) < abs(vvalue
-privatedata
.vfromvalue
)
1222 if privatedata
.lastvvalue
is not None and (not currentvalid
or not currentbigger
or gap
):
1223 self
.vposline(privatedata
, sharedata
, graph
,
1224 privatedata
.lastvmax
, privatedata
.lastvvalue
, privatedata
.vfromvalue
)
1226 self
.vmoveto(privatedata
, sharedata
, graph
,
1228 if currentvalid
and (privatedata
.lastvvalue
is None or currentbigger
or gap
):
1229 self
.vmoveto(privatedata
, sharedata
, graph
,
1230 vmin
, privatedata
.vfromvalue
)
1231 self
.vposline(privatedata
, sharedata
, graph
,
1232 vmin
, privatedata
.vfromvalue
, vvalue
)
1234 self
.vvalueline(privatedata
, sharedata
, graph
,
1236 privatedata
.lastvvalue
= vvalue
1237 privatedata
.lastvmax
= vmax
1239 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1241 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1242 privatedata
.path
= path
.path()
1243 privatedata
.lastvvalue
= privatedata
.lastvmax
= None
1244 privatedata
.vcurrentpoint
= None
1245 privatedata
.count
= 0
1246 if self
.fromvalue
is not None:
1247 valueaxisname
= sharedata
.poscolumnnames
[1-privatedata
.rangeaxisindex
]
1248 privatedata
.vfromvalue
= graph
.axes
[valueaxisname
].convert(self
.fromvalue
)
1249 privatedata
.vfromvaluecut
= 0
1250 if privatedata
.vfromvalue
< 0:
1251 privatedata
.vfromvalue
= 0
1252 privatedata
.vfromvaluecut
= -1
1253 if privatedata
.vfromvalue
> 1:
1254 privatedata
.vfromvalue
= 1
1255 privatedata
.vfromvaluecut
= 1
1256 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1257 graph
.stroke(graph
.axes
[valueaxisname
].vgridpath(privatedata
.vfromvalue
),
1258 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1260 privatedata
.vfromvalue
= 0
1262 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1263 if privatedata
.autohistogram
:
1264 # automatic range handling
1265 privatedata
.count
+= 1
1266 if privatedata
.count
== 2:
1267 if privatedata
.rangeaxisindex
:
1268 privatedata
.vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1269 self
.drawvalue(privatedata
, sharedata
, graph
,
1270 privatedata
.lastvpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1271 privatedata
.lastvpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1272 privatedata
.lastvpos
[0])
1274 privatedata
.vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1275 self
.drawvalue(privatedata
, sharedata
, graph
,
1276 privatedata
.lastvpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1277 privatedata
.lastvpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1278 privatedata
.lastvpos
[1])
1279 elif privatedata
.count
> 2:
1280 if privatedata
.rangeaxisindex
:
1281 vrange
= sharedata
.vpos
[1] - privatedata
.lastvpos
[1]
1283 vrange
= sharedata
.vpos
[0] - privatedata
.lastvpos
[0]
1284 if abs(privatedata
.vrange
- vrange
) > self
.epsilon
:
1285 raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
1286 if privatedata
.count
> 1:
1287 if privatedata
.rangeaxisindex
:
1288 self
.drawvalue(privatedata
, sharedata
, graph
,
1289 sharedata
.vpos
[1] - self
.autohistogrampointpos
*privatedata
.vrange
,
1290 sharedata
.vpos
[1] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1293 self
.drawvalue(privatedata
, sharedata
, graph
,
1294 sharedata
.vpos
[0] - self
.autohistogrampointpos
*privatedata
.vrange
,
1295 sharedata
.vpos
[0] + (1-self
.autohistogrampointpos
)*privatedata
.vrange
,
1297 privatedata
.lastvpos
= sharedata
.vpos
[:]
1299 if privatedata
.rangeaxisindex
:
1300 self
.drawvalue(privatedata
, sharedata
, graph
,
1301 sharedata
.vrange
[1][0], sharedata
.vrange
[1][1], sharedata
.vpos
[0])
1303 self
.drawvalue(privatedata
, sharedata
, graph
,
1304 sharedata
.vrange
[0][0], sharedata
.vrange
[0][1], sharedata
.vpos
[1])
1306 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1307 self
.drawvalue(privatedata
, sharedata
, graph
, None, None, None)
1308 if privatedata
.lineattrs
is not None and len(privatedata
.path
):
1309 graph
.draw(privatedata
.path
, privatedata
.lineattrs
)
1311 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1312 if privatedata
.lineattrs
is not None:
1314 p
= path
.rect_pt(x_pt
, y_pt
, width_pt
, height_pt
)
1316 p
= path
.line_pt(x_pt
, y_pt
+0.5*height_pt
, x_pt
+width_pt
, y_pt
+0.5*height_pt
)
1317 graph
.draw(p
, privatedata
.lineattrs
)
1320 class barpos(_style
):
1322 providesdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1324 defaultfrompathattrs
= []
1326 def __init__(self
, fromvalue
=None, frompathattrs
=[], epsilon
=1e-10):
1327 self
.fromvalue
= fromvalue
1328 self
.frompathattrs
= frompathattrs
1329 self
.epsilon
= epsilon
1331 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1332 sharedata
.barposcolumnnames
= []
1333 sharedata
.barvalueindex
= None
1334 for dimension
, axisnames
in enumerate(graph
.axesnames
):
1336 for axisname
in axisnames
:
1337 if axisname
in columnnames
:
1338 if sharedata
.barvalueindex
is not None:
1339 raise ValueError("multiple values")
1340 sharedata
.barvalueindex
= dimension
1341 sharedata
.barposcolumnnames
.append(axisname
)
1343 if (axisname
+ "name") in columnnames
:
1344 sharedata
.barposcolumnnames
.append(axisname
+ "name")
1347 raise ValueError("multiple names and value")
1349 raise ValueError("value/name missing")
1350 if sharedata
.barvalueindex
is None:
1351 raise ValueError("missing value")
1352 sharedata
.vposmissing
= []
1353 return sharedata
.barposcolumnnames
1355 def addsubvalue(self
, value
, subvalue
):
1360 return value
[0], self
.addsubvalue(value
[1], subvalue
)
1362 return value
, subvalue
1364 return value
, subvalue
1366 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1368 i
= sharedata
.barposcolumnnames
.index(columnname
)
1372 if i
== sharedata
.barvalueindex
:
1373 if self
.fromvalue
is not None:
1374 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis([self
.fromvalue
])
1375 graph
.axes
[sharedata
.barposcolumnnames
[i
]].adjustaxis(data
)
1377 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 0) for x
in data
])
1378 graph
.axes
[sharedata
.barposcolumnnames
[i
][:-4]].adjustaxis([self
.addsubvalue(x
, 1) for x
in data
])
1380 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1381 privatedata
.insertfrompath
= selectindex
== 0
1383 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1384 sharedata
.vpos
= [None]*(len(sharedata
.barposcolumnnames
))
1385 sharedata
.vbarrange
= [[None for i
in xrange(2)] for x
in sharedata
.barposcolumnnames
]
1386 sharedata
.stackedbar
= sharedata
.stackedbardraw
= 0
1388 if self
.fromvalue
is not None:
1389 privatedata
.vfromvalue
= graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].convert(self
.fromvalue
)
1390 if privatedata
.vfromvalue
< 0:
1391 privatedata
.vfromvalue
= 0
1392 if privatedata
.vfromvalue
> 1:
1393 privatedata
.vfromvalue
= 1
1394 if self
.frompathattrs
is not None and privatedata
.insertfrompath
:
1395 graph
.stroke(graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
][0]].vgridpath(privatedata
.vfromvalue
),
1396 self
.defaultfrompathattrs
+ self
.frompathattrs
)
1398 privatedata
.vfromvalue
= 0
1400 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1401 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1402 for i
, barname
in enumerate(sharedata
.barposcolumnnames
):
1403 if i
== sharedata
.barvalueindex
:
1404 sharedata
.vbarrange
[i
][0] = privatedata
.vfromvalue
1405 sharedata
.lastbarvalue
= point
[barname
]
1407 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = graph
.axes
[barname
].convert(sharedata
.lastbarvalue
)
1408 except (ArithmeticError, ValueError, TypeError):
1409 sharedata
.vpos
[i
] = sharedata
.vbarrange
[i
][1] = None
1413 sharedata
.vbarrange
[i
][j
] = graph
.axes
[barname
[:-4]].convert(self
.addsubvalue(point
[barname
], j
))
1414 except (ArithmeticError, ValueError, TypeError):
1415 sharedata
.vbarrange
[i
][j
] = None
1417 sharedata
.vpos
[i
] = 0.5*(sharedata
.vbarrange
[i
][0]+sharedata
.vbarrange
[i
][1])
1418 except (ArithmeticError, ValueError, TypeError):
1419 sharedata
.vpos
[i
] = None
1420 if sharedata
.vpos
[i
] is None:
1421 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1422 elif sharedata
.vpos
[i
] < -self
.epsilon
or sharedata
.vpos
[i
] > 1+self
.epsilon
:
1423 sharedata
.vposvalid
= 0
1425 registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
1428 class stackedbarpos(_style
):
1430 # provides no additional data, but needs some data (and modifies some of them)
1431 needsdata
= ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
1433 def __init__(self
, stackname
, addontop
=0, epsilon
=1e-10):
1434 self
.stackname
= stackname
1435 self
.epsilon
= epsilon
1436 self
.addontop
= addontop
1438 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1439 if self
.stackname
not in columnnames
:
1440 raise ValueError("column '%s' missing" % self
.stackname
)
1441 return [self
.stackname
]
1443 def adjustaxis(self
, privatedata
, sharedata
, graph
, columnname
, data
):
1444 if columnname
== self
.stackname
:
1445 graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].adjustaxis(data
)
1447 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1448 if sharedata
.stackedbardraw
: # do not count the start bar when not gets painted
1449 sharedata
.stackedbar
+= 1
1451 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1452 sharedata
.vbarrange
[sharedata
.barvalueindex
][0] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1]
1455 sharedata
.lastbarvalue
+= point
[self
.stackname
]
1456 except (ArithmeticError, ValueError, TypeError):
1457 sharedata
.lastbarvalue
= None
1459 sharedata
.lastbarvalue
= point
[self
.stackname
]
1461 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = graph
.axes
[sharedata
.barposcolumnnames
[sharedata
.barvalueindex
]].convert(sharedata
.lastbarvalue
)
1462 except (ArithmeticError, ValueError, TypeError):
1463 sharedata
.vpos
[sharedata
.barvalueindex
] = sharedata
.vbarrange
[sharedata
.barvalueindex
][1] = None
1464 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1466 if not sharedata
.vposavailable
or not sharedata
.vposvalid
:
1467 sharedata
.vposavailable
= sharedata
.vposvalid
= 1
1468 for v
in sharedata
.vpos
:
1470 sharedata
.vposavailable
= sharedata
.vposvalid
= 0
1472 if v
< -self
.epsilon
or v
> 1+self
.epsilon
:
1473 sharedata
.vposvalid
= 0
1478 needsdata
= ["vbarrange"]
1480 defaultbarattrs
= [color
.gradient
.Rainbow
, deco
.stroked([color
.grey
.black
])]
1482 def __init__(self
, barattrs
=[], epsilon
=1e-10, gradient
=color
.gradient
.RedBlack
):
1483 self
.barattrs
= barattrs
1484 self
.epsilon
= epsilon
1485 self
.gradient
= gradient
1487 def lighting(self
, angle
, zindex
):
1488 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1490 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1493 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1494 privatedata
.barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1496 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1497 privatedata
.barcanvas
= graph
.insert(canvas
.canvas())
1498 sharedata
.stackedbardraw
= 1
1499 privatedata
.stackedbar
= sharedata
.stackedbar
1500 privatedata
.todraw
= []
1502 def drawpointfill(self
, privatedata
, p
):
1504 privatedata
.barcanvas
.fill(p
, privatedata
.barattrs
)
1506 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1508 for vmin
, vmax
in sharedata
.vbarrange
:
1509 if vmin
is None or vmax
is None:
1510 self
.drawpointfill(privatedata
, None)
1513 vmin
, vmax
= vmax
, vmin
1514 if vmin
> 1 or vmax
< 0:
1515 self
.drawpointfill(privatedata
, None)
1521 vbarrange
.append((vmin
, vmax
))
1522 if len(vbarrange
) == 2:
1523 p
= graph
.vgeodesic(vbarrange
[0][0], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][0])
1524 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][0], vbarrange
[0][1], vbarrange
[1][1]))
1525 p
.append(graph
.vgeodesic_el(vbarrange
[0][1], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][1]))
1526 p
.append(graph
.vgeodesic_el(vbarrange
[0][0], vbarrange
[1][1], vbarrange
[0][0], vbarrange
[1][0]))
1527 p
.append(path
.closepath())
1528 self
.drawpointfill(privatedata
, p
)
1529 elif len(vbarrange
) == 3:
1531 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[1][0] - vbarrange
[1][1]):
1532 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1533 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1534 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1535 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0]))
1536 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1537 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1538 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1539 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1]))
1540 if abs(vbarrange
[0][0] - vbarrange
[0][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1541 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1542 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1],
1543 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1544 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0]))
1545 planes
.append((vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1546 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0],
1547 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1548 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1]))
1549 if abs(vbarrange
[1][0] - vbarrange
[1][1]) > self
.epsilon
and abs(vbarrange
[2][0] - vbarrange
[2][1]):
1550 planes
.append((vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][0],
1551 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][0],
1552 vbarrange
[0][0], vbarrange
[1][1], vbarrange
[2][1],
1553 vbarrange
[0][0], vbarrange
[1][0], vbarrange
[2][1]))
1554 planes
.append((vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][0],
1555 vbarrange
[0][1], vbarrange
[1][0], vbarrange
[2][1],
1556 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][1],
1557 vbarrange
[0][1], vbarrange
[1][1], vbarrange
[2][0]))
1558 v
= [0.5 * (vbarrange
[0][0] + vbarrange
[0][1]),
1559 0.5 * (vbarrange
[1][0] + vbarrange
[1][1]),
1560 0.5 * (vbarrange
[2][0] + vbarrange
[2][1])]
1561 v
[sharedata
.barvalueindex
] = 0.5
1562 zindex
= graph
.vzindex(*v
)
1563 for v11
, v12
, v13
, v21
, v22
, v23
, v31
, v32
, v33
, v41
, v42
, v43
in planes
:
1564 angle
= graph
.vangle(v11
, v12
, v13
, v21
, v22
, v23
, v41
, v42
, v43
)
1566 p
= graph
.vgeodesic(v11
, v12
, v13
, v21
, v22
, v23
)
1567 p
.append(graph
.vgeodesic_el(v21
, v22
, v23
, v31
, v32
, v33
))
1568 p
.append(graph
.vgeodesic_el(v31
, v32
, v33
, v41
, v42
, v43
))
1569 p
.append(graph
.vgeodesic_el(v41
, v42
, v43
, v11
, v12
, v13
))
1570 p
.append(path
.closepath())
1572 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
+ [self
.lighting(angle
, zindex
)]))
1574 privatedata
.todraw
.append((-zindex
, p
, privatedata
.barattrs
))
1576 raise TypeError("bar style restricted to two- and three dimensional graphs")
1578 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1579 privatedata
.todraw
.sort()
1580 for vzindex
, p
, a
in privatedata
.todraw
:
1581 privatedata
.barcanvas
.fill(p
, a
)
1583 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1584 selectindex
= privatedata
.stackedbar
1585 selecttotal
= sharedata
.stackedbar
+ 1
1586 graph
.fill(path
.rect_pt(x_pt
+ width_pt
*selectindex
/float(selecttotal
), y_pt
, width_pt
/float(selecttotal
), height_pt
), privatedata
.barattrs
)
1589 class changebar(bar
):
1591 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1592 if selecttotal
!= 1:
1593 raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
1595 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1596 if len(graph
.axesnames
) != 2:
1597 raise TypeError("changebar style restricted on two-dimensional graphs (at least for the moment)")
1598 bar
.initdrawpoints(self
, privatedata
, sharedata
, graph
)
1599 privatedata
.bars
= []
1601 def drawpointfill(self
, privatedata
, p
):
1602 privatedata
.bars
.append(p
)
1604 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1605 selecttotal
= len(privatedata
.bars
)
1606 for selectindex
, p
in enumerate(privatedata
.bars
):
1608 barattrs
= attr
.selectattrs(self
.defaultbarattrs
+ self
.barattrs
, selectindex
, selecttotal
)
1609 privatedata
.barcanvas
.fill(p
, barattrs
)
1611 def key_pt(self
, privatedata
, sharedata
, graph
, x_pt
, y_pt
, width_pt
, height_pt
):
1612 raise RuntimeError("Style currently doesn't provide a graph key")
1615 class gridpos(_style
):
1617 needsdata
= ["vpos", "vposmissing", "vposavailable", "vposvalid"]
1618 providesdata
= ["values1", "values2", "data12", "data21", "index1", "index2"]
1620 def __init__(self
, index1
=0, index2
=1, epsilon
=1e-10):
1621 self
.index1
= index1
1622 self
.index2
= index2
1623 self
.epsilon
= epsilon
1625 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1626 sharedata
.index1
= self
.index1
1627 sharedata
.index2
= self
.index2
1628 sharedata
.values1
= {}
1629 sharedata
.values2
= {}
1630 sharedata
.data12
= {}
1631 sharedata
.data21
= {}
1633 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1634 if sharedata
.vposavailable
:
1635 sharedata
.value1
= sharedata
.vpos
[self
.index1
]
1636 sharedata
.value2
= sharedata
.vpos
[self
.index2
]
1637 if not sharedata
.values1
.has_key(sharedata
.value1
):
1638 for hasvalue
in sharedata
.values1
.keys():
1639 if hasvalue
- self
.epsilon
<= sharedata
.value1
<= hasvalue
+ self
.epsilon
:
1640 sharedata
.value1
= hasvalue
1643 sharedata
.values1
[sharedata
.value1
] = 1
1644 if not sharedata
.values2
.has_key(sharedata
.value2
):
1645 for hasvalue
in sharedata
.values2
.keys():
1646 if hasvalue
- self
.epsilon
<= sharedata
.value2
<= hasvalue
+ self
.epsilon
:
1647 sharedata
.value2
= hasvalue
1650 sharedata
.values2
[sharedata
.value2
] = 1
1651 data
= sharedata
.vposavailable
, sharedata
.vposvalid
, sharedata
.vpos
[:]
1652 sharedata
.data12
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = data
1653 sharedata
.data21
.setdefault(sharedata
.value2
, {})[sharedata
.value1
] = data
1655 registerdefaultprovider(gridpos(), gridpos
.providesdata
)
1658 class grid(_line
, _style
):
1660 needsdata
= ["values1", "values2", "data12", "data21"]
1662 defaultgridattrs
= [line
.changelinestyle
]
1664 def __init__(self
, gridlines1
=1, gridlines2
=1, gridattrs
=[]):
1665 self
.gridlines1
= gridlines1
1666 self
.gridlines2
= gridlines2
1667 self
.gridattrs
= gridattrs
1669 def selectstyle(self
, privatedata
, sharedata
, graph
, selectindex
, selecttotal
):
1670 if self
.gridattrs
is not None:
1671 privatedata
.gridattrs
= attr
.selectattrs(self
.defaultgridattrs
+ self
.gridattrs
, selectindex
, selecttotal
)
1673 privatedata
.gridattrs
= None
1675 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1676 values1
= sharedata
.values1
.keys()
1678 values2
= sharedata
.values2
.keys()
1681 for value2
in values2
:
1682 data1
= sharedata
.data21
[value2
]
1683 self
.initpointstopath(privatedata
)
1684 for value1
in values1
:
1686 data
= data1
[value1
]
1688 self
.addinvalid(privatedata
)
1690 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1691 p
= self
.donepointstopath(privatedata
)
1693 graph
.stroke(p
, privatedata
.gridattrs
)
1695 for value1
in values1
:
1696 data2
= sharedata
.data12
[value1
]
1697 self
.initpointstopath(privatedata
)
1698 for value2
in values2
:
1700 data
= data2
[value2
]
1702 self
.addinvalid(privatedata
)
1704 self
.addpoint(privatedata
, graph
.vpos_pt
, *data
)
1705 p
= self
.donepointstopath(privatedata
)
1707 graph
.stroke(p
, privatedata
.gridattrs
)
1710 class surface(_style
):
1712 needsdata
= ["values1", "values2", "data12", "data21"]
1714 def __init__(self
, colorname
="color", gradient
=color
.gradient
.Grey
, mincolor
=None, maxcolor
=None,
1715 gridlines1
=0.05, gridlines2
=0.05, gridcolor
=None,
1716 backcolor
=color
.gray
.black
):
1717 self
.colorname
= colorname
1718 self
.gradient
= gradient
1719 self
.mincolor
= mincolor
1720 self
.maxcolor
= maxcolor
1721 self
.gridlines1
= gridlines1
1722 self
.gridlines2
= gridlines2
1723 self
.gridcolor
= gridcolor
1724 self
.backcolor
= backcolor
1726 colorspacestring
= gradient
.getcolor(0).colorspacestring()
1727 if self
.gridcolor
is not None and self
.gridcolor
.colorspacestring() != colorspacestring
:
1728 raise RuntimeError("colorspace mismatch (gradient/grid)")
1729 if self
.backcolor
is not None and self
.backcolor
.colorspacestring() != colorspacestring
:
1730 raise RuntimeError("colorspace mismatch (gradient/back)")
1732 def midvalue(self
, v1
, v2
, v3
, v4
):
1733 return [0.25*sum(values
) for values
in zip(v1
, v2
, v3
, v4
)]
1735 def midcolor(self
, c1
, c2
, c3
, c4
):
1736 return 0.25*(c1
+c2
+c3
+c4
)
1738 def lighting(self
, angle
, zindex
):
1739 if angle
< 0 and self
.backcolor
is not None:
1740 return self
.backcolor
1741 return self
.gradient
.getcolor(0.7-0.4*abs(angle
)+0.1*zindex
)
1743 def columnnames(self
, privatedata
, sharedata
, graph
, columnnames
):
1744 privatedata
.colorize
= self
.colorname
in columnnames
1745 if privatedata
.colorize
:
1746 return [self
.colorname
]
1749 def initdrawpoints(self
, privatedata
, sharedata
, graph
):
1750 privatedata
.colors
= {}
1751 privatedata
.mincolor
= privatedata
.maxcolor
= None
1753 def drawpoint(self
, privatedata
, sharedata
, graph
, point
):
1754 if privatedata
.colorize
:
1756 color
= point
[self
.colorname
] + 0
1760 privatedata
.colors
.setdefault(sharedata
.value1
, {})[sharedata
.value2
] = color
1761 if privatedata
.mincolor
is None or color
< privatedata
.mincolor
:
1762 privatedata
.mincolor
= color
1763 if privatedata
.mincolor
is None or privatedata
.maxcolor
< color
:
1764 privatedata
.maxcolor
= color
1766 def donedrawpoints(self
, privatedata
, sharedata
, graph
):
1767 v1
= [0]*len(graph
.axesnames
)
1768 v2
= [0]*len(graph
.axesnames
)
1769 v3
= [0]*len(graph
.axesnames
)
1770 v4
= [0]*len(graph
.axesnames
)
1771 v1
[sharedata
.index2
] = 0.5
1772 v2
[sharedata
.index1
] = 0.5
1773 v3
[sharedata
.index1
] = 0.5
1774 v3
[sharedata
.index2
] = 1
1775 v4
[sharedata
.index1
] = 1
1776 v4
[sharedata
.index2
] = 0.5
1777 sortElements
= [-graph
.vzindex(*v1
),
1778 -graph
.vzindex(*v2
),
1779 -graph
.vzindex(*v3
),
1780 -graph
.vzindex(*v4
)]
1782 values1
= sharedata
.values1
.keys()
1784 v1
= [0]*len(graph
.axesnames
)
1785 v2
= [0]*len(graph
.axesnames
)
1786 v1
[sharedata
.index1
] = -1
1787 v2
[sharedata
.index1
] = 1
1789 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1792 sortElements
= [sortElements
[3], sortElements
[1], sortElements
[2], sortElements
[0]]
1794 values2
= sharedata
.values2
.keys()
1796 v1
= [0]*len(graph
.axesnames
)
1797 v2
= [0]*len(graph
.axesnames
)
1798 v1
[sharedata
.index2
] = -1
1799 v2
[sharedata
.index2
] = 1
1800 if graph
.vzindex(*v1
) < graph
.vzindex(*v2
):
1803 sortElements
= [sortElements
[0], sortElements
[2], sortElements
[1], sortElements
[3]]
1805 sortElements
= [(zindex
, i
) for i
, zindex
in enumerate(sortElements
)]
1808 mincolor
, maxcolor
= privatedata
.mincolor
, privatedata
.maxcolor
1809 if self
.mincolor
is not None:
1810 mincolor
= self
.mincolor
1811 if self
.maxcolor
is not None:
1812 maxcolor
= self
.maxcolor
1815 for value1a
, value1b
in zip(values1
[:-1], values1
[1:]):
1816 for value2a
, value2b
in zip(values2
[:-1], values2
[1:]):
1818 available1
, valid1
, v1
= sharedata
.data12
[value1a
][value2a
]
1819 available2
, valid2
, v2
= sharedata
.data12
[value1a
][value2b
]
1820 available3
, valid3
, v3
= sharedata
.data12
[value1b
][value2a
]
1821 available4
, valid4
, v4
= sharedata
.data12
[value1b
][value2b
]
1824 if not available1
or not available2
or not available3
or not available4
:
1826 if not valid1
or not valid2
or not valid3
or not valid4
:
1827 warnings
.warn("surface elements partially outside of the graph are (currently) skipped completely")
1829 def shrink(index
, v1
, v2
, by
):
1832 for i
in builtinrange(3):
1834 v1
[i
], v2
[i
] = v1
[i
] + by
*(v2
[i
]-v1
[i
]), v2
[i
] + by
*(v1
[i
]-v2
[i
])
1836 v1f
, v2f
, v3f
, v4f
= v1
, v2
, v3
, v4
1837 if self
.gridcolor
is not None and self
.gridlines1
:
1838 v1
, v2
= shrink(sharedata
.index1
, v1
, v2
, self
.gridlines1
)
1839 v3
, v4
= shrink(sharedata
.index1
, v3
, v4
, self
.gridlines1
)
1840 if self
.gridcolor
is not None and self
.gridlines2
:
1841 v1
, v3
= shrink(sharedata
.index2
, v1
, v3
, self
.gridlines2
)
1842 v2
, v4
= shrink(sharedata
.index2
, v2
, v4
, self
.gridlines2
)
1843 v5
= self
.midvalue(v1
, v2
, v3
, v4
)
1844 x1_pt
, y1_pt
= graph
.vpos_pt(*v1
)
1845 x2_pt
, y2_pt
= graph
.vpos_pt(*v2
)
1846 x3_pt
, y3_pt
= graph
.vpos_pt(*v3
)
1847 x4_pt
, y4_pt
= graph
.vpos_pt(*v4
)
1848 x5_pt
, y5_pt
= graph
.vpos_pt(*v5
)
1849 if privatedata
.colorize
:
1850 def colorfromgradient(c
):
1851 vc
= (c
- mincolor
) / float(maxcolor
- mincolor
)
1853 warnings
.warn("gradiend color range is exceeded due to mincolor setting")
1856 warnings
.warn("gradiend color range is exceeded due to maxcolor setting")
1858 return self
.gradient
.getcolor(vc
)
1859 c1
= privatedata
.colors
[value1a
][value2a
]
1860 c2
= privatedata
.colors
[value1a
][value2b
]
1861 c3
= privatedata
.colors
[value1b
][value2a
]
1862 c4
= privatedata
.colors
[value1b
][value2b
]
1863 c5
= self
.midcolor(c1
, c2
, c3
, c4
)
1864 c1a
= c1b
= colorfromgradient(c1
)
1865 c2a
= c2c
= colorfromgradient(c2
)
1866 c3b
= c3d
= colorfromgradient(c3
)
1867 c4c
= c4d
= colorfromgradient(c4
)
1868 c5a
= c5b
= c5c
= c5d
= colorfromgradient(c5
)
1869 if self
.backcolor
is not None and sign
*graph
.vangle(*(v1
+v2
+v5
)) < 0:
1870 c1a
= c2a
= c5a
= self
.backcolor
1871 if self
.backcolor
is not None and sign
*graph
.vangle(*(v3
+v1
+v5
)) < 0:
1872 c3b
= c1b
= c5b
= self
.backcolor
1873 if self
.backcolor
is not None and sign
*graph
.vangle(*(v2
+v4
+v5
)) < 0:
1874 c2c
= c4c
= c5c
= self
.backcolor
1875 if self
.backcolor
is not None and sign
*graph
.vangle(*(v4
+v3
+v5
)) < 0:
1876 c4d
= c3d
= c5d
= self
.backcolor
1878 zindex
= graph
.vzindex(*v5
)
1879 c1a
= c2a
= c5a
= self
.lighting(sign
*graph
.vangle(*(v1
+v2
+v5
)), zindex
)
1880 c3b
= c1b
= c5b
= self
.lighting(sign
*graph
.vangle(*(v3
+v1
+v5
)), zindex
)
1881 c2c
= c4c
= c5c
= self
.lighting(sign
*graph
.vangle(*(v2
+v4
+v5
)), zindex
)
1882 c4d
= c3d
= c5d
= self
.lighting(sign
*graph
.vangle(*(v4
+v3
+v5
)), zindex
)
1883 for zindex
, i
in sortElements
:
1885 elements
.append(mesh
.element((mesh
.node_pt((x1_pt
, y1_pt
), c1a
),
1886 mesh
.node_pt((x2_pt
, y2_pt
), c2a
),
1887 mesh
.node_pt((x5_pt
, y5_pt
), c5a
))))
1888 if self
.gridcolor
is not None and self
.gridlines2
:
1889 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1890 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1891 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1892 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1893 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
),
1894 mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
))))
1896 elements
.append(mesh
.element((mesh
.node_pt((x3_pt
, y3_pt
), c3b
),
1897 mesh
.node_pt((x1_pt
, y1_pt
), c1b
),
1898 mesh
.node_pt((x5_pt
, y5_pt
), c5b
))))
1899 if self
.gridcolor
is not None and self
.gridlines1
:
1900 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1901 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1902 mesh
.node_pt(graph
.vpos_pt(*v1
), self
.gridcolor
))))
1903 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v1f
), self
.gridcolor
),
1904 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
),
1905 mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
))))
1907 elements
.append(mesh
.element((mesh
.node_pt((x2_pt
, y2_pt
), c2c
),
1908 mesh
.node_pt((x4_pt
, y4_pt
), c4c
),
1909 mesh
.node_pt((x5_pt
, y5_pt
), c5c
))))
1910 if self
.gridcolor
is not None and self
.gridlines1
:
1911 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1912 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1913 mesh
.node_pt(graph
.vpos_pt(*v2
), self
.gridcolor
))))
1914 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v2f
), self
.gridcolor
),
1915 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1916 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1918 elements
.append(mesh
.element((mesh
.node_pt((x4_pt
, y4_pt
), c4d
),
1919 mesh
.node_pt((x3_pt
, y3_pt
), c3d
),
1920 mesh
.node_pt((x5_pt
, y5_pt
), c5d
))))
1921 if self
.gridcolor
is not None and self
.gridlines2
:
1922 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1923 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1924 mesh
.node_pt(graph
.vpos_pt(*v3
), self
.gridcolor
))))
1925 elements
.append(mesh
.element((mesh
.node_pt(graph
.vpos_pt(*v3f
), self
.gridcolor
),
1926 mesh
.node_pt(graph
.vpos_pt(*v4
), self
.gridcolor
),
1927 mesh
.node_pt(graph
.vpos_pt(*v4f
), self
.gridcolor
))))
1928 m
= mesh
.mesh(elements
, check
=0)