# -*- coding: ISO-8859-1 -*-
#
#
# Copyright (C) 2002-2004 Jrg Lehmann <joergl@users.sourceforge.net>
# Copyright (C) 2003-2004 Michael Schindler <m-schindler@users.sourceforge.net>
# Copyright (C) 2002-2006 Andr Wobst <wobsta@users.sourceforge.net>
#
# This file is part of PyX (http://pyx.sourceforge.net/).
#
# PyX is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PyX is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with PyX; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
from __future__ import nested_scopes
import math, warnings
from pyx import attr,deco,style,color,unit,canvas,path,mesh
from pyx import text
builtinrange = range
try:
sum([])
except NameError:
# fallback implementation for Python 2.2 and below
def sum(list):
return reduce(lambda x, y: x+y, list, 0)
try:
enumerate([])
except NameError:
# fallback implementation for Python 2.2. and below
def enumerate(list):
return zip(xrange(len(list)), list)
class _style:
"""Interface class for graph styles
Each graph style must support the methods described in this
class. However, since a graph style might not need to perform
actions on all the various events, it does not need to overwrite
all methods of this base class (e.g. this class is not an abstract
class in any respect).
A style should never store private data by istance variables
(i.e. accessing self), but it should use the sharedata and privatedata
instances instead. A style instance can be used multiple times with
different sharedata and privatedata instances at the very same time.
The sharedata and privatedata instances act as data containers and
sharedata allows for sharing information across several styles.
Every style contains two class variables, which are not to be
modified:
- providesdata is a list of variable names a style offers via
the sharedata instance. This list is used to determine whether
all needs of subsequent styles are fullfilled. Otherwise
getdefaultprovider should return a proper style to be used.
- needsdata is a list of variable names the style needs to access in the
sharedata instance.
"""
providesdata = [] # by default, we provide nothing
needsdata = [] # and do not depend on anything
def columnnames(self, privatedata, sharedata, graph, columnnames):
"""Set column information
This method is used setup the column name information to be
accessible to the style later on. The style should analyse
the list of column names. The method should return a list of
column names which the style will make use of."""
return []
def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
"""Adjust axis range
This method is called in order to adjust the axis range to
the provided data. columnname is the column name (each style
is subsequently called for all column names)."""
pass
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
"""Select stroke/fill attributes
This method is called to allow for the selection of
changable attributes of a style."""
pass
def initdrawpoints(self, privatedata, sharedata, graph):
"""Initialize drawing of data
This method might be used to initialize the drawing of data."""
pass
def drawpoint(self, privatedata, sharedata, graph, point):
"""Draw data
This method is called for each data point. The data is
available in the dictionary point. The dictionary
keys are the column names."""
pass
def donedrawpoints(self, privatedata, sharedata, graph):
"""Finalize drawing of data
This method is called after the last data point was
drawn using the drawpoint method above."""
pass
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
"""Draw graph key"""
# The following two methods are used to register and get a default provider
# for keys. A key is a variable name in sharedata. A provider is a style
# which creates variables in sharedata.
_defaultprovider = {}
def registerdefaultprovider(style, keys):
"""sets a style as a default creator for sharedata variables 'keys'"""
for key in keys:
assert key in style.providesdata, "key not provided by style"
# we might allow for overwriting the defaults, i.e. the following is not checked:
# assert key in _defaultprovider.keys(), "default provider already registered for key"
_defaultprovider[key] = style
def getdefaultprovider(key):
"""returns a style, which acts as a default creator for the
sharedata variable 'key'"""
return _defaultprovider[key]
class pos(_style):
providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
def __init__(self, epsilon=1e-10):
self.epsilon = epsilon
def columnnames(self, privatedata, sharedata, graph, columnnames):
sharedata.poscolumnnames = []
sharedata.vposmissing = []
for count, axisnames in enumerate(graph.axesnames):
for axisname in axisnames:
for columnname in columnnames:
if axisname == columnname:
sharedata.poscolumnnames.append(columnname)
if len(sharedata.poscolumnnames) > count+1:
raise ValueError("multiple axes per graph dimension")
elif len(sharedata.poscolumnnames) < count+1:
sharedata.vposmissing.append(count)
sharedata.poscolumnnames.append(None)
return [columnname for columnname in sharedata.poscolumnnames if columnname is not None]
def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
if columnname in sharedata.poscolumnnames:
graph.axes[columnname].adjustaxis(data)
def initdrawpoints(self, privatedata, sharedata, graph):
sharedata.vpos = [None]*(len(graph.axesnames))
privatedata.pointpostmplist = [[columnname, index, graph.axes[columnname]] # temporarily used by drawpoint only
for index, columnname in enumerate([columnname for columnname in sharedata.poscolumnnames if columnname is not None])]
for missing in sharedata.vposmissing:
for pointpostmp in privatedata.pointpostmplist:
if pointpostmp[1] >= missing:
pointpostmp[1] += 1
def drawpoint(self, privatedata, sharedata, graph, point):
sharedata.vposavailable = 1 # valid position (but might be outside of the graph)
sharedata.vposvalid = 1 # valid position inside the graph
for columnname, index, axis in privatedata.pointpostmplist:
try:
v = axis.convert(point[columnname])
except (ArithmeticError, ValueError, TypeError):
sharedata.vposavailable = sharedata.vposvalid = 0
sharedata.vpos[index] = None
else:
if v < -self.epsilon or v > 1+self.epsilon:
sharedata.vposvalid = 0
sharedata.vpos[index] = v
registerdefaultprovider(pos(), pos.providesdata)
class range(_style):
providesdata = ["vrange", "vrangemissing", "vrangeminmissing", "vrangemaxmissing"]
# internal bit masks
mask_value = 1
mask_min = 2
mask_max = 4
mask_dmin = 8
mask_dmax = 16
mask_d = 32
def __init__(self, usenames={}, epsilon=1e-10):
self.usenames = usenames
self.epsilon = epsilon
def _numberofbits(self, mask):
if not mask:
return 0
if mask & 1:
return self._numberofbits(mask >> 1) + 1
else:
return self._numberofbits(mask >> 1)
def columnnames(self, privatedata, sharedata, graph, columnnames):
usecolumns = []
privatedata.rangeposcolumns = []
sharedata.vrangemissing = []
sharedata.vrangeminmissing = []
sharedata.vrangemaxmissing = []
privatedata.rangeposdeltacolumns = {} # temporarily used by adjustaxis only
for count, axisnames in enumerate(graph.axesnames):
for axisname in axisnames:
try:
usename = self.usenames[axisname]
except KeyError:
usename = axisname
mask = 0
for columnname in columnnames:
addusecolumns = 1
if usename == columnname:
mask += self.mask_value
elif usename + "min" == columnname:
mask += self.mask_min
elif usename + "max" == columnname:
mask += self.mask_max
elif "d" + usename + "min" == columnname:
mask += self.mask_dmin
elif "d" + usename + "max" == columnname:
mask += self.mask_dmax
elif "d" + usename == columnname:
mask += self.mask_d
else:
addusecolumns = 0
if addusecolumns:
usecolumns.append(columnname)
if mask & (self.mask_min | self.mask_max | self.mask_dmin | self.mask_dmax | self.mask_d):
if (self._numberofbits(mask & (self.mask_min | self.mask_dmin | self.mask_d)) > 1 or
self._numberofbits(mask & (self.mask_max | self.mask_dmax | self.mask_d)) > 1):
raise ValueError("multiple range definition")
if mask & (self.mask_dmin | self.mask_dmax | self.mask_d):
if not (mask & self.mask_value):
raise ValueError("missing value for delta")
privatedata.rangeposdeltacolumns[axisname] = {}
privatedata.rangeposcolumns.append((axisname, usename, mask))
elif mask == self.mask_value:
usecolumns = usecolumns[:-1]
if len(privatedata.rangeposcolumns) + len(sharedata.vrangemissing) > count+1:
raise ValueError("multiple axes per graph dimension")
elif len(privatedata.rangeposcolumns) + len(sharedata.vrangemissing) < count+1:
sharedata.vrangemissing.append(count)
sharedata.vrangeminmissing.append(count)
sharedata.vrangemaxmissing.append(count)
else:
if not (privatedata.rangeposcolumns[-1][2] & (self.mask_min | self.mask_dmin | self.mask_d)):
sharedata.vrangeminmissing.append(count)
if not (privatedata.rangeposcolumns[-1][2] & (self.mask_max | self.mask_dmax | self.mask_d)):
sharedata.vrangemaxmissing.append(count)
return usecolumns
def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
if columnname in [c + "min" for a, c, m in privatedata.rangeposcolumns if m & self.mask_min]:
graph.axes[columnname[:-3]].adjustaxis(data)
if columnname in [c + "max" for a, c, m in privatedata.rangeposcolumns if m & self.mask_max]:
graph.axes[columnname[:-3]].adjustaxis(data)
# delta handling: fill rangeposdeltacolumns
for axisname, usename, mask in privatedata.rangeposcolumns:
if columnname == usename and mask & (self.mask_dmin | self.mask_dmax | self.mask_d):
privatedata.rangeposdeltacolumns[axisname][self.mask_value] = data
if columnname == "d" + usename + "min" and mask & self.mask_dmin:
privatedata.rangeposdeltacolumns[axisname][self.mask_dmin] = data
if columnname == "d" + usename + "max" and mask & self.mask_dmax:
privatedata.rangeposdeltacolumns[axisname][self.mask_dmax] = data
if columnname == "d" + usename and mask & self.mask_d:
privatedata.rangeposdeltacolumns[axisname][self.mask_d] = data
# delta handling: process rangeposdeltacolumns
for a, d in privatedata.rangeposdeltacolumns.items():
if d.has_key(self.mask_value):
for k in d.keys():
if k != self.mask_value:
if k & (self.mask_dmin | self.mask_d):
mindata = []
for value, delta in zip(d[self.mask_value], d[k]):
try:
mindata.append(value-delta)
except:
pass
graph.axes[a].adjustaxis(mindata)
if k & (self.mask_dmax | self.mask_d):
maxdata = []
for value, delta in zip(d[self.mask_value], d[k]):
try:
maxdata.append(value+delta)
except:
pass
graph.axes[a].adjustaxis(maxdata)
del d[k]
def initdrawpoints(self, privatedata, sharedata, graph):
sharedata.vrange = [[None for x in xrange(2)] for y in privatedata.rangeposcolumns + sharedata.vrangemissing]
privatedata.rangepostmplist = [[usename, mask, index, graph.axes[axisname]] # temporarily used by drawpoint only
for index, (axisname, usename, mask) in enumerate(privatedata.rangeposcolumns)]
for missing in sharedata.vrangemissing:
for rangepostmp in privatedata.rangepostmplist:
if rangepostmp[2] >= missing:
rangepostmp[2] += 1
def drawpoint(self, privatedata, sharedata, graph, point):
for usename, mask, index, axis in privatedata.rangepostmplist:
try:
if mask & self.mask_min:
sharedata.vrange[index][0] = axis.convert(point[usename + "min"])
if mask & self.mask_dmin:
sharedata.vrange[index][0] = axis.convert(point[usename] - point["d" + usename + "min"])
if mask & self.mask_d:
sharedata.vrange[index][0] = axis.convert(point[usename] - point["d" + usename])
except (ArithmeticError, ValueError, TypeError):
sharedata.vrange[index][0] = None
try:
if mask & self.mask_max:
sharedata.vrange[index][1] = axis.convert(point[usename + "max"])
if mask & self.mask_dmax:
sharedata.vrange[index][1] = axis.convert(point[usename] + point["d" + usename + "max"])
if mask & self.mask_d:
sharedata.vrange[index][1] = axis.convert(point[usename] + point["d" + usename])
except (ArithmeticError, ValueError, TypeError):
sharedata.vrange[index][1] = None
# some range checks for data consistency
if (sharedata.vrange[index][0] is not None and sharedata.vrange[index][1] is not None and
sharedata.vrange[index][0] > sharedata.vrange[index][1] + self.epsilon):
raise ValueError("inverse range")
# disabled due to missing vpos access:
# if (sharedata.vrange[index][0] is not None and sharedata.vpos[index] is not None and
# sharedata.vrange[index][0] > sharedata.vpos[index] + self.epsilon):
# raise ValueError("negative minimum errorbar")
# if (sharedata.vrange[index][1] is not None and sharedata.vpos[index] is not None and
# sharedata.vrange[index][1] < sharedata.vpos[index] - self.epsilon):
# raise ValueError("negative maximum errorbar")
registerdefaultprovider(range(), range.providesdata)
def _crosssymbol(c, x_pt, y_pt, size_pt, attrs):
c.draw(path.path(path.moveto_pt(x_pt-0.5*size_pt, y_pt-0.5*size_pt),
path.lineto_pt(x_pt+0.5*size_pt, y_pt+0.5*size_pt),
path.moveto_pt(x_pt-0.5*size_pt, y_pt+0.5*size_pt),
path.lineto_pt(x_pt+0.5*size_pt, y_pt-0.5*size_pt)), attrs)
def _plussymbol(c, x_pt, y_pt, size_pt, attrs):
c.draw(path.path(path.moveto_pt(x_pt-0.707106781*size_pt, y_pt),
path.lineto_pt(x_pt+0.707106781*size_pt, y_pt),
path.moveto_pt(x_pt, y_pt-0.707106781*size_pt),
path.lineto_pt(x_pt, y_pt+0.707106781*size_pt)), attrs)
def _squaresymbol(c, x_pt, y_pt, size_pt, attrs):
c.draw(path.path(path.moveto_pt(x_pt-0.5*size_pt, y_pt-0.5*size_pt),
path.lineto_pt(x_pt+0.5*size_pt, y_pt-0.5*size_pt),
path.lineto_pt(x_pt+0.5*size_pt, y_pt+0.5*size_pt),
path.lineto_pt(x_pt-0.5*size_pt, y_pt+0.5*size_pt),
path.closepath()), attrs)
def _trianglesymbol(c, x_pt, y_pt, size_pt, attrs):
c.draw(path.path(path.moveto_pt(x_pt-0.759835685*size_pt, y_pt-0.438691337*size_pt),
path.lineto_pt(x_pt+0.759835685*size_pt, y_pt-0.438691337*size_pt),
path.lineto_pt(x_pt, y_pt+0.877382675*size_pt),
path.closepath()), attrs)
def _circlesymbol(c, x_pt, y_pt, size_pt, attrs):
c.draw(path.path(path.arc_pt(x_pt, y_pt, 0.564189583*size_pt, 0, 360),
path.closepath()), attrs)
def _diamondsymbol(c, x_pt, y_pt, size_pt, attrs):
c.draw(path.path(path.moveto_pt(x_pt-0.537284965*size_pt, y_pt),
path.lineto_pt(x_pt, y_pt-0.930604859*size_pt),
path.lineto_pt(x_pt+0.537284965*size_pt, y_pt),
path.lineto_pt(x_pt, y_pt+0.930604859*size_pt),
path.closepath()), attrs)
class _styleneedingpointpos(_style):
needsdata = ["vposmissing"]
def columnnames(self, privatedata, sharedata, graph, columnnames):
if len(sharedata.vposmissing):
raise ValueError("incomplete position information")
return []
class symbol(_styleneedingpointpos):
needsdata = ["vpos", "vposmissing", "vposvalid"]
# "inject" the predefinied symbols into the class:
#
# Note, that statements like cross = _crosssymbol are
# invalid, since the would lead to unbound methods, but
# a single entry changeable list does the trick.
#
# Once we require Python 2.2+ we should use staticmethods
# to implement the default symbols inplace.
cross = attr.changelist([_crosssymbol])
plus = attr.changelist([_plussymbol])
square = attr.changelist([_squaresymbol])
triangle = attr.changelist([_trianglesymbol])
circle = attr.changelist([_circlesymbol])
diamond = attr.changelist([_diamondsymbol])
changecross = attr.changelist([_crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol])
changeplus = attr.changelist([_plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol])
changesquare = attr.changelist([_squaresymbol, _trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol])
changetriangle = attr.changelist([_trianglesymbol, _circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol])
changecircle = attr.changelist([_circlesymbol, _diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol])
changediamond = attr.changelist([_diamondsymbol, _crosssymbol, _plussymbol, _squaresymbol, _trianglesymbol, _circlesymbol])
changesquaretwice = attr.changelist([_squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol])
changetriangletwice = attr.changelist([_trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol])
changecircletwice = attr.changelist([_circlesymbol, _circlesymbol, _diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol])
changediamondtwice = attr.changelist([_diamondsymbol, _diamondsymbol, _squaresymbol, _squaresymbol, _trianglesymbol, _trianglesymbol, _circlesymbol, _circlesymbol])
changestrokedfilled = attr.changelist([deco.stroked, deco.filled])
changefilledstroked = attr.changelist([deco.filled, deco.stroked])
defaultsymbolattrs = [deco.stroked]
def __init__(self, symbol=changecross, size=0.2*unit.v_cm, symbolattrs=[]):
self.symbol = symbol
self.size = size
self.symbolattrs = symbolattrs
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
privatedata.symbol = attr.selectattr(self.symbol, selectindex, selecttotal)
privatedata.size_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal))
if self.symbolattrs is not None:
privatedata.symbolattrs = attr.selectattrs(self.defaultsymbolattrs + self.symbolattrs, selectindex, selecttotal)
else:
privatedata.symbolattrs = None
def initdrawpoints(self, privatedata, sharedata, graph):
privatedata.symbolcanvas = canvas.canvas()
def drawpoint(self, privatedata, sharedata, graph, point):
if sharedata.vposvalid and privatedata.symbolattrs is not None:
x_pt, y_pt = graph.vpos_pt(*sharedata.vpos)
privatedata.symbol(privatedata.symbolcanvas, x_pt, y_pt, privatedata.size_pt, privatedata.symbolattrs)
def donedrawpoints(self, privatedata, sharedata, graph):
graph.insert(privatedata.symbolcanvas)
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
if privatedata.symbolattrs is not None:
privatedata.symbol(graph, x_pt+0.5*width_pt, y_pt+0.5*height_pt, privatedata.size_pt, privatedata.symbolattrs)
class _line(_styleneedingpointpos):
# this style is not a complete style, but it provides the basic functionality to
# create a line, which is cut at the graph boundaries (or at otherwise invalid points)
def initpointstopath(self, privatedata):
privatedata.path = path.path()
privatedata.linebasepoints = []
privatedata.lastvpos = None
def addpointstopath(self, privatedata):
# add baselinepoints to privatedata.path
if len(privatedata.linebasepoints) > 1:
privatedata.path.append(path.moveto_pt(*privatedata.linebasepoints[0]))
if len(privatedata.linebasepoints) > 2:
privatedata.path.append(path.multilineto_pt(privatedata.linebasepoints[1:]))
else:
privatedata.path.append(path.lineto_pt(*privatedata.linebasepoints[1]))
privatedata.linebasepoints = []
def addpoint(self, privatedata, graphvpos_pt, vposavailable, vposvalid, vpos):
# append linebasepoints
if vposavailable:
if len(privatedata.linebasepoints):
# the last point was inside the graph
if vposvalid: # shortcut for the common case
privatedata.linebasepoints.append(graphvpos_pt(*vpos))
else:
# cut end
cut = 1
for vstart, vend in zip(privatedata.lastvpos, vpos):
newcut = None
if vend > 1:
# 1 = vstart + (vend - vstart) * cut
try:
newcut = (1 - vstart)/(vend - vstart)
except (ArithmeticError, TypeError):
break
if vend < 0:
# 0 = vstart + (vend - vstart) * cut
try:
newcut = - vstart/(vend - vstart)
except (ArithmeticError, TypeError):
break
if newcut is not None and newcut < cut:
cut = newcut
else:
cutvpos = []
for vstart, vend in zip(privatedata.lastvpos, vpos):
cutvpos.append(vstart + (vend - vstart) * cut)
privatedata.linebasepoints.append(graphvpos_pt(*cutvpos))
self.addpointstopath(privatedata)
else:
# the last point was outside the graph
if privatedata.lastvpos is not None:
if vposvalid:
# cut beginning
cut = 0
for vstart, vend in zip(privatedata.lastvpos, vpos):
newcut = None
if vstart > 1:
# 1 = vstart + (vend - vstart) * cut
try:
newcut = (1 - vstart)/(vend - vstart)
except (ArithmeticError, TypeError):
break
if vstart < 0:
# 0 = vstart + (vend - vstart) * cut
try:
newcut = - vstart/(vend - vstart)
except (ArithmeticError, TypeError):
break
if newcut is not None and newcut > cut:
cut = newcut
else:
cutvpos = []
for vstart, vend in zip(privatedata.lastvpos, vpos):
cutvpos.append(vstart + (vend - vstart) * cut)
privatedata.linebasepoints.append(graphvpos_pt(*cutvpos))
privatedata.linebasepoints.append(graphvpos_pt(*vpos))
else:
# sometimes cut beginning and end
cutfrom = 0
cutto = 1
for vstart, vend in zip(privatedata.lastvpos, vpos):
newcutfrom = None
if vstart > 1:
if vend > 1:
break
# 1 = vstart + (vend - vstart) * cutfrom
try:
newcutfrom = (1 - vstart)/(vend - vstart)
except (ArithmeticError, TypeError):
break
if vstart < 0:
if vend < 0:
break
# 0 = vstart + (vend - vstart) * cutfrom
try:
newcutfrom = - vstart/(vend - vstart)
except (ArithmeticError, TypeError):
break
if newcutfrom is not None and newcutfrom > cutfrom:
cutfrom = newcutfrom
newcutto = None
if vend > 1:
# 1 = vstart + (vend - vstart) * cutto
try:
newcutto = (1 - vstart)/(vend - vstart)
except (ArithmeticError, TypeError):
break
if vend < 0:
# 0 = vstart + (vend - vstart) * cutto
try:
newcutto = - vstart/(vend - vstart)
except (ArithmeticError, TypeError):
break
if newcutto is not None and newcutto < cutto:
cutto = newcutto
else:
if cutfrom < cutto:
cutfromvpos = []
cuttovpos = []
for vstart, vend in zip(privatedata.lastvpos, vpos):
cutfromvpos.append(vstart + (vend - vstart) * cutfrom)
cuttovpos.append(vstart + (vend - vstart) * cutto)
privatedata.linebasepoints.append(graphvpos_pt(*cutfromvpos))
privatedata.linebasepoints.append(graphvpos_pt(*cuttovpos))
self.addpointstopath(privatedata)
privatedata.lastvpos = vpos[:]
else:
if len(privatedata.linebasepoints) > 1:
self.addpointstopath(privatedata)
privatedata.lastvpos = None
def addinvalid(self, privatedata):
if len(privatedata.linebasepoints) > 1:
self.addpointstopath(privatedata)
privatedata.lastvpos = None
def donepointstopath(self, privatedata):
if len(privatedata.linebasepoints) > 1:
self.addpointstopath(privatedata)
return privatedata.path
class line(_line):
needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid"]
changelinestyle = attr.changelist([style.linestyle.solid,
style.linestyle.dashed,
style.linestyle.dotted,
style.linestyle.dashdotted])
defaultlineattrs = [changelinestyle]
def __init__(self, lineattrs=[]):
self.lineattrs = lineattrs
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
if self.lineattrs is not None:
privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
else:
privatedata.lineattrs = None
def initdrawpoints(self, privatedata, sharedata, graph):
self.initpointstopath(privatedata)
def drawpoint(self, privatedata, sharedata, graph, point):
self.addpoint(privatedata, graph.vpos_pt, sharedata.vposavailable, sharedata.vposvalid, sharedata.vpos)
def donedrawpoints(self, privatedata, sharedata, graph):
path = self.donepointstopath(privatedata)
if privatedata.lineattrs is not None and len(path):
graph.stroke(path, privatedata.lineattrs)
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
if privatedata.lineattrs is not None:
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)
class impulses(_styleneedingpointpos):
needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "poscolumnnames"]
defaultlineattrs = [line.changelinestyle]
defaultfrompathattrs = []
def __init__(self, lineattrs=[], fromvalue=0, frompathattrs=[], valueaxisindex=1):
self.lineattrs = lineattrs
self.fromvalue = fromvalue
self.frompathattrs = frompathattrs
self.valueaxisindex = valueaxisindex
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
privatedata.insertfrompath = selectindex == 0
if self.lineattrs is not None:
privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
else:
privatedata.lineattrs = None
def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
if self.fromvalue is not None:
try:
i = sharedata.poscolumnnames.index(columnname)
except ValueError:
pass
else:
if i == self.valueaxisindex:
graph.axes[sharedata.poscolumnnames[i]].adjustaxis([self.fromvalue])
def initdrawpoints(self, privatedata, sharedata, graph):
privatedata.impulsescanvas = canvas.canvas()
if self.fromvalue is not None:
valueaxisname = sharedata.poscolumnnames[self.valueaxisindex]
privatedata.vfromvalue = graph.axes[valueaxisname].convert(self.fromvalue)
privatedata.vfromvaluecut = 0
if privatedata.vfromvalue < 0:
privatedata.vfromvalue = 0
if privatedata.vfromvalue > 1:
privatedata.vfromvalue = 1
if self.frompathattrs is not None and privatedata.insertfrompath:
graph.stroke(graph.axes[valueaxisname].vgridpath(privatedata.vfromvalue),
self.defaultfrompathattrs + self.frompathattrs)
else:
privatedata.vfromvalue = 0
def drawpoint(self, privatedata, sharedata, graph, point):
if sharedata.vposvalid and privatedata.lineattrs is not None:
vpos = sharedata.vpos[:]
vpos[self.valueaxisindex] = privatedata.vfromvalue
privatedata.impulsescanvas.stroke(graph.vgeodesic(*(vpos + sharedata.vpos)), privatedata.lineattrs)
def donedrawpoints(self, privatedata, sharedata, graph):
graph.insert(privatedata.impulsescanvas)
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
if privatedata.lineattrs is not None:
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)
class errorbar(_style):
needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangeminmissing", "vrangemaxmissing"]
defaulterrorbarattrs = []
def __init__(self, size=0.1*unit.v_cm,
errorbarattrs=[],
epsilon=1e-10):
self.size = size
self.errorbarattrs = errorbarattrs
self.epsilon = epsilon
def columnnames(self, privatedata, sharedata, graph, columnnames):
for i in sharedata.vposmissing:
if i in sharedata.vrangeminmissing and i in sharedata.vrangemaxmissing:
raise ValueError("position and range for a graph dimension missing")
return []
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
privatedata.errorsize_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal))
privatedata.errorbarattrs = attr.selectattrs(self.defaulterrorbarattrs + self.errorbarattrs, selectindex, selecttotal)
def initdrawpoints(self, privatedata, sharedata, graph):
if privatedata.errorbarattrs is not None:
privatedata.errorbarcanvas = canvas.canvas(privatedata.errorbarattrs)
privatedata.dimensionlist = list(xrange(len(sharedata.vpos)))
def drawpoint(self, privatedata, sharedata, graph, point):
if privatedata.errorbarattrs is not None:
for i in privatedata.dimensionlist:
for j in privatedata.dimensionlist:
if (i != j and
(sharedata.vpos[j] is None or
sharedata.vpos[j] < -self.epsilon or
sharedata.vpos[j] > 1+self.epsilon)):
break
else:
if ((sharedata.vrange[i][0] is None and sharedata.vpos[i] is None) or
(sharedata.vrange[i][1] is None and sharedata.vpos[i] is None) or
(sharedata.vrange[i][0] is None and sharedata.vrange[i][1] is None)):
continue
vminpos = sharedata.vpos[:]
if sharedata.vrange[i][0] is not None:
vminpos[i] = sharedata.vrange[i][0]
mincap = 1
else:
mincap = 0
if vminpos[i] > 1+self.epsilon:
continue
if vminpos[i] < -self.epsilon:
vminpos[i] = 0
mincap = 0
vmaxpos = sharedata.vpos[:]
if sharedata.vrange[i][1] is not None:
vmaxpos[i] = sharedata.vrange[i][1]
maxcap = 1
else:
maxcap = 0
if vmaxpos[i] < -self.epsilon:
continue
if vmaxpos[i] > 1+self.epsilon:
vmaxpos[i] = 1
maxcap = 0
privatedata.errorbarcanvas.stroke(graph.vgeodesic(*(vminpos + vmaxpos)))
for j in privatedata.dimensionlist:
if i != j:
if mincap:
privatedata.errorbarcanvas.stroke(graph.vcap_pt(j, privatedata.errorsize_pt, *vminpos))
if maxcap:
privatedata.errorbarcanvas.stroke(graph.vcap_pt(j, privatedata.errorsize_pt, *vmaxpos))
def donedrawpoints(self, privatedata, sharedata, graph):
if privatedata.errorbarattrs is not None:
graph.insert(privatedata.errorbarcanvas)
class text(_styleneedingpointpos):
needsdata = ["vpos", "vposmissing", "vposvalid"]
defaulttextattrs = [textmodule.halign.center, textmodule.vshift.mathaxis]
def __init__(self, textname="text", dxname=None, dyname=None,
dxunit=0.3*unit.v_cm, dyunit=0.3*unit.v_cm,
textdx=0*unit.v_cm, textdy=0.3*unit.v_cm, textattrs=[]):
self.textname = textname
self.dxname = dxname
self.dyname = dyname
self.dxunit = dxunit
self.dyunit = dyunit
self.textdx = textdx
self.textdy = textdy
self.textattrs = textattrs
def columnnames(self, privatedata, sharedata, graph, columnnames):
if self.textname not in columnnames:
raise ValueError("column '%s' missing" % self.textname)
names = [self.textname]
if self.dxname is not None:
if self.dxname not in columnnames:
raise ValueError("column '%s' missing" % self.dxname)
names.append(self.dxname)
if self.dyname is not None:
if self.dyname not in columnnames:
raise ValueError("column '%s' missing" % self.dyname)
names.append(self.dyname)
return names + _styleneedingpointpos.columnnames(self, privatedata, sharedata, graph, columnnames)
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
if self.textattrs is not None:
privatedata.textattrs = attr.selectattrs(self.defaulttextattrs + self.textattrs, selectindex, selecttotal)
else:
privatedata.textattrs = None
def initdrawpoints(self, privatedata, sharedata, grap):
if self.dxname is None:
privatedata.textdx_pt = unit.topt(self.textdx)
else:
privatedata.dxunit_pt = unit.topt(self.dxunit)
if self.dyname is None:
privatedata.textdy_pt = unit.topt(self.textdy)
else:
privatedata.dyunit_pt = unit.topt(self.dyunit)
def drawpoint(self, privatedata, sharedata, graph, point):
if privatedata.textattrs is not None and sharedata.vposvalid:
x_pt, y_pt = graph.vpos_pt(*sharedata.vpos)
try:
text = str(point[self.textname])
except:
pass
else:
if self.dxname is None:
dx_pt = privatedata.textdx_pt
else:
dx_pt = float(point[self.dxname]) * privatedata.dxunit_pt
if self.dyname is None:
dy_pt = privatedata.textdy_pt
else:
dy_pt = float(point[self.dyname]) * privatedata.dyunit_pt
graph.text_pt(x_pt + dx_pt, y_pt + dy_pt, text, privatedata.textattrs)
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
raise RuntimeError("Style currently doesn't provide a graph key")
class arrow(_styleneedingpointpos):
needsdata = ["vpos", "vposmissing", "vposvalid"]
defaultlineattrs = []
defaultarrowattrs = []
def __init__(self, linelength=0.25*unit.v_cm, arrowsize=0.15*unit.v_cm, lineattrs=[], arrowattrs=[], arrowpos=0.5, epsilon=1e-5):
self.linelength = linelength
self.arrowsize = arrowsize
self.lineattrs = lineattrs
self.arrowattrs = arrowattrs
self.arrowpos = arrowpos
self.epsilon = epsilon
def columnnames(self, privatedata, sharedata, graph, columnnames):
if len(graph.axesnames) != 2:
raise ValueError("arrow style restricted on two-dimensional graphs")
if "size" not in columnnames:
raise ValueError("size missing")
if "angle" not in columnnames:
raise ValueError("angle missing")
return ["size", "angle"] + _styleneedingpointpos.columnnames(self, privatedata, sharedata, graph, columnnames)
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
if self.lineattrs is not None:
privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
else:
privatedata.lineattrs = None
if self.arrowattrs is not None:
privatedata.arrowattrs = attr.selectattrs(self.defaultarrowattrs + self.arrowattrs, selectindex, selecttotal)
else:
privatedata.arrowattrs = None
def initdrawpoints(self, privatedata, sharedata, graph):
privatedata.arrowcanvas = canvas.canvas()
def drawpoint(self, privatedata, sharedata, graph, point):
if privatedata.lineattrs is not None and privatedata.arrowattrs is not None and sharedata.vposvalid:
linelength_pt = unit.topt(self.linelength)
x_pt, y_pt = graph.vpos_pt(*sharedata.vpos)
try:
angle = point["angle"] + 0.0
size = point["size"] + 0.0
except:
pass
else:
if point["size"] > self.epsilon:
dx = math.cos(angle*math.pi/180)
dy = math.sin(angle*math.pi/180)
x1 = x_pt-self.arrowpos*dx*linelength_pt*size
y1 = y_pt-self.arrowpos*dy*linelength_pt*size
x2 = x_pt+(1-self.arrowpos)*dx*linelength_pt*size
y2 = y_pt+(1-self.arrowpos)*dy*linelength_pt*size
privatedata.arrowcanvas.stroke(path.line_pt(x1, y1, x2, y2), privatedata.lineattrs +
[deco.earrow(privatedata.arrowattrs, size=self.arrowsize*size)])
def donedrawpoints(self, privatedata, sharedata, graph):
graph.insert(privatedata.arrowcanvas)
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
raise RuntimeError("Style currently doesn't provide a graph key")
class rect(_style):
needsdata = ["vrange", "vrangeminmissing", "vrangemaxmissing"]
def __init__(self, gradient=color.gradient.Grey):
self.gradient = gradient
def columnnames(self, privatedata, sharedata, graph, columnnames):
if len(graph.axesnames) != 2:
raise TypeError("arrow style restricted on two-dimensional graphs")
if "color" not in columnnames:
raise ValueError("color missing")
if len(sharedata.vrangeminmissing) + len(sharedata.vrangemaxmissing):
raise ValueError("incomplete range")
return ["color"]
def initdrawpoints(self, privatedata, sharedata, graph):
privatedata.rectcanvas = graph.insert(canvas.canvas())
def drawpoint(self, privatedata, sharedata, graph, point):
xvmin = sharedata.vrange[0][0]
xvmax = sharedata.vrange[0][1]
yvmin = sharedata.vrange[1][0]
yvmax = sharedata.vrange[1][1]
if (xvmin is not None and xvmin < 1 and
xvmax is not None and xvmax > 0 and
yvmin is not None and yvmin < 1 and
yvmax is not None and yvmax > 0):
if xvmin < 0:
xvmin = 0
elif xvmax > 1:
xvmax = 1
if yvmin < 0:
yvmin = 0
elif yvmax > 1:
yvmax = 1
p = graph.vgeodesic(xvmin, yvmin, xvmax, yvmin)
p.append(graph.vgeodesic_el(xvmax, yvmin, xvmax, yvmax))
p.append(graph.vgeodesic_el(xvmax, yvmax, xvmin, yvmax))
p.append(graph.vgeodesic_el(xvmin, yvmax, xvmin, yvmin))
p.append(path.closepath())
privatedata.rectcanvas.fill(p, [self.gradient.getcolor(point["color"])])
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
raise RuntimeError("Style currently doesn't provide a graph key")
class histogram(_style):
needsdata = ["vpos", "vposmissing", "vrange", "vrangeminmissing", "vrangemaxmissing"]
defaultlineattrs = [deco.stroked]
defaultfrompathattrs = []
def __init__(self, lineattrs=[], steps=0, fromvalue=0, frompathattrs=[], fillable=0, rectkey=0,
autohistogramaxisindex=0, autohistogrampointpos=0.5, epsilon=1e-10):
self.lineattrs = lineattrs
self.steps = steps
self.fromvalue = fromvalue
self.frompathattrs = frompathattrs
self.fillable = fillable # TODO: fillable paths might not properly be closed by straight lines on curved graph geometries
self.rectkey = rectkey
self.autohistogramaxisindex = autohistogramaxisindex
self.autohistogrampointpos = autohistogrampointpos
self.epsilon = epsilon
def columnnames(self, privatedata, sharedata, graph, columnnames):
if len(graph.axesnames) != 2:
raise TypeError("histogram style restricted on two-dimensional graphs")
privatedata.rangeaxisindex = None
for i in builtinrange(len(graph.axesnames)):
if i in sharedata.vrangeminmissing or i in sharedata.vrangemaxmissing:
if i in sharedata.vposmissing:
raise ValueError("pos and range missing")
else:
if privatedata.rangeaxisindex is not None:
raise ValueError("multiple ranges")
privatedata.rangeaxisindex = i
if privatedata.rangeaxisindex is None:
privatedata.rangeaxisindex = self.autohistogramaxisindex
privatedata.autohistogram = 1
else:
privatedata.autohistogram = 0
return []
def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
if privatedata.autohistogram and columnname == sharedata.poscolumnnames[privatedata.rangeaxisindex]:
if len(data) == 1:
raise ValueError("several data points needed for automatic histogram width calculation")
if data:
delta = data[1] - data[0]
min = data[0] - self.autohistogrampointpos * delta
max = data[-1] + (1-self.autohistogrampointpos) * delta
graph.axes[columnname].adjustaxis([min, max])
elif self.fromvalue is not None and columnname == sharedata.poscolumnnames[1-privatedata.rangeaxisindex]:
graph.axes[columnname].adjustaxis([self.fromvalue])
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
privatedata.insertfrompath = selectindex == 0
if self.lineattrs is not None:
privatedata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal)
else:
privatedata.lineattrs = None
def vmoveto(self, privatedata, sharedata, graph, vpos, vvalue):
if -self.epsilon < vpos < 1+self.epsilon and -self.epsilon < vvalue < 1+self.epsilon:
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vpos)))
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos, vvalue)))
def vposline(self, privatedata, sharedata, graph, vpos, vvalue1, vvalue2):
if -self.epsilon < vpos < 1+self.epsilon:
vvalue1cut = 0
if vvalue1 < 0:
vvalue1 = 0
vvalue1cut = -1
elif vvalue1 > 1:
vvalue1 = 1
vvalue1cut = 1
vvalue2cut = 0
if vvalue2 < 0:
vvalue2 = 0
vvalue2cut = -1
elif vvalue2 > 1:
vvalue2 = 1
vvalue2cut = 1
if abs(vvalue1cut + vvalue2cut) <= 1:
if vvalue1cut and not self.fillable:
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue1, vpos)))
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos, vvalue1)))
if privatedata.rangeaxisindex:
privatedata.path.append(graph.vgeodesic_el(vvalue1, vpos, vvalue2, vpos))
else:
privatedata.path.append(graph.vgeodesic_el(vpos, vvalue1, vpos, vvalue2))
def vvalueline(self, privatedata, sharedata, graph, vvalue, vpos1, vpos2):
if self.fillable:
if vvalue < -self.epsilon:
vvalue = 0
warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
if vvalue > 1+self.epsilon:
vvalue = 1
warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
if self.fillable or (-self.epsilon < vvalue < 1+self.epsilon):
vpos1cut = 0
if vpos1 < 0:
vpos1 = 0
vpos1cut = -1
elif vpos1 > 1:
vpos1 = 1
vpos1cut = 1
vpos2cut = 0
if vpos2 < 0:
vpos2 = 0
vpos2cut = -1
elif vpos2 > 1:
vpos2 = 1
vpos2cut = 1
if abs(vpos1cut + vpos2cut) <= 1:
if vpos1cut:
if self.fillable:
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vpos1)))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vpos1, vvalue, vpos1))
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos1, privatedata.vfromvalue)))
privatedata.path.append(graph.vgeodesic_el(vpos1, privatedata.vfromvalue, vpos1, vvalue))
else:
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vpos1)))
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vpos1, vvalue)))
if privatedata.rangeaxisindex:
privatedata.path.append(graph.vgeodesic_el(vvalue, vpos1, vvalue, vpos2))
else:
privatedata.path.append(graph.vgeodesic_el(vpos1, vvalue, vpos2, vvalue))
if self.fillable and vpos2cut:
warnings.warn("cut at graph boundary adds artificial lines to fillable step histogram path")
if privatedata.rangeaxisindex:
privatedata.path.append(graph.vgeodesic_el(vvalue, vpos2, privatedata.vfromvalue, vpos2))
else:
privatedata.path.append(graph.vgeodesic_el(vpos2, vvalue, vpos2, privatedata.vfromvalue))
def drawvalue(self, privatedata, sharedata, graph, vmin, vmax, vvalue):
currentvalid = vmin is not None and vmax is not None and vvalue is not None
if self.fillable and not self.steps:
if not currentvalid:
return
vmincut = 0
if vmin < -self.epsilon:
vmin = 0
vmincut = -1
elif vmin > 1+self.epsilon:
vmin = 1
vmincut = 1
vmaxcut = 0
if vmax < -self.epsilon:
vmax = 0
vmaxcut = -1
if vmax > 1+self.epsilon:
vmax = 1
vmaxcut = 1
vvaluecut = 0
if vvalue < -self.epsilon:
vvalue = 0
vvaluecut = -1
if vvalue > 1+self.epsilon:
vvalue = 1
vvaluecut = 1
done = 0
if abs(vmincut) + abs(vmaxcut) + abs(vvaluecut) + abs(privatedata.vfromvaluecut) > 1:
if abs(vmincut + vmaxcut) > 1 or abs(vvaluecut+privatedata.vfromvaluecut) > 1:
done = 1
else:
warnings.warn("multiple cuts at graph boundary add artificial lines to fillable rectangle histogram path")
elif vmincut:
done = 1
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmin)))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax))
privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin))
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, privatedata.vfromvalue)))
privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue))
privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue))
privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue))
elif vmaxcut:
done = 1
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vmax)))
privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin))
privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax))
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmax, vvalue)))
privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue))
privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue))
privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue))
elif privatedata.vfromvaluecut:
done = 1
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmax)))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax))
privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin))
privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin))
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmax, privatedata.vfromvalue)))
privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue))
privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue))
privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue))
elif vvaluecut:
done = 1
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vvalue, vmin)))
privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax))
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, vvalue)))
privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue))
privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue))
privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue))
if not done:
if privatedata.rangeaxisindex:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(privatedata.vfromvalue, vmin)))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmin, privatedata.vfromvalue, vmax))
privatedata.path.append(graph.vgeodesic_el(privatedata.vfromvalue, vmax, vvalue, vmax))
privatedata.path.append(graph.vgeodesic_el(vvalue, vmax, vvalue, vmin))
privatedata.path.append(graph.vgeodesic_el(vvalue, vmin, privatedata.vfromvalue, vmin))
privatedata.path.append(path.closepath())
else:
privatedata.path.append(path.moveto_pt(*graph.vpos_pt(vmin, privatedata.vfromvalue)))
privatedata.path.append(graph.vgeodesic_el(vmin, privatedata.vfromvalue, vmax, privatedata.vfromvalue))
privatedata.path.append(graph.vgeodesic_el(vmax, privatedata.vfromvalue, vmax, vvalue))
privatedata.path.append(graph.vgeodesic_el(vmax, vvalue, vmin, vvalue))
privatedata.path.append(graph.vgeodesic_el(vmin, vvalue, vmin, privatedata.vfromvalue))
privatedata.path.append(path.closepath())
else:
try:
gap = abs(vmin - privatedata.lastvmax) > self.epsilon
except (ArithmeticError, ValueError, TypeError):
gap = 1
if (privatedata.lastvvalue is not None and currentvalid and not gap and
(self.steps or (privatedata.lastvvalue-privatedata.vfromvalue)*(vvalue-privatedata.vfromvalue) < 0)):
self.vposline(privatedata, sharedata, graph,
vmin, privatedata.lastvvalue, vvalue)
else:
if privatedata.lastvvalue is not None and currentvalid:
currentbigger = abs(privatedata.lastvvalue-privatedata.vfromvalue) < abs(vvalue-privatedata.vfromvalue)
if privatedata.lastvvalue is not None and (not currentvalid or not currentbigger or gap):
self.vposline(privatedata, sharedata, graph,
privatedata.lastvmax, privatedata.lastvvalue, privatedata.vfromvalue)
if currentvalid:
self.vmoveto(privatedata, sharedata, graph,
vmin, vvalue)
if currentvalid and (privatedata.lastvvalue is None or currentbigger or gap):
self.vmoveto(privatedata, sharedata, graph,
vmin, privatedata.vfromvalue)
self.vposline(privatedata, sharedata, graph,
vmin, privatedata.vfromvalue, vvalue)
if currentvalid:
self.vvalueline(privatedata, sharedata, graph,
vvalue, vmin, vmax)
privatedata.lastvvalue = vvalue
privatedata.lastvmax = vmax
else:
privatedata.lastvvalue = privatedata.lastvmax = None
def initdrawpoints(self, privatedata, sharedata, graph):
privatedata.path = path.path()
privatedata.lastvvalue = privatedata.lastvmax = None
privatedata.vcurrentpoint = None
privatedata.count = 0
if self.fromvalue is not None:
valueaxisname = sharedata.poscolumnnames[1-privatedata.rangeaxisindex]
privatedata.vfromvalue = graph.axes[valueaxisname].convert(self.fromvalue)
privatedata.vfromvaluecut = 0
if privatedata.vfromvalue < 0:
privatedata.vfromvalue = 0
privatedata.vfromvaluecut = -1
if privatedata.vfromvalue > 1:
privatedata.vfromvalue = 1
privatedata.vfromvaluecut = 1
if self.frompathattrs is not None and privatedata.insertfrompath:
graph.stroke(graph.axes[valueaxisname].vgridpath(privatedata.vfromvalue),
self.defaultfrompathattrs + self.frompathattrs)
else:
privatedata.vfromvalue = 0
def drawpoint(self, privatedata, sharedata, graph, point):
if privatedata.autohistogram:
# automatic range handling
privatedata.count += 1
if privatedata.count == 2:
if privatedata.rangeaxisindex:
privatedata.vrange = sharedata.vpos[1] - privatedata.lastvpos[1]
self.drawvalue(privatedata, sharedata, graph,
privatedata.lastvpos[1] - self.autohistogrampointpos*privatedata.vrange,
privatedata.lastvpos[1] + (1-self.autohistogrampointpos)*privatedata.vrange,
privatedata.lastvpos[0])
else:
privatedata.vrange = sharedata.vpos[0] - privatedata.lastvpos[0]
self.drawvalue(privatedata, sharedata, graph,
privatedata.lastvpos[0] - self.autohistogrampointpos*privatedata.vrange,
privatedata.lastvpos[0] + (1-self.autohistogrampointpos)*privatedata.vrange,
privatedata.lastvpos[1])
elif privatedata.count > 2:
if privatedata.rangeaxisindex:
vrange = sharedata.vpos[1] - privatedata.lastvpos[1]
else:
vrange = sharedata.vpos[0] - privatedata.lastvpos[0]
if abs(privatedata.vrange - vrange) > self.epsilon:
raise ValueError("equal steps (in graph coordinates) needed for automatic width calculation")
if privatedata.count > 1:
if privatedata.rangeaxisindex:
self.drawvalue(privatedata, sharedata, graph,
sharedata.vpos[1] - self.autohistogrampointpos*privatedata.vrange,
sharedata.vpos[1] + (1-self.autohistogrampointpos)*privatedata.vrange,
sharedata.vpos[0])
else:
self.drawvalue(privatedata, sharedata, graph,
sharedata.vpos[0] - self.autohistogrampointpos*privatedata.vrange,
sharedata.vpos[0] + (1-self.autohistogrampointpos)*privatedata.vrange,
sharedata.vpos[1])
privatedata.lastvpos = sharedata.vpos[:]
else:
if privatedata.rangeaxisindex:
self.drawvalue(privatedata, sharedata, graph,
sharedata.vrange[1][0], sharedata.vrange[1][1], sharedata.vpos[0])
else:
self.drawvalue(privatedata, sharedata, graph,
sharedata.vrange[0][0], sharedata.vrange[0][1], sharedata.vpos[1])
def donedrawpoints(self, privatedata, sharedata, graph):
self.drawvalue(privatedata, sharedata, graph, None, None, None)
if privatedata.lineattrs is not None and len(privatedata.path):
graph.draw(privatedata.path, privatedata.lineattrs)
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
if privatedata.lineattrs is not None:
if self.rectkey:
p = path.rect_pt(x_pt, y_pt, width_pt, height_pt)
else:
p = path.line_pt(x_pt, y_pt+0.5*height_pt, x_pt+width_pt, y_pt+0.5*height_pt)
graph.draw(p, privatedata.lineattrs)
class barpos(_style):
providesdata = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
defaultfrompathattrs = []
def __init__(self, fromvalue=None, frompathattrs=[], epsilon=1e-10):
self.fromvalue = fromvalue
self.frompathattrs = frompathattrs
self.epsilon = epsilon
def columnnames(self, privatedata, sharedata, graph, columnnames):
sharedata.barposcolumnnames = []
sharedata.barvalueindex = None
for dimension, axisnames in enumerate(graph.axesnames):
found = 0
for axisname in axisnames:
if axisname in columnnames:
if sharedata.barvalueindex is not None:
raise ValueError("multiple values")
sharedata.barvalueindex = dimension
sharedata.barposcolumnnames.append(axisname)
found += 1
if (axisname + "name") in columnnames:
sharedata.barposcolumnnames.append(axisname + "name")
found += 1
if found > 1:
raise ValueError("multiple names and value")
if not found:
raise ValueError("value/name missing")
if sharedata.barvalueindex is None:
raise ValueError("missing value")
sharedata.vposmissing = []
return sharedata.barposcolumnnames
def addsubvalue(self, value, subvalue):
try:
value + ""
except:
try:
return value[0], self.addsubvalue(value[1], subvalue)
except:
return value, subvalue
else:
return value, subvalue
def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
try:
i = sharedata.barposcolumnnames.index(columnname)
except ValueError:
pass
else:
if i == sharedata.barvalueindex:
if self.fromvalue is not None:
graph.axes[sharedata.barposcolumnnames[i]].adjustaxis([self.fromvalue])
graph.axes[sharedata.barposcolumnnames[i]].adjustaxis(data)
else:
graph.axes[sharedata.barposcolumnnames[i][:-4]].adjustaxis([self.addsubvalue(x, 0) for x in data])
graph.axes[sharedata.barposcolumnnames[i][:-4]].adjustaxis([self.addsubvalue(x, 1) for x in data])
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
privatedata.insertfrompath = selectindex == 0
def initdrawpoints(self, privatedata, sharedata, graph):
sharedata.vpos = [None]*(len(sharedata.barposcolumnnames))
sharedata.vbarrange = [[None for i in xrange(2)] for x in sharedata.barposcolumnnames]
sharedata.stackedbar = sharedata.stackedbardraw = 0
if self.fromvalue is not None:
privatedata.vfromvalue = graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex][0]].convert(self.fromvalue)
if privatedata.vfromvalue < 0:
privatedata.vfromvalue = 0
if privatedata.vfromvalue > 1:
privatedata.vfromvalue = 1
if self.frompathattrs is not None and privatedata.insertfrompath:
graph.stroke(graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex][0]].vgridpath(privatedata.vfromvalue),
self.defaultfrompathattrs + self.frompathattrs)
else:
privatedata.vfromvalue = 0
def drawpoint(self, privatedata, sharedata, graph, point):
sharedata.vposavailable = sharedata.vposvalid = 1
for i, barname in enumerate(sharedata.barposcolumnnames):
if i == sharedata.barvalueindex:
sharedata.vbarrange[i][0] = privatedata.vfromvalue
sharedata.lastbarvalue = point[barname]
try:
sharedata.vpos[i] = sharedata.vbarrange[i][1] = graph.axes[barname].convert(sharedata.lastbarvalue)
except (ArithmeticError, ValueError, TypeError):
sharedata.vpos[i] = sharedata.vbarrange[i][1] = None
else:
for j in xrange(2):
try:
sharedata.vbarrange[i][j] = graph.axes[barname[:-4]].convert(self.addsubvalue(point[barname], j))
except (ArithmeticError, ValueError, TypeError):
sharedata.vbarrange[i][j] = None
try:
sharedata.vpos[i] = 0.5*(sharedata.vbarrange[i][0]+sharedata.vbarrange[i][1])
except (ArithmeticError, ValueError, TypeError):
sharedata.vpos[i] = None
if sharedata.vpos[i] is None:
sharedata.vposavailable = sharedata.vposvalid = 0
elif sharedata.vpos[i] < -self.epsilon or sharedata.vpos[i] > 1+self.epsilon:
sharedata.vposvalid = 0
registerdefaultprovider(barpos(), ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"])
class stackedbarpos(_style):
# provides no additional data, but needs some data (and modifies some of them)
needsdata = ["vbarrange", "barposcolumnnames", "barvalueindex", "lastbarvalue", "stackedbar", "stackedbardraw"]
def __init__(self, stackname, addontop=0, epsilon=1e-10):
self.stackname = stackname
self.epsilon = epsilon
self.addontop = addontop
def columnnames(self, privatedata, sharedata, graph, columnnames):
if self.stackname not in columnnames:
raise ValueError("column '%s' missing" % self.stackname)
return [self.stackname]
def adjustaxis(self, privatedata, sharedata, graph, columnname, data):
if columnname == self.stackname:
graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex]].adjustaxis(data)
def initdrawpoints(self, privatedata, sharedata, graph):
if sharedata.stackedbardraw: # do not count the start bar when not gets painted
sharedata.stackedbar += 1
def drawpoint(self, privatedata, sharedata, graph, point):
sharedata.vbarrange[sharedata.barvalueindex][0] = sharedata.vbarrange[sharedata.barvalueindex][1]
if self.addontop:
try:
sharedata.lastbarvalue += point[self.stackname]
except (ArithmeticError, ValueError, TypeError):
sharedata.lastbarvalue = None
else:
sharedata.lastbarvalue = point[self.stackname]
try:
sharedata.vpos[sharedata.barvalueindex] = sharedata.vbarrange[sharedata.barvalueindex][1] = graph.axes[sharedata.barposcolumnnames[sharedata.barvalueindex]].convert(sharedata.lastbarvalue)
except (ArithmeticError, ValueError, TypeError):
sharedata.vpos[sharedata.barvalueindex] = sharedata.vbarrange[sharedata.barvalueindex][1] = None
sharedata.vposavailable = sharedata.vposvalid = 0
else:
if not sharedata.vposavailable or not sharedata.vposvalid:
sharedata.vposavailable = sharedata.vposvalid = 1
for v in sharedata.vpos:
if v is None:
sharedata.vposavailable = sharedata.vposvalid = 0
break
if v < -self.epsilon or v > 1+self.epsilon:
sharedata.vposvalid = 0
class bar(_style):
needsdata = ["vbarrange"]
defaultbarattrs = [color.gradient.Rainbow, deco.stroked([color.grey.black])]
def __init__(self, barattrs=[]):
self.barattrs = barattrs
def columnnames(self, privatedata, sharedata, graph, columnnames):
if len(graph.axesnames) != 2:
raise TypeError("bar style restricted on two-dimensional graphs")
return []
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
privatedata.barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal)
def initdrawpoints(self, privatedata, sharedata, graph):
privatedata.rectcanvas = graph.insert(canvas.canvas())
sharedata.stackedbardraw = 1
privatedata.stackedbar = sharedata.stackedbar
def drawpointfill(self, privatedata, p):
if p:
privatedata.rectcanvas.fill(p, privatedata.barattrs)
def drawpoint(self, privatedata, sharedata, graph, point):
xvmin = sharedata.vbarrange[0][0]
xvmax = sharedata.vbarrange[0][1]
yvmin = sharedata.vbarrange[1][0]
yvmax = sharedata.vbarrange[1][1]
try:
if xvmin > xvmax:
xvmin, xvmax = xvmax, xvmin
except:
pass
try:
if yvmin > yvmax:
yvmin, yvmax = yvmax, yvmin
except:
pass
if (xvmin is not None and xvmin < 1 and
xvmax is not None and xvmax > 0 and
yvmin is not None and yvmin < 1 and
yvmax is not None and yvmax > 0):
if xvmin < 0:
xvmin = 0
elif xvmax > 1:
xvmax = 1
if yvmin < 0:
yvmin = 0
elif yvmax > 1:
yvmax = 1
p = graph.vgeodesic(xvmin, yvmin, xvmax, yvmin)
p.append(graph.vgeodesic_el(xvmax, yvmin, xvmax, yvmax))
p.append(graph.vgeodesic_el(xvmax, yvmax, xvmin, yvmax))
p.append(graph.vgeodesic_el(xvmin, yvmax, xvmin, yvmin))
p.append(path.closepath())
self.drawpointfill(privatedata, p)
else:
self.drawpointfill(privatedata, None)
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
selectindex = privatedata.stackedbar
selecttotal = sharedata.stackedbar + 1
graph.fill(path.rect_pt(x_pt + width_pt*selectindex/float(selecttotal), y_pt, width_pt/float(selecttotal), height_pt), privatedata.barattrs)
class changebar(bar):
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
if selecttotal != 1:
raise RuntimeError("Changebar can't change its appearance. Thus you can't use it to plot several bars side by side on a subaxis.")
def initdrawpoints(self, privatedata, sharedata, graph):
bar.initdrawpoints(self, privatedata, sharedata, graph)
privatedata.bars = []
def drawpointfill(self, privatedata, p):
privatedata.bars.append(p)
def donedrawpoints(self, privatedata, sharedata, graph):
selecttotal = len(privatedata.bars)
for selectindex, p in enumerate(privatedata.bars):
if p:
barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal)
privatedata.rectcanvas.fill(p, barattrs)
def key_pt(self, privatedata, sharedata, graph, x_pt, y_pt, width_pt, height_pt):
raise RuntimeError("Style currently doesn't provide a graph key")
class gridpos(_style):
needsdata = ["vpos", "vposmissing", "vposavailable", "vposvalid"]
providesdata = ["values1", "values2", "data12", "data21", "index1", "index2"]
def __init__(self, index1=0, index2=1, epsilon=1e-10):
self.index1 = index1
self.index2 = index2
self.epsilon = epsilon
def initdrawpoints(self, privatedata, sharedata, graph):
sharedata.index1 = self.index1
sharedata.index2 = self.index2
sharedata.values1 = {}
sharedata.values2 = {}
sharedata.data12 = {}
sharedata.data21 = {}
def drawpoint(self, privatedata, sharedata, graph, point):
if sharedata.vposavailable:
sharedata.value1 = sharedata.vpos[self.index1]
sharedata.value2 = sharedata.vpos[self.index2]
if not sharedata.values1.has_key(sharedata.value1):
for hasvalue in sharedata.values1.keys():
if hasvalue - self.epsilon <= sharedata.value1 <= hasvalue + self.epsilon:
sharedata.value1 = hasvalue
break
else:
sharedata.values1[sharedata.value1] = 1
if not sharedata.values2.has_key(sharedata.value2):
for hasvalue in sharedata.values2.keys():
if hasvalue - self.epsilon <= sharedata.value2 <= hasvalue + self.epsilon:
sharedata.value2 = hasvalue
break
else:
sharedata.values2[sharedata.value2] = 1
data = sharedata.vposavailable, sharedata.vposvalid, sharedata.vpos[:]
sharedata.data12.setdefault(sharedata.value1, {})[sharedata.value2] = data
sharedata.data21.setdefault(sharedata.value2, {})[sharedata.value1] = data
registerdefaultprovider(gridpos(), gridpos.providesdata)
class grid(_line, _style):
needsdata = ["values1", "values2", "data12", "data21"]
defaultgridattrs = [line.changelinestyle]
def __init__(self, gridlines1=1, gridlines2=1, gridattrs=[]):
self.gridlines1 = gridlines1
self.gridlines2 = gridlines2
self.gridattrs = gridattrs
def selectstyle(self, privatedata, sharedata, graph, selectindex, selecttotal):
if self.gridattrs is not None:
privatedata.gridattrs = attr.selectattrs(self.defaultgridattrs + self.gridattrs, selectindex, selecttotal)
else:
privatedata.gridattrs = None
def donedrawpoints(self, privatedata, sharedata, graph):
values1 = sharedata.values1.keys()
values1.sort()
values2 = sharedata.values2.keys()
values2.sort()
if self.gridlines1:
for value2 in values2:
data1 = sharedata.data21[value2]
self.initpointstopath(privatedata)
for value1 in values1:
try:
data = data1[value1]
except KeyError:
self.addinvalid(privatedata)
else:
self.addpoint(privatedata, graph.vpos_pt, *data)
p = self.donepointstopath(privatedata)
if len(p):
graph.stroke(p, privatedata.gridattrs)
if self.gridlines2:
for value1 in values1:
data2 = sharedata.data12[value1]
self.initpointstopath(privatedata)
for value2 in values2:
try:
data = data2[value2]
except KeyError:
self.addinvalid(privatedata)
else:
self.addpoint(privatedata, graph.vpos_pt, *data)
p = self.donepointstopath(privatedata)
if len(p):
graph.stroke(p, privatedata.gridattrs)
class surface(_style):
needsdata = ["values1", "values2", "data12", "data21"]
def __init__(self, colorname="color", gradient=color.gradient.Grey, mincolor=None, maxcolor=None,
gridlines1=0.05, gridlines2=0.05, gridcolor=None,
backcolor=color.gray.black):
self.colorname = colorname
self.gradient = gradient
self.mincolor = mincolor
self.maxcolor = maxcolor
self.gridlines1 = gridlines1
self.gridlines2 = gridlines2
self.gridcolor = gridcolor
self.backcolor = backcolor
colorspacestring = gradient.getcolor(0).colorspacestring()
if self.gridcolor is not None and self.gridcolor.colorspacestring() != colorspacestring:
raise RuntimeError("colorspace mismatch (gradient/grid)")
if self.backcolor is not None and self.backcolor.colorspacestring() != colorspacestring:
raise RuntimeError("colorspace mismatch (gradient/back)")
def midvalue(self, v1, v2, v3, v4):
return [0.25*sum(values) for values in zip(v1, v2, v3, v4)]
def midcolor(self, c1, c2, c3, c4):
return 0.25*(c1+c2+c3+c4)
def lightning(self, angle, zindex):
if angle < 0 and self.backcolor is not None:
return self.backcolor
return self.gradient.getcolor(0.7-0.4*abs(angle)+0.1*zindex)
def columnnames(self, privatedata, sharedata, graph, columnnames):
privatedata.colorize = self.colorname in columnnames
if privatedata.colorize:
return [self.colorname]
return []
def initdrawpoints(self, privatedata, sharedata, graph):
privatedata.colors = {}
privatedata.mincolor = privatedata.maxcolor = None
def drawpoint(self, privatedata, sharedata, graph, point):
if privatedata.colorize:
try:
color = point[self.colorname] + 0
except:
pass
else:
privatedata.colors.setdefault(sharedata.value1, {})[sharedata.value2] = color
if privatedata.mincolor is None or color < privatedata.mincolor:
privatedata.mincolor = color
if privatedata.mincolor is None or privatedata.maxcolor < color:
privatedata.maxcolor = color
def donedrawpoints(self, privatedata, sharedata, graph):
v1 = [0]*len(graph.axesnames)
v2 = [0]*len(graph.axesnames)
v3 = [0]*len(graph.axesnames)
v4 = [0]*len(graph.axesnames)
v1[sharedata.index2] = 0.5
v2[sharedata.index1] = 0.5
v3[sharedata.index1] = 0.5
v3[sharedata.index2] = 1
v4[sharedata.index1] = 1
v4[sharedata.index2] = 0.5
sortElements = [-graph.vzindex(*v1),
-graph.vzindex(*v2),
-graph.vzindex(*v3),
-graph.vzindex(*v4)]
values1 = sharedata.values1.keys()
values1.sort()
v1 = [0]*len(graph.axesnames)
v2 = [0]*len(graph.axesnames)
v1[sharedata.index1] = -1
v2[sharedata.index1] = 1
sign = 1
if graph.vzindex(*v1) < graph.vzindex(*v2):
values1.reverse()
sign *= -1
sortElements = [sortElements[3], sortElements[1], sortElements[2], sortElements[0]]
values2 = sharedata.values2.keys()
values2.sort()
v1 = [0]*len(graph.axesnames)
v2 = [0]*len(graph.axesnames)
v1[sharedata.index2] = -1
v2[sharedata.index2] = 1
if graph.vzindex(*v1) < graph.vzindex(*v2):
values2.reverse()
sign *= -1
sortElements = [sortElements[0], sortElements[2], sortElements[1], sortElements[3]]
sortElements = [(zindex, i) for i, zindex in enumerate(sortElements)]
sortElements.sort()
if self.mincolor is not None:
mincolor = self.mincolor
if self.maxcolor is not None:
maxcolor = self.maxcolor
nodes = []
elements = []
for value1a, value1b in zip(values1[:-1], values1[1:]):
for value2a, value2b in zip(values2[:-1], values2[1:]):
try:
available1, valid1, v1 = sharedata.data12[value1a][value2a]
available2, valid2, v2 = sharedata.data12[value1a][value2b]
available3, valid3, v3 = sharedata.data12[value1b][value2a]
available4, valid4, v4 = sharedata.data12[value1b][value2b]
except KeyError:
continue
if not available1 or not available2 or not available3 or not available4:
continue
if not valid1 or not valid2 or not valid3 or not valid4:
warnings.warn("surface elements partially outside of the graph are (currently) skipped completely")
continue
def shrink(index, v1, v2, by):
v1 = v1[:]
v2 = v2[:]
for i in builtinrange(3):
if i != index:
v1[i], v2[i] = v1[i] + by*(v2[i]-v1[i]), v2[i] + by*(v1[i]-v2[i])
return v1, v2
v1f, v2f, v3f, v4f = v1, v2, v3, v4
if self.gridcolor is not None and self.gridlines1:
v1, v2 = shrink(sharedata.index1, v1, v2, self.gridlines1)
v3, v4 = shrink(sharedata.index1, v3, v4, self.gridlines1)
if self.gridcolor is not None and self.gridlines2:
v1, v3 = shrink(sharedata.index2, v1, v3, self.gridlines2)
v2, v4 = shrink(sharedata.index2, v2, v4, self.gridlines2)
v5 = self.midvalue(v1, v2, v3, v4)
x1_pt, y1_pt = graph.vpos_pt(*v1)
x2_pt, y2_pt = graph.vpos_pt(*v2)
x3_pt, y3_pt = graph.vpos_pt(*v3)
x4_pt, y4_pt = graph.vpos_pt(*v4)
x5_pt, y5_pt = graph.vpos_pt(*v5)
if privatedata.colorize:
def colorfromgradient(c):
return self.gradient.getcolor((c - privatedata.mincolor) /
float(privatedata.maxcolor - privatedata.mincolor))
c1 = privatedata.colors[value1a][value2a]
c2 = privatedata.colors[value1a][value2b]
c3 = privatedata.colors[value1b][value2a]
c4 = privatedata.colors[value1b][value2b]
c5 = self.midcolor(c1, c2, c3, c4)
c1a = c1b = colorfromgradient(c1)
c2a = c2c = colorfromgradient(c2)
c3b = c3d = colorfromgradient(c3)
c4c = c4d = colorfromgradient(c4)
c5a = c5b = c5c = c5d = colorfromgradient(c5)
if self.backcolor is not None and sign*graph.vangle(*(v1+v2+v5)) < 0:
c1a = c2a = c5a = self.backcolor
if self.backcolor is not None and sign*graph.vangle(*(v3+v1+v5)) < 0:
c3b = c1b = c5b = self.backcolor
if self.backcolor is not None and sign*graph.vangle(*(v2+v4+v5)) < 0:
c2c = c4c = c5c = self.backcolor
if self.backcolor is not None and sign*graph.vangle(*(v4+v3+v5)) < 0:
c4d = c3d = c5d = self.backcolor
else:
zindex = graph.vzindex(*v5)
c1a = c2a = c5a = self.lightning(sign*graph.vangle(*(v1+v2+v5)), zindex)
c3b = c1b = c5b = self.lightning(sign*graph.vangle(*(v3+v1+v5)), zindex)
c2c = c4c = c5c = self.lightning(sign*graph.vangle(*(v2+v4+v5)), zindex)
c4d = c3d = c5d = self.lightning(sign*graph.vangle(*(v4+v3+v5)), zindex)
for zindex, i in sortElements:
if i == 0:
elements.append(mesh.element((mesh.node_pt((x1_pt, y1_pt), c1a),
mesh.node_pt((x2_pt, y2_pt), c2a),
mesh.node_pt((x5_pt, y5_pt), c5a))))
if self.gridcolor is not None and self.gridlines2:
elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v1f), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v2), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v1), self.gridcolor))))
elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v1f), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v2), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v2f), self.gridcolor))))
elif i == 1:
elements.append(mesh.element((mesh.node_pt((x3_pt, y3_pt), c3b),
mesh.node_pt((x1_pt, y1_pt), c1b),
mesh.node_pt((x5_pt, y5_pt), c5b))))
if self.gridcolor is not None and self.gridlines1:
elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v1f), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v3), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v1), self.gridcolor))))
elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v1f), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v3), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v3f), self.gridcolor))))
elif i == 2:
elements.append(mesh.element((mesh.node_pt((x2_pt, y2_pt), c2c),
mesh.node_pt((x4_pt, y4_pt), c4c),
mesh.node_pt((x5_pt, y5_pt), c5c))))
if self.gridcolor is not None and self.gridlines1:
elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v2f), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v4), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v2), self.gridcolor))))
elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v2f), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v4), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v4f), self.gridcolor))))
elif i == 3:
elements.append(mesh.element((mesh.node_pt((x4_pt, y4_pt), c4d),
mesh.node_pt((x3_pt, y3_pt), c3d),
mesh.node_pt((x5_pt, y5_pt), c5d))))
if self.gridcolor is not None and self.gridlines2:
elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v3f), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v4), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v3), self.gridcolor))))
elements.append(mesh.element((mesh.node_pt(graph.vpos_pt(*v3f), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v4), self.gridcolor),
mesh.node_pt(graph.vpos_pt(*v4f), self.gridcolor))))
m = mesh.mesh(elements, check=0)
graph.insert(m)
|