From 54b43033bf3bce64ea67d05f0e5808c9c492f6cb Mon Sep 17 00:00:00 2001 From: =?utf8?q?Andr=C3=A9=20Wobst?= Date: Wed, 30 Jun 2004 12:48:21 +0000 Subject: [PATCH] graph style rework continues git-svn-id: https://pyx.svn.sourceforge.net/svnroot/pyx/trunk/pyx@1780 069f4177-920e-0410-937b-c2a4a81bcd90 --- pyx/graph/axis/axis.py | 34 +- pyx/graph/data.py | 46 +- pyx/graph/graph.py | 30 +- pyx/graph/key.py | 4 +- pyx/graph/style.py | 1803 ++++++++++++++++++++++------------------- test/functional/test_graph.py | 16 +- 6 files changed, 1035 insertions(+), 898 deletions(-) rewrite pyx/graph/style.py (65%) diff --git a/pyx/graph/axis/axis.py b/pyx/graph/axis/axis.py index 5bd2dccf..451e7dd8 100644 --- a/pyx/graph/axis/axis.py +++ b/pyx/graph/axis/axis.py @@ -229,38 +229,36 @@ class _axis: zero = 0.0 - def adjustrange(self, points, index, deltaindex=None, deltaminindex=None, deltamaxindex=None): + def adjustrange(self, data, index, deltamindata=None, deltaminindex=None, deltamaxdata=None, deltamaxindex=None): + assert deltaminindex is None or deltamaxindex is None min = max = None - if len([x for x in [deltaindex, deltaminindex, deltamaxindex] if x is not None]) > 1: - raise RuntimeError("only one of delta???index should set") - if deltaindex is not None: - deltaminindex = deltamaxindex = deltaindex if deltaminindex is not None: - for point in points: + for point, minpoint in zip(data, deltamindata): try: - value = point[index] - point[deltaminindex] + self.zero + value = point[index] - minpoint[deltaminindex] + self.zero except: pass else: if min is None or value < min: min = value if max is None or value > max: max = value - if deltamaxindex is not None: - for point in points: + elif deltamaxindex is not None: + for point, maxpoint in zip(data, deltamaxdata): try: - value = point[index] + point[deltamaxindex] + self.zero + value = point[index] + maxpoint[deltamaxindex] + self.zero + except: + pass + else: + if min is None or value < min: min = value + if max is None or value > max: max = value + else: + for point in data: + try: + value = point[index] + self.zero except: pass else: if min is None or value < min: min = value if max is None or value > max: max = value - for point in points: - try: - value = point[index] + self.zero - except: - pass - else: - if min is None or value < min: min = value - if max is None or value > max: max = value self.setrange(min, max) def getrange(self): diff --git a/pyx/graph/data.py b/pyx/graph/data.py index 5cbe3bbf..cf0b1d07 100644 --- a/pyx/graph/data.py +++ b/pyx/graph/data.py @@ -85,18 +85,32 @@ class _data: return [point[columnno] for point in self.points] def setstyles(self, graph, styles): - self.styles = styles + provided = [] + addstyles = [] # a list of style instances to be added + for s in styles: + for need in s.need: + if need not in provided: + for addstyle in addstyles: + if need in addstyle.provide: + break + else: + addstyles.append(style.provider[need]) + provided.extend(s.provide) + + self.styles = addstyles + styles self.styledata = styledata() - unhandledcolumns = self.columns - for style in self.styles: - unhandledcolumns = style.setdata(graph, unhandledcolumns, self.styledata) - unhandledcolumnkeys = unhandledcolumns.keys() - if len(unhandledcolumnkeys): - raise ValueError("style couldn't handle column keys %s" % unhandledcolumnkeys) + + columns = self.columns.keys() + usedcolumns = [] + for s in self.styles: + usedcolumns.extend(s.columns(self.styledata, graph, columns)) + for column in columns: + if column not in usedcolumns: + raise ValueError("unused column '%s'" % column) def selectstyle(self, graph, selectindex, selecttotal): for style in self.styles: - style.selectstyle(selectindex, selecttotal, self.styledata) + style.selectstyle(self.styledata, graph, selectindex, selecttotal) def adjustaxes(self, graph, step): """ @@ -107,18 +121,22 @@ class _data: - on step == 2 axes ranges not previously set should be updated by data accumulated by step 1""" if step == 0: - for style in self.styles: - style.adjustaxes(self.points, self.columns.values(), self.styledata) + for key, value in self.columns.items(): + for style in self.styles: + style.adjustaxis(self.styledata, graph, key, self.points, value) def draw(self, graph): + columnsitems = self.columns.items() + self.styledata.point = {} for style in self.styles: - style.initdrawpoints(graph, self.styledata) + style.initdrawpoints(self.styledata, graph) for point in self.points: - self.styledata.point = point + for key, value in columnsitems: + self.styledata.point[key] = point[value] for style in self.styles: - style.drawpoint(graph, self.styledata) + style.drawpoint(self.styledata, graph) for style in self.styles: - style.donedrawpoints(graph, self.styledata) + style.donedrawpoints(self.styledata, graph) class list(_data): diff --git a/pyx/graph/graph.py b/pyx/graph/graph.py index abc7addd..267f3ae7 100644 --- a/pyx/graph/graph.py +++ b/pyx/graph/graph.py @@ -137,8 +137,6 @@ class lineaxisposlinegrid(lineaxispos): class graphxy(canvas.canvas): - axisnames = "x", "y" - def plot(self, data, styles=None): if self.haslayout: raise RuntimeError("layout setup was already performed") @@ -193,15 +191,15 @@ class graphxy(canvas.canvas): return path.lineto_pt(self.xpos_pt + vx2*self.width_pt, self.ypos_pt + vy2*self.height_pt) - def vcap_pt(self, direction, length_pt, vx, vy): - """returns an error cap path for a given direction, lengths and + def vcap_pt(self, coordinate, length_pt, vx, vy): + """returns an error cap path for a given coordinate, lengths and point in graph coordinates""" - if direction == "x": + if coordinate == 0: return path.line_pt(self.xpos_pt + vx*self.width_pt - 0.5*length_pt, self.ypos_pt + vy*self.height_pt, self.xpos_pt + vx*self.width_pt + 0.5*length_pt, self.ypos_pt + vy*self.height_pt) - elif direction == "y": + elif coordinate == 1: return path.line_pt(self.xpos_pt + vx*self.width_pt, self.ypos_pt + vy*self.height_pt - 0.5*length_pt, self.xpos_pt + vx*self.width_pt, @@ -258,8 +256,8 @@ class graphxy(canvas.canvas): data.adjustaxes(self, step) # finish all axes - XPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[0]) - YPattern = re.compile(r"%s([2-9]|[1-9][0-9]+)?$" % self.axisnames[1]) + XPattern = re.compile(r"x([2-9]|[1-9][0-9]+)?$") + YPattern = re.compile(r"y([2-9]|[1-9][0-9]+)?$") xaxisextents = [0, 0] yaxisextents = [0, 0] needxaxisdist = [0, 0] @@ -274,7 +272,7 @@ class graphxy(canvas.canvas): if XPattern.match(key): if needxaxisdist[num2]: xaxisextents[num2] += self.axesdist - self.axespos[key] = lineaxisposlinegrid(self.axes[key].convert, + self.axespos[key] = lineaxisposlinegrid(axis.convert, self.xpos, self.ypos + num2*self.height - num3*xaxisextents[num2], self.xpos + self.width, @@ -295,7 +293,7 @@ class graphxy(canvas.canvas): elif YPattern.match(key): if needyaxisdist[num2]: yaxisextents[num2] += self.axesdist - self.axespos[key] = lineaxisposlinegrid(self.axes[key].convert, + self.axespos[key] = lineaxisposlinegrid(axis.convert, self.xpos + num2*self.width - num3*yaxisextents[num2], self.ypos, self.xpos + num2*self.width - num3*yaxisextents[num2], @@ -381,7 +379,7 @@ class graphxy(canvas.canvas): if self.height_pt <= 0: raise ValueError("height <= 0") def initaxes(self, axes, addlinkaxes=0): - for key in self.axisnames: + for key in ["x", "y"]: if not axes.has_key(key): axes[key] = axis.linear() elif axes[key] is None: @@ -392,6 +390,16 @@ class graphxy(canvas.canvas): elif axes[key + "2"] is None: del axes[key + "2"] self.axes = axes + self.axesnames = ([], []) + for key in axes.keys(): + if len(key) != 1 and (not key[1:].isdigit() or key[1:] == "1"): + raise ValueError("invalid axis count") + if key[0] == "x": + self.axesnames[0].append(key) + elif key[0] == "y": + self.axesnames[1].append(key) + else: + raise ValueError("invalid axis name") def __init__(self, xpos=0, ypos=0, width=None, height=None, ratio=goldenmean, key=None, backgroundattrs=None, axesdist=0.8*unit.v_cm, **axes): diff --git a/pyx/graph/key.py b/pyx/graph/key.py index 8c1e0e0b..64cd0ea3 100644 --- a/pyx/graph/key.py +++ b/pyx/graph/key.py @@ -83,8 +83,8 @@ class key: box.linealignequal_pt([plotdat.temp_titlebox for plotdat in plotdata], self.symbolwidth_pt + self.symbolspace_pt, 1, 0) for plotdat in plotdata: # TODO: loop over styles - plotdat.styles[-1].key_pt(c, 0, -0.5 * self.symbolheight_pt + plotdat.temp_titlebox.center[1], - self.symbolwidth_pt, self.symbolheight_pt, plotdat.styledata) + plotdat.styles[-1].key_pt(plotdat.styledata, c, 0, -0.5 * self.symbolheight_pt + plotdat.temp_titlebox.center[1], + self.symbolwidth_pt, self.symbolheight_pt) c.insert(plotdat.temp_titlebox) # for plotdat in plotdata: diff --git a/pyx/graph/style.py b/pyx/graph/style.py dissimilarity index 65% index 08c82cd1..c980ba8c 100644 --- a/pyx/graph/style.py +++ b/pyx/graph/style.py @@ -1,845 +1,958 @@ -#!/usr/bin/env python -# -*- coding: ISO-8859-1 -*- -# -# -# Copyright (C) 2002-2004 Jörg Lehmann -# Copyright (C) 2003-2004 Michael Schindler -# Copyright (C) 2002-2004 André Wobst -# -# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - - -import re, math -from pyx import attr, deco, style, color, unit, canvas, path -from pyx import text as textmodule - - -class _style: - - def setdatapattern(self, graph, columns, pattern): - for datakey in columns.keys(): - match = pattern.match(datakey) - if match: - # XXX match.groups()[0] must contain the full axisname - axisname = match.groups()[0] - index = columns[datakey] - del columns[datakey] - return graph.axes[axisname], index - - def key_pt(self, c, x_pt, y_pt, width_pt, height_pt, styledata): - raise RuntimeError("style doesn't provide a key") - - def adjustaxes(self, points, columns, styledata): - return - - def setdata(self, graph, columns, styledata): - return columns - - def selectstyle(self, selectindex, selecttotal, styledata): - pass - - def initdrawpoints(self, graph, styledata): - pass - - def drawpoint(self, graph, styledata): - pass - - def donedrawpoints(self, graph, styledata): - pass - - -class pointpos(_style): - - def __init__(self, epsilon=1e-10): - self.epsilon = epsilon - - def setdata(self, graph, columns, styledata): - # analyse column information - styledata.pointposaxisindex = [] - columns = columns.copy() - for axisname in graph.axisnames: - pattern = re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % axisname) - styledata.pointposaxisindex.append(self.setdatapattern(graph, columns, pattern)) # TODO: append "needed=1" - return columns - - def adjustaxes(self, points, columns, styledata): - for axis, index in styledata.pointposaxisindex: - axis.adjustrange(points, index) - - def drawpoint(self, graph, styledata): - # calculate vpos - styledata.vpos = [] - styledata.vposavailable = 1 # valid position (but might be outside of the graph) - styledata.vposvalid = 1 # valid position inside the graph - for axis, index in styledata.pointposaxisindex: - try: - v = axis.convert(styledata.point[index]) - except (ArithmeticError, KeyError, ValueError, TypeError): - styledata.vposavailable = 0 - styledata.vposvalid = 0 - styledata.vpos.append(None) - else: - if v < - self.epsilon or v > 1 + self.epsilon: - styledata.vposvalid = 0 - styledata.vpos.append(v) - - -class rangepos(_style): - - def setdata(self, graph, columns, styledata): - """ - - the instance should be considered read-only - (it might be shared between several data) - - styledata is the place where to store information - - returns the dictionary of columns not used by the style""" - - # analyse column information - styledata.index = {} # a nested index dictionary containing - # column numbers, e.g. styledata.index["x"]["x"], - # styledata.index["y"]["dmin"] etc.; the first key is a axis - # name (without the axis number), the second is one of - # the datanames ["x", "min", "max", "d", "dmin", "dmax"] - styledata.axes = {} # mapping from axis name (without axis number) to the axis - - columns = columns.copy() - for axisname in graph.axisnames: - for dataname, pattern in [("x", re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % axisname)), - ("min", re.compile(r"(%s([2-9]|[1-9][0-9]+)?)min$" % axisname)), - ("max", re.compile(r"(%s([2-9]|[1-9][0-9]+)?)max$" % axisname)), - ("d", re.compile(r"d(%s([2-9]|[1-9][0-9]+)?)$" % axisname)), - ("dmin", re.compile(r"d(%s([2-9]|[1-9][0-9]+)?)min$" % axisname)), - ("dmax", re.compile(r"d(%s([2-9]|[1-9][0-9]+)?)max$" % axisname))]: - matchresult = self.setdatapattern(graph, columns, pattern) - if matchresult is not None: - axis, index = matchresult - if styledata.axes.has_key(axisname): - if styledata.axes[axisname] != axis: - raise ValueError("axis mismatch for axis name '%s'" % axisname) - styledata.index[axisname][dataname] = index - else: - styledata.index[axisname] = {dataname: index} - styledata.axes[axisname] = axis - if not styledata.axes.has_key(axisname): - raise ValueError("missing columns for axis name '%s'" % axisname) - if ((styledata.index[axisname].has_key("min") and styledata.index[axisname].has_key("d")) or - (styledata.index[axisname].has_key("min") and styledata.index[axisname].has_key("dmin")) or - (styledata.index[axisname].has_key("d") and styledata.index[axisname].has_key("dmin")) or - (styledata.index[axisname].has_key("max") and styledata.index[axisname].has_key("d")) or - (styledata.index[axisname].has_key("max") and styledata.index[axisname].has_key("dmax")) or - (styledata.index[axisname].has_key("d") and styledata.index[axisname].has_key("dmax"))): - raise ValueError("multiple errorbar definition for axis name '%s'" % axisname) - if (not styledata.index[axisname].has_key("x") and - (styledata.index[axisname].has_key("d") or - styledata.index[axisname].has_key("dmin") or - styledata.index[axisname].has_key("dmax"))): - raise ValueError("errorbar definition start value missing for axis name '%s'" % axisname) - return columns - - def adjustaxes(self, points, columns, styledata): - # reverse lookup for axisnames - # TODO: the reverse lookup is ugly - axisnames = [] - for column in columns: - for axisname in styledata.index.keys(): - for thiscolumn in styledata.index[axisname].values(): - if thiscolumn == column and axisname not in axisnames: - axisnames.append(axisname) - # TODO: perform check to verify that all columns for a given axisname are available at the same time - for axisname in axisnames: - if styledata.index[axisname].has_key("x"): - styledata.axes[axisname].adjustrange(points, styledata.index[axisname]["x"]) - if styledata.index[axisname].has_key("min"): - styledata.axes[axisname].adjustrange(points, styledata.index[axisname]["min"]) - if styledata.index[axisname].has_key("max"): - styledata.axes[axisname].adjustrange(points, styledata.index[axisname]["max"]) - if styledata.index[axisname].has_key("d"): - styledata.axes[axisname].adjustrange(points, styledata.index[axisname]["x"], deltaindex=styledata.index[axisname]["d"]) - if styledata.index[axisname].has_key("dmin"): - styledata.axes[axisname].adjustrange(points, styledata.index[axisname]["x"], deltaminindex=styledata.index[axisname]["dmin"]) - if styledata.index[axisname].has_key("dmax"): - styledata.axes[axisname].adjustrange(points, styledata.index[axisname]["x"], deltamaxindex=styledata.index[axisname]["dmax"]) - - def doerrorbars(self, styledata): - # errorbar loop over the different direction having errorbars - for erroraxisname, erroraxisindex in styledata.errorlist: - - # check for validity of other point components - i = 0 - for v in styledata.vpos: - if v is None and i != erroraxisindex: - break - i += 1 - else: - # calculate min and max - errorindex = styledata.index[erroraxisname] - try: - min = styledata.point[errorindex["x"]] - styledata.point[errorindex["d"]] - except: - try: - min = styledata.point[errorindex["x"]] - styledata.point[errorindex["dmin"]] - except: - try: - min = styledata.point[errorindex["min"]] - except: - min = None - try: - max = styledata.point[errorindex["x"]] + styledata.point[errorindex["d"]] - except: - try: - max = styledata.point[errorindex["x"]] + styledata.point[errorindex["dmax"]] - except: - try: - max = styledata.point[errorindex["max"]] - except: - max = None - - # calculate vmin and vmax - try: - vmin = styledata.axes[erroraxisname].convert(min) - except: - vmin = None - try: - vmax = styledata.axes[erroraxisname].convert(max) - except: - vmax = None - - # create vminpos and vmaxpos - vcaps = [] - if vmin is not None: - vminpos = styledata.vpos[:] - if vmin > - self.epsilon and vmin < 1 + self.epsilon: - vminpos[erroraxisindex] = vmin - vcaps.append(vminpos) - else: - vminpos[erroraxisindex] = 0 - elif styledata.vpos[erroraxisindex] is not None: - vminpos = styledata.vpos - else: - break - if vmax is not None: - vmaxpos = styledata.vpos[:] - if vmax > - self.epsilon and vmax < 1 + self.epsilon: - vmaxpos[erroraxisindex] = vmax - vcaps.append(vmaxpos) - else: - vmaxpos[erroraxisindex] = 1 - elif styledata.vpos[erroraxisindex] is not None: - vmaxpos = styledata.vpos - else: - break - - -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 symbol(_style): - - # insert symbols like staticmethods - cross = _crosssymbol - plus = _plussymbol - square = _squaresymbol - triangle = _trianglesymbol - circle = _circlesymbol - diamond = _diamondsymbol - - changecross = attr.changelist([cross, plus, square, triangle, circle, diamond]) - changeplus = attr.changelist([plus, square, triangle, circle, diamond, cross]) - changesquare = attr.changelist([square, triangle, circle, diamond, cross, plus]) - changetriangle = attr.changelist([triangle, circle, diamond, cross, plus, square]) - changecircle = attr.changelist([circle, diamond, cross, plus, square, triangle]) - changediamond = attr.changelist([diamond, cross, plus, square, triangle, circle]) - changesquaretwice = attr.changelist([square, square, triangle, triangle, circle, circle, diamond, diamond]) - changetriangletwice = attr.changelist([triangle, triangle, circle, circle, diamond, diamond, square, square]) - changecircletwice = attr.changelist([circle, circle, diamond, diamond, square, square, triangle, triangle]) - changediamondtwice = attr.changelist([diamond, diamond, square, square, triangle, triangle, circle, circle]) - - 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, selectindex, selecttotal, styledata): - styledata.symbol = attr.selectattr(self.symbol, selectindex, selecttotal) - styledata.size_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal)) - if self.symbolattrs is not None: - styledata.symbolattrs = attr.selectattrs(self.defaultsymbolattrs + self.symbolattrs, selectindex, selecttotal) - else: - styledata.symbolattrs = None - - def drawpoint(self, graph, styledata): - if styledata.vposvalid and styledata.symbolattrs is not None: - xpos, ypos = graph.vpos_pt(*styledata.vpos) - styledata.symbol(graph, xpos, ypos, styledata.size_pt, styledata.symbolattrs) - - def key_pt(self, graph, x_pt, y_pt, width_pt, height_pt, styledata): - if styledata.symbolattrs is not None: - styledata.symbol(graph, x_pt+0.5*width_pt, y_pt+0.5*height_pt, styledata.size_pt, styledata.symbolattrs) - - -class line(_style): - - 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, selectindex, selecttotal, styledata): - styledata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal) - - def initdrawpoints(self, graph, styledata): - styledata.linecanvas = graph.insert(canvas.canvas()) - styledata.path = path.path() - styledata.linebasepoints = [] - styledata.lastvpos = None - - def appendlinebasepoints(self, graph, styledata): - # append linebasepoints - if styledata.vposavailable: - if len(styledata.linebasepoints): - # the last point was inside the graph - if styledata.vposvalid: # shortcut for the common case - styledata.linebasepoints.append(graph.vpos_pt(*styledata.vpos)) - else: - # cut end - cut = 1 - for vstart, vend in zip(styledata.lastvpos, styledata.vpos): - newcut = None - if vend > 1: - # 1 = vstart + (vend - vstart) * cut - try: - newcut = (1 - vstart)/(vend - vstart) - except ArithmeticError: - break - if vend < 0: - # 0 = vstart + (vend - vstart) * cut - try: - newcut = - vstart/(vend - vstart) - except ArithmeticError: - break - if newcut is not None and newcut < cut: - cut = newcut - else: - cutvpos = [] - for vstart, vend in zip(styledata.lastvpos, styledata.vpos): - cutvpos.append(vstart + (vend - vstart) * cut) - styledata.linebasepoints.append(styledata.graph.vpos_pt(*cutvpos)) - styledata.lastvpos = styledata.vpos - return 0 - else: - # the last point was outside the graph - if styledata.lastvpos is not None: - if styledata.vposvalid: - # cut beginning - cut = 0 - for vstart, vend in zip(styledata.lastvpos, styledata.vpos): - newcut = None - if vstart > 1: - # 1 = vstart + (vend - vstart) * cut - try: - newcut = (1 - vstart)/(vend - vstart) - except ArithmeticError: - break - if vstart < 0: - # 0 = vstart + (vend - vstart) * cut - try: - newcut = - vstart/(vend - vstart) - except ArithmeticError: - break - if newcut is not None and newcut > cut: - cut = newcut - else: - cutvpos = [] - for vstart, vend in zip(styledata.lastvpos, styledata.vpos): - cutvpos.append(vstart + (vend - vstart) * cut) - styledata.linebasepoints.append(graph.vpos_pt(*cutvpos)) - styledata.linebasepoints.append(graph.vpos_pt(*styledata.vpos)) - else: - # sometimes cut beginning and end - cutfrom = 0 - cutto = 1 - for vstart, vend in zip(styledata.lastvpos, styledata.vpos): - newcutfrom = None - if vstart > 1: - if vend > 1: - break - # 1 = vstart + (vend - vstart) * cutfrom - try: - newcutfrom = (1 - vstart)/(vend - vstart) - except ArithmeticError: - break - if vstart < 0: - if vend < 0: - break - # 0 = vstart + (vend - vstart) * cutfrom - try: - newcutfrom = - vstart/(vend - vstart) - except ArithmeticError: - 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: - break - if vend < 0: - # 0 = vstart + (vend - vstart) * cutto - try: - newcutto = - vstart/(vend - vstart) - except ArithmeticError: - break - if newcutto is not None and newcutto < cutto: - cutto = newcutto - else: - if cutfrom < cutto: - cutfromvpos = [] - cuttovpos = [] - for vstart, vend in zip(styledata.lastvpos, styledata.vpos): - cutfromvpos.append(vstart + (vend - vstart) * cutfrom) - cuttovpos.append(vstart + (vend - vstart) * cutto) - styledata.linebasepoints.append(styledata.graph.vpos_pt(*cutfromvpos)) - styledata.linebasepoints.append(styledata.graph.vpos_pt(*cuttovpos)) - styledata.lastvpos = styledata.vpos - return 0 - styledata.lastvpos = styledata.vpos - else: - styledata.lastvpos = None - return 0 - - def addpointstopath(self, styledata): - # add baselinepoints to styledata.path - if len(styledata.linebasepoints) > 1: - styledata.path.append(path.moveto_pt(*styledata.linebasepoints[0])) - if len(styledata.linebasepoints) > 2: - styledata.path.append(path.multilineto_pt(styledata.linebasepoints[1:])) - else: - styledata.path.append(path.lineto_pt(*styledata.linebasepoints[1])) - styledata.linebasepoints = [] - - def drawpoint(self, graph, styledata): - if self.appendlinebasepoints(graph, styledata): - self.addpointstopath(styledata) - - def donedrawpoints(self, graph, styledata): - self.addpointstopath(styledata) - if styledata.lineattrs is not None and len(styledata.path.path): - styledata.linecanvas.stroke(styledata.path, styledata.lineattrs) - - def key_pt(self, c, x_pt, y_pt, width_pt, height_pt, styledata): - if styledata.lineattrs is not None: - c.stroke(path.line_pt(x_pt, y_pt+0.5*height_pt, x_pt+width_pt, y_pt+0.5*height_pt), styledata.lineattrs) - - -class errorbars(_style): - - defaulterrorbarattrs = [] - - def __init__(self, size=0.1*unit.v_cm, - errorbarattrs=[], - epsilon=1e-10): - self.size = size - self.errorbarattrs = errorbarattrs - self.epsilon = epsilon - - def selectstyle(self, selectindex, selecttotal, styledata): - styledata.errorsize_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal)) - styledata.errorbarattrs = attr.selectattrs(self.defaulterrorbarattrs + self.errorbarattrs, selectindex, selecttotal) - - def initdrawpoints(self, graph, styledata): - styledata.errorbarcanvas = graph.insert(canvas.canvas()) - styledata.errorlist = [] - if styledata.errorbarattrs is not None: - axisindex = 0 - for axisname in graph.axisnames: - if styledata.index[axisname].keys() != ["x"]: - styledata.errorlist.append((axisname, axisindex)) - axisindex += 1 - - def doerrorbars(self, styledata): - # errorbar loop over the different direction having errorbars - for erroraxisname, erroraxisindex in styledata.errorlist: - # create path for errorbars - errorpath = path.path() - errorpath += styledata.graph.vgeodesic(*(vminpos + vmaxpos)) - for vcap in vcaps: - for axisname in styledata.graph.axisnames: - if axisname != erroraxisname: - errorpath += styledata.graph.vcap_pt(axisname, styledata.errorsize_pt, *vcap) - - # stroke errorpath - if len(errorpath.path): - styledata.errorbarcanvas.stroke(errorpath, styledata.errorbarattrs) - - def drawpoint(self, graph, styledata): - self.doerrorbars(styledata) - - -class text(symbol): - - defaulttextattrs = [textmodule.halign.center, textmodule.vshift.mathaxis] - - def __init__(self, textdx=0*unit.v_cm, textdy=0.3*unit.v_cm, textattrs=[], **kwargs): - self.textdx = textdx - self.textdy = textdy - self.textattrs = textattrs - symbol.__init__(self, **kwargs) - - def setdata(self, graph, columns, styledata): - columns = columns.copy() - styledata.textindex = columns["text"] - del columns["text"] - return symbol.setdata(self, graph, columns, styledata) - - def selectstyle(self, selectindex, selecttotal, styledata): - if self.textattrs is not None: - styledata.textattrs = attr.selectattrs(self.defaulttextattrs + self.textattrs, selectindex, selecttotal) - else: - styledata.textattrs = None - symbol.selectstyle(self, selectindex, selecttotal, styledata) - - def drawsymbol_pt(self, c, x, y, styledata, point=None): - symbol.drawsymbol_pt(self, c, x, y, styledata, point) - if None not in (x, y, point[styledata.textindex]) and styledata.textattrs is not None: - c.text_pt(x + styledata.textdx_pt, y + styledata.textdy_pt, str(point[styledata.textindex]), styledata.textattrs) - - def drawpoints(self, points, graph, styledata): - styledata.textdx_pt = unit.topt(self.textdx) - styledata.textdy_pt = unit.topt(self.textdy) - symbol.drawpoints(self, points, graph, styledata) - - -class arrow(_style): - - defaultlineattrs = [] - defaultarrowattrs = [] - - def __init__(self, linelength=0.25*unit.v_cm, arrowsize=0.15*unit.v_cm, lineattrs=[], arrowattrs=[], epsilon=1e-10): - self.linelength = linelength - self.arrowsize = arrowsize - self.lineattrs = lineattrs - self.arrowattrs = arrowattrs - self.epsilon = epsilon - - def setdata(self, graph, columns, styledata): - if len(graph.axisnames) != 2: - raise TypeError("arrow style restricted on two-dimensional graphs") - columns = columns.copy() - styledata.xaxis, styledata.xindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % graph.axisnames[0])) - styledata.yaxis, styledata.yindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % graph.axisnames[1])) - styledata.sizeindex = columns["size"] - del columns["size"] - styledata.angleindex = columns["angle"] - del columns["angle"] - return columns - - def adjustaxes(self, points, columns, styledata): - if styledata.xindex in columns: - styledata.xaxis.adjustrange(points, styledata.xindex) - if styledata.yindex in columns: - styledata.yaxis.adjustrange(points, styledata.yindex) - - def selectstyle(self, selectindex, selecttotal, styledata): - if self.lineattrs is not None: - styledata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal) - else: - styledata.lineattrs = None - if self.arrowattrs is not None: - styledata.arrowattrs = attr.selectattrs(self.defaultarrowattrs + self.arrowattrs, selectindex, selecttotal) - else: - styledata.arrowattrs = None - - def drawpoints(self, points, graph, styledata): - if styledata.lineattrs is not None and styledata.arrowattrs is not None: - linelength_pt = unit.topt(self.linelength) - for point in points: - xpos, ypos = graph.pos_pt(point[styledata.xindex], point[styledata.yindex], xaxis=styledata.xaxis, yaxis=styledata.yaxis) - if point[styledata.sizeindex] > self.epsilon: - dx = math.cos(point[styledata.angleindex]*math.pi/180) - dy = math.sin(point[styledata.angleindex]*math.pi/180) - x1 = xpos-0.5*dx*linelength_pt*point[styledata.sizeindex] - y1 = ypos-0.5*dy*linelength_pt*point[styledata.sizeindex] - x2 = xpos+0.5*dx*linelength_pt*point[styledata.sizeindex] - y2 = ypos+0.5*dy*linelength_pt*point[styledata.sizeindex] - graph.stroke(path.line_pt(x1, y1, x2, y2), styledata.lineattrs + - [deco.earrow(styledata.arrowattrs, size=self.arrowsize*point[styledata.sizeindex])]) - - -class rect(_style): - - def __init__(self, palette=color.palette.Gray): - self.palette = palette - - def setdata(self, graph, columns, styledata): - if len(graph.axisnames) != 2: - raise TypeError("arrow style restricted on two-dimensional graphs") - columns = columns.copy() - styledata.xaxis, styledata.xminindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)min$" % graph.axisnames[0])) - styledata.yaxis, styledata.yminindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)min$" % graph.axisnames[1])) - xaxis, styledata.xmaxindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)max$" % graph.axisnames[0])) - yaxis, styledata.ymaxindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)max$" % graph.axisnames[1])) - if xaxis != styledata.xaxis or yaxis != styledata.yaxis: - raise ValueError("min/max values should use the same axes") - styledata.colorindex = columns["color"] - del columns["color"] - return columns - - def selectstyle(self, selectindex, selecttotal, styledata): - pass - - def adjustaxes(self, points, columns, styledata): - if styledata.xminindex in columns: - styledata.xaxis.adjustrange(points, styledata.xminindex) - if styledata.xmaxindex in columns: - styledata.xaxis.adjustrange(points, styledata.xmaxindex) - if styledata.yminindex in columns: - styledata.yaxis.adjustrange(points, styledata.yminindex) - if styledata.ymaxindex in columns: - styledata.yaxis.adjustrange(points, styledata.ymaxindex) - - def drawpoints(self, points, graph, styledata): - # TODO: bbox shortcut - c = graph.insert(canvas.canvas()) - lastcolorvalue = None - for point in points: - try: - xvmin = styledata.xaxis.convert(point[styledata.xminindex]) - xvmax = styledata.xaxis.convert(point[styledata.xmaxindex]) - yvmin = styledata.yaxis.convert(point[styledata.yminindex]) - yvmax = styledata.yaxis.convert(point[styledata.ymaxindex]) - colorvalue = point[styledata.colorindex] - if colorvalue != lastcolorvalue: - color = self.palette.getcolor(point[styledata.colorindex]) - except: - continue - if ((xvmin < 0 and xvmax < 0) or (xvmin > 1 and xvmax > 1) or - (yvmin < 0 and yvmax < 0) or (yvmin > 1 and yvmax > 1)): - continue - if xvmin < 0: - xvmin = 0 - elif xvmin > 1: - xvmin = 1 - if xvmax < 0: - xvmax = 0 - elif xvmax > 1: - xvmax = 1 - if yvmin < 0: - yvmin = 0 - elif yvmin > 1: - yvmin = 1 - if yvmax < 0: - yvmax = 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()) - if colorvalue != lastcolorvalue: - c.set([color]) - c.fill(p) - - -class bar(_style): - - defaultfrompathattrs = [] - defaultbarattrs = [color.palette.Rainbow, deco.stroked([color.gray.black])] - - def __init__(self, fromvalue=None, frompathattrs=[], barattrs=[], subnames=None, epsilon=1e-10): - self.fromvalue = fromvalue - self.frompathattrs = frompathattrs - self.barattrs = barattrs - self.subnames = subnames - self.epsilon = epsilon - - def setdata(self, graph, columns, styledata): - # TODO: remove limitation to 2d graphs - if len(graph.axisnames) != 2: - raise TypeError("arrow style currently restricted on two-dimensional graphs") - columns = columns.copy() - xvalue = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % graph.axisnames[0])) - yvalue = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % graph.axisnames[1])) - if (xvalue is None and yvalue is None) or (xvalue is not None and yvalue is not None): - raise TypeError("must specify exactly one value axis") - if xvalue is not None: - styledata.valuepos = 0 - styledata.nameaxis, styledata.nameindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)name$" % graph.axisnames[1])) - styledata.valueaxis = xvalue[0] - styledata.valueindices = [xvalue[1]] - else: - styledata.valuepos = 1 - styledata.nameaxis, styledata.nameindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)name$" % graph.axisnames[0])) - styledata.valueaxis = yvalue[0] - styledata.valueindices = [yvalue[1]] - i = 1 - while 1: - try: - valueaxis, valueindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)stack%i$" % (graph.axisnames[styledata.valuepos], i))) - except: - break - if styledata.valueaxis != valueaxis: - raise ValueError("different value axes for stacked bars") - styledata.valueindices.append(valueindex) - i += 1 - return columns - - def selectstyle(self, selectindex, selecttotal, styledata): - if selectindex: - styledata.frompathattrs = None - else: - styledata.frompathattrs = self.defaultfrompathattrs + self.frompathattrs - if selecttotal > 1: - if self.barattrs is not None: - styledata.barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal) - else: - styledata.barattrs = None - else: - styledata.barattrs = self.defaultbarattrs + self.barattrs - styledata.selectindex = selectindex - styledata.selecttotal = selecttotal - if styledata.selecttotal != 1 and self.subnames is not None: - raise ValueError("subnames not allowed when iterating over bars") - - def adjustaxes(self, points, columns, styledata): - if styledata.nameindex in columns: - if styledata.selecttotal == 1: - styledata.nameaxis.adjustrange(points, styledata.nameindex, subnames=self.subnames) - else: - for i in range(styledata.selecttotal): - styledata.nameaxis.adjustrange(points, styledata.nameindex, subnames=[i]) - for valueindex in styledata.valueindices: - if valueindex in columns: - styledata.valueaxis.adjustrange(points, valueindex) - - def drawpoints(self, points, graph, styledata): - if self.fromvalue is not None: - vfromvalue = styledata.valueaxis.convert(self.fromvalue) - if vfromvalue < -self.epsilon: - vfromvalue = 0 - if vfromvalue > 1 + self.epsilon: - vfromvalue = 1 - if styledata.frompathattrs is not None and vfromvalue > self.epsilon and vfromvalue < 1 - self.epsilon: - if styledata.valuepos: - p = graph.vgeodesic(0, vfromvalue, 1, vfromvalue) - else: - p = graph.vgeodesic(vfromvalue, 0, vfromvalue, 1) - graph.stroke(p, styledata.frompathattrs) - else: - vfromvalue = 0 - l = len(styledata.valueindices) - if l > 1: - barattrslist = [] - for i in range(l): - barattrslist.append(attr.selectattrs(styledata.barattrs, i, l)) - else: - barattrslist = [styledata.barattrs] - for point in points: - vvaluemax = vfromvalue - for valueindex, barattrs in zip(styledata.valueindices, barattrslist): - vvaluemin = vvaluemax - try: - vvaluemax = styledata.valueaxis.convert(point[valueindex]) - except: - continue - - if styledata.selecttotal == 1: - try: - vnamemin = styledata.nameaxis.convert((point[styledata.nameindex], 0)) - except: - continue - try: - vnamemax = styledata.nameaxis.convert((point[styledata.nameindex], 1)) - except: - continue - else: - try: - vnamemin = styledata.nameaxis.convert((point[styledata.nameindex], styledata.selectindex, 0)) - except: - continue - try: - vnamemax = styledata.nameaxis.convert((point[styledata.nameindex], styledata.selectindex, 1)) - except: - continue - - if styledata.valuepos: - p = graph.vgeodesic(vnamemin, vvaluemin, vnamemin, vvaluemax) - p.append(graph.vgeodesic_el(vnamemin, vvaluemax, vnamemax, vvaluemax)) - p.append(graph.vgeodesic_el(vnamemax, vvaluemax, vnamemax, vvaluemin)) - p.append(graph.vgeodesic_el(vnamemax, vvaluemin, vnamemin, vvaluemin)) - p.append(path.closepath()) - else: - p = graph.vgeodesic(vvaluemin, vnamemin, vvaluemin, vnamemax) - p.append(graph.vgeodesic_el(vvaluemin, vnamemax, vvaluemax, vnamemax)) - p.append(graph.vgeodesic_el(vvaluemax, vnamemax, vvaluemax, vnamemin)) - p.append(graph.vgeodesic_el(vvaluemax, vnamemin, vvaluemin, vnamemin)) - p.append(path.closepath()) - if barattrs is not None: - graph.fill(p, barattrs) - - def key_pt(self, c, x_pt, y_pt, width_pt, height_pt, styledata): - l = len(styledata.valueindices) - if l > 1: - for i in range(l): - c.fill(path.rect_pt(x_pt+i*width_pt/l, y_pt, width_pt/l, height_pt), attr.selectattrs(styledata.barattrs, i, l)) - else: - c.fill(path.rect_pt(x_pt, y_pt, width_pt, height_pt), styledata.barattrs) +#!/usr/bin/env python +# -*- coding: ISO-8859-1 -*- +# +# +# Copyright (C) 2002-2004 Jörg Lehmann +# Copyright (C) 2003-2004 Michael Schindler +# Copyright (C) 2002-2004 André Wobst +# +# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +import re, math +from pyx import attr, deco, style, color, unit, canvas, path +from pyx import text as textmodule + + +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 instance variables + (i.e. accessing self), but it should use the styledata instance + instead. A style instance can be used multiple times with different + styledata instances at the very same time. The styledata instance + acts as a data container and furthermore allows for sharing + information across several styles. + + A style contains two class variables, which are not to be + modified. provide is a of variable names a style provides via + the styledata instance. This list should be used to find, whether + all needs of subsequent styles are fullfilled. Otherwise the + provider dictionary described below should list a proper style + to be inserted. Contrary, need is a list of variable names the + style needs to access in the styledata instance.""" + + provide = [] # by default, we provide nothing + need = [] # and do not depend on anything + + def columns(self, styledata, graph, columns): + """Set column information + + This method is used setup the column information to be + accessible to the style later on. The style should analyse + the list of strings columns, which contain the column names + of the data. The method should return a list of column names + which the style will make use of.""" + return [] + + def selectstyle(self, styledata, graph, selectindex, selecttotal): + """Select stroke/fill attributes + + This method is called to allow for the selection of + changable attributes of a style.""" + pass + + def adjustaxis(self, styledata, graph, column, data, index): + """Adjust axis range + + This method is called in order to adjust the axis range to + the provided data. Column is the name of the column (each + style is subsequently called for all column names), data is + a list of points and index is the index of the column within + a point.""" + pass + + def initdrawpoints(self, styledata, graph): + """Initialize drawing of data + + This method might be used to initialize the drawing of data.""" + pass + + def drawpoint(self, styledata, graph): + """Draw data + + This method is called for each data point. The data is + available in the dictionary styledata.data. The dictionary + keys are the column names.""" + pass + + def donedrawpoints(self, styledata, 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, styledata, graph, x_pt, y_pt, width_pt, height_pt): + """Draw a graph key + + Draw a graph key to graph.""" + pass + + +# provider is a dictionary, which maps styledata variable names +# to default styles, which provide a default way to create the +# corresponding styledata variable. + +provider = {} + + +class _pos(_style): + + provide = ["vpos", "vposmissing", "vposavailable", "vposvalid"] + + def __init__(self, epsilon=1e-10): + self.epsilon = epsilon + + def columns(self, styledata, graph, columns): + styledata.pointposcolumns = [] + styledata.vposmissing = [] + for count, axisnames in enumerate(graph.axesnames): + for axisname in axisnames: + for column in columns: + if axisname == column: + styledata.pointposcolumns.append(column) + if len(styledata.pointposcolumns) + len(styledata.vposmissing) > count+1: + raise ValueError("multiple axes per graph dimension") + elif len(styledata.pointposcolumns) + len(styledata.vposmissing) < count+1: + styledata.vposmissing.append(count) + return styledata.pointposcolumns + + def adjustaxis(self, styledata, graph, column, data, index): + if column in styledata.pointposcolumns: + graph.axes[column].adjustrange(data, index) + + def initdrawpoints(self, styledata, graph): + styledata.vpos = [None]*(len(styledata.pointposcolumns) + len(styledata.vposmissing)) + styledata.pointpostmplist = [[column, index, graph.axes[column]] # temporarily used by drawpoint only + for index, column in enumerate(styledata.pointposcolumns)] + for missing in styledata.vposmissing: + for pointpostmp in styledata.pointpostmplist: + if pointpostmp[1] >= missing: + pointpostmp[1] += 1 + + def drawpoint(self, styledata, graph): + styledata.vposavailable = 1 # valid position (but might be outside of the graph) + styledata.vposvalid = 1 # valid position inside the graph + for column, index, axis in styledata.pointpostmplist: + try: + v = axis.convert(styledata.point[column]) + except (ArithmeticError, ValueError, TypeError): + styledata.vposavailable = styledata.vposvalid = 0 + styledata.vpos[index] = None + else: + if v < - self.epsilon or v > 1 + self.epsilon: + styledata.vposvalid = 0 + styledata.vpos[index] = v + + +provider["vpos"] = provider["vposmissing"] = provider["vposavailable"] = provider["vposvalid"] = _pos() + + +class _range(_style): + + provide = ["vrange", "vrangemissing"] + + # internal bit masks + mask_value = 1 + mask_min = 2 + mask_max = 4 + mask_dmin = 8 + mask_dmax = 16 + mask_d = 32 + + def __init__(self, epsilon=1e-10): + self.epsilon = epsilon + + def columns(self, styledata, graph, columns): + def numberofbits(mask): + if not mask: + return 0 + if mask & 1: + return numberofbits(mask >> 1) + 1 + else: + return numberofbits(mask >> 1) + usecolumns = [] + styledata.rangeposcolumns = [] + styledata.vrangemissing = [] + styledata.rangeposdeltacolumns = {} # temporarily used by adjustaxis only + for count, axisnames in enumerate(graph.axesnames): + for axisname in axisnames: + mask = 0 + for column in columns: + addusecolumns = 1 + if axisname == column: + mask += self.mask_value + elif axisname + "min" == column: + mask += self.mask_min + elif axisname + "max" == column: + mask += self.mask_max + elif "d" + axisname + "min" == column: + mask += self.mask_dmin + elif "d" + axisname + "max" == column: + mask += self.mask_dmax + elif "d" + axisname == column: + mask += self.mask_d + else: + addusecolumns = 0 + if addusecolumns: + usecolumns.append(column) + if mask & (self.mask_min | self.mask_max | self.mask_dmin | self.mask_dmax | self.mask_d): + if (numberofbits(mask & (self.mask_min | self.mask_dmin | self.mask_d)) > 1 or + numberofbits(mask & (self.mask_max | self.mask_dmax | self.mask_d)) > 1): + raise ValueError("multiple errorbar definition") + if mask & (self.mask_dmin | self.mask_dmax | self.mask_d): + if not (mask & self.mask_value): + raise ValueError("missing value for delta") + styledata.rangeposdeltacolumns[axisname] = {} + styledata.rangeposcolumns.append((axisname, mask)) + elif mask == self.mask_value: + usecolumns = usecolumns[:-1] + if len(styledata.rangeposcolumns) + len(styledata.vrangemissing) > count+1: + raise ValueError("multiple axes per graph dimension") + elif len(styledata.rangeposcolumns) + len(styledata.vrangemissing) < count+1: + styledata.vrangemissing.append(count) + return usecolumns + + def adjustaxis(self, styledata, graph, column, data, index): + if column in [c + "min" for c, m in styledata.rangeposcolumns if m & self.mask_min]: + graph.axes[column[:-3]].adjustrange(data, index) + if column in [c + "max" for c, m in styledata.rangeposcolumns if m & self.mask_max]: + graph.axes[column[:-3]].adjustrange(data, index) + + # delta handling: fill rangeposdeltacolumns + if column in [c for c, m in styledata.rangeposcolumns if m & (self.mask_dmin | self.mask_dmax | self.mask_d)]: + styledata.rangeposdeltacolumns[column][self.mask_value] = data, index + if column in ["d" + c + "min" for c, m in styledata.rangeposcolumns if m & self.mask_dmin]: + styledata.rangeposdeltacolumns[column[1:-3]][self.mask_dmin] = data, index + if column in ["d" + c + "max" for c, m in styledata.rangeposcolumns if m & self.mask_dmax]: + styledata.rangeposdeltacolumns[column[1:-3]][self.mask_dmax] = data, index + if column in ["d" + c for c, m in styledata.rangeposcolumns if m & self.mask_d]: + styledata.rangeposdeltacolumns[column[1:]][self.mask_d] = data, index + + # delta handling: process rangeposdeltacolumns + for c, d in styledata.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): + graph.axes[c].adjustrange(d[self.mask_value][0], d[self.mask_value][1], + deltamindata=d[k][0], deltaminindex=d[k][1]) + if k & (self.mask_dmax | self.mask_d): + graph.axes[c].adjustrange(d[self.mask_value][0], d[self.mask_value][1], + deltamaxdata=d[k][0], deltamaxindex=d[k][1]) + del d[k] + + def initdrawpoints(self, styledata, graph): + styledata.vrange = [[None for x in range(2)] for y in styledata.rangeposcolumns + styledata.vrangemissing] + styledata.rangepostmplist = [[column, mask, index, graph.axes[column]] # temporarily used by drawpoint only + for index, (column, mask) in enumerate(styledata.rangeposcolumns)] + for missing in styledata.vrangemissing: + for rangepostmp in styledata.rangepostmplist: + if rangepostmp[2] >= missing: + rangepostmp[2] += 1 + + def drawpoint(self, styledata, graph): + for column, mask, index, axis in styledata.rangepostmplist: + try: + if mask & self.mask_min: + styledata.vrange[index][0] = axis.convert(styledata.point[column + "min"]) + if mask & self.mask_dmin: + styledata.vrange[index][0] = axis.convert(styledata.point[column] - styledata.point["d" + column + "min"]) + if mask & self.mask_d: + styledata.vrange[index][0] = axis.convert(styledata.point[column] - styledata.point["d" + column]) + except (ArithmeticError, ValueError, TypeError): + styledata.vrange[index][0] = None + try: + if mask & self.mask_max: + styledata.vrange[index][1] = axis.convert(styledata.point[column + "max"]) + if mask & self.mask_dmax: + styledata.vrange[index][1] = axis.convert(styledata.point[column] + styledata.point["d" + column + "max"]) + if mask & self.mask_d: + styledata.vrange[index][1] = axis.convert(styledata.point[column] + styledata.point["d" + column]) + except (ArithmeticError, ValueError, TypeError): + styledata.vrange[index][1] = None + + # some range checks for data consistency + if (styledata.vrange[index][0] is not None and styledata.vrange[index][1] is not None and + styledata.vrange[index][0] > styledata.vrange[index][1] + self.epsilon): + raise ValueError("negative errorbar range") + if (styledata.vrange[index][0] is not None and styledata.vpos[index] is not None and + styledata.vrange[index][0] > styledata.vpos[index] + self.epsilon): + raise ValueError("negative minimum errorbar") + if (styledata.vrange[index][1] is not None and styledata.vpos[index] is not None and + styledata.vrange[index][1] < styledata.vpos[index] - self.epsilon): + raise ValueError("negative maximum errorbar") + + +provider["vrange"] = provider["vrangemissing"] = _range() + + +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): + + need = ["vposmissing"] + + def columns(self, styledata, graph, columns): + if len(styledata.vposmissing): + raise ValueError("position columns incomplete") + return [] + + +class symbol(_styleneedingpointpos): + + need = ["vpos", "vposmissing", "vposvalid"] + + # insert symbols like staticmethods + cross = _crosssymbol + plus = _plussymbol + square = _squaresymbol + triangle = _trianglesymbol + circle = _circlesymbol + diamond = _diamondsymbol + + changecross = attr.changelist([cross, plus, square, triangle, circle, diamond]) + changeplus = attr.changelist([plus, square, triangle, circle, diamond, cross]) + changesquare = attr.changelist([square, triangle, circle, diamond, cross, plus]) + changetriangle = attr.changelist([triangle, circle, diamond, cross, plus, square]) + changecircle = attr.changelist([circle, diamond, cross, plus, square, triangle]) + changediamond = attr.changelist([diamond, cross, plus, square, triangle, circle]) + changesquaretwice = attr.changelist([square, square, triangle, triangle, circle, circle, diamond, diamond]) + changetriangletwice = attr.changelist([triangle, triangle, circle, circle, diamond, diamond, square, square]) + changecircletwice = attr.changelist([circle, circle, diamond, diamond, square, square, triangle, triangle]) + changediamondtwice = attr.changelist([diamond, diamond, square, square, triangle, triangle, circle, circle]) + + 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, styledata, graph, selectindex, selecttotal): + styledata.symbol = attr.selectattr(self.symbol, selectindex, selecttotal) + styledata.size_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal)) + if self.symbolattrs is not None: + styledata.symbolattrs = attr.selectattrs(self.defaultsymbolattrs + self.symbolattrs, selectindex, selecttotal) + else: + styledata.symbolattrs = None + + def initdrawpoints(self, styledata, graph): + styledata.symbolcanvas = graph.insert(canvas.canvas()) + + def drawpoint(self, styledata, graph): + if styledata.vposvalid and styledata.symbolattrs is not None: + xpos, ypos = graph.vpos_pt(*styledata.vpos) + styledata.symbol(styledata.symbolcanvas, xpos, ypos, styledata.size_pt, styledata.symbolattrs) + + def key_pt(self, styledata, graph, x_pt, y_pt, width_pt, height_pt): + if styledata.symbolattrs is not None: + styledata.symbol(graph, x_pt+0.5*width_pt, y_pt+0.5*height_pt, styledata.size_pt, styledata.symbolattrs) + + +class line(_styleneedingpointpos): + + need = ["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, styledata, graph, selectindex, selecttotal): + styledata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal) + + def initdrawpoints(self, styledata, graph): + styledata.linecanvas = graph.insert(canvas.canvas()) + if styledata.lineattrs is not None: + styledata.linecanvas.set(styledata.lineattrs) + styledata.path = path.path() + styledata.linebasepoints = [] + styledata.lastvpos = None + + def addpointstopath(self, styledata): + # add baselinepoints to styledata.path + if len(styledata.linebasepoints) > 1: + styledata.path.append(path.moveto_pt(*styledata.linebasepoints[0])) + if len(styledata.linebasepoints) > 2: + styledata.path.append(path.multilineto_pt(styledata.linebasepoints[1:])) + else: + styledata.path.append(path.lineto_pt(*styledata.linebasepoints[1])) + styledata.linebasepoints = [] + + def drawpoint(self, styledata, graph): + # append linebasepoints + if styledata.vposavailable: + if len(styledata.linebasepoints): + # the last point was inside the graph + if styledata.vposvalid: # shortcut for the common case + styledata.linebasepoints.append(graph.vpos_pt(*styledata.vpos)) + else: + # cut end + cut = 1 + for vstart, vend in zip(styledata.lastvpos, styledata.vpos): + newcut = None + if vend > 1: + # 1 = vstart + (vend - vstart) * cut + try: + newcut = (1 - vstart)/(vend - vstart) + except ArithmeticError: + break + if vend < 0: + # 0 = vstart + (vend - vstart) * cut + try: + newcut = - vstart/(vend - vstart) + except ArithmeticError: + break + if newcut is not None and newcut < cut: + cut = newcut + else: + cutvpos = [] + for vstart, vend in zip(styledata.lastvpos, styledata.vpos): + cutvpos.append(vstart + (vend - vstart) * cut) + styledata.linebasepoints.append(styledata.graph.vpos_pt(*cutvpos)) + self.addpointstopath(styledata) + else: + # the last point was outside the graph + if styledata.lastvpos is not None: + if styledata.vposvalid: + # cut beginning + cut = 0 + for vstart, vend in zip(styledata.lastvpos, styledata.vpos): + newcut = None + if vstart > 1: + # 1 = vstart + (vend - vstart) * cut + try: + newcut = (1 - vstart)/(vend - vstart) + except ArithmeticError: + break + if vstart < 0: + # 0 = vstart + (vend - vstart) * cut + try: + newcut = - vstart/(vend - vstart) + except ArithmeticError: + break + if newcut is not None and newcut > cut: + cut = newcut + else: + cutvpos = [] + for vstart, vend in zip(styledata.lastvpos, styledata.vpos): + cutvpos.append(vstart + (vend - vstart) * cut) + styledata.linebasepoints.append(graph.vpos_pt(*cutvpos)) + styledata.linebasepoints.append(graph.vpos_pt(*styledata.vpos)) + else: + # sometimes cut beginning and end + cutfrom = 0 + cutto = 1 + for vstart, vend in zip(styledata.lastvpos, styledata.vpos): + newcutfrom = None + if vstart > 1: + if vend > 1: + break + # 1 = vstart + (vend - vstart) * cutfrom + try: + newcutfrom = (1 - vstart)/(vend - vstart) + except ArithmeticError: + break + if vstart < 0: + if vend < 0: + break + # 0 = vstart + (vend - vstart) * cutfrom + try: + newcutfrom = - vstart/(vend - vstart) + except ArithmeticError: + 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: + break + if vend < 0: + # 0 = vstart + (vend - vstart) * cutto + try: + newcutto = - vstart/(vend - vstart) + except ArithmeticError: + break + if newcutto is not None and newcutto < cutto: + cutto = newcutto + else: + if cutfrom < cutto: + cutfromvpos = [] + cuttovpos = [] + for vstart, vend in zip(styledata.lastvpos, styledata.vpos): + cutfromvpos.append(vstart + (vend - vstart) * cutfrom) + cuttovpos.append(vstart + (vend - vstart) * cutto) + styledata.linebasepoints.append(styledata.graph.vpos_pt(*cutfromvpos)) + styledata.linebasepoints.append(styledata.graph.vpos_pt(*cuttovpos)) + self.addpointstopath(styledata) + styledata.lastvpos = styledata.vpos[:] + else: + if len(styledata.linebasepoints) > 1: + self.addpointstopath(styledata) + styledata.lastvpos = None + + def donedrawpoints(self, styledata, graph): + if len(styledata.linebasepoints) > 1: + self.addpointstopath(styledata) + if styledata.lineattrs is not None and len(styledata.path.path): + styledata.linecanvas.stroke(styledata.path) + + def key_pt(self, styledata, graph, x_pt, y_pt, width_pt, height_pt): + if styledata.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), styledata.lineattrs) + + +class errorbar(_style): + + need = ["vpos", "vposmissing", "vposavailable", "vposvalid", "vrange", "vrangemissing"] + + defaulterrorbarattrs = [] + + def __init__(self, size=0.1*unit.v_cm, + errorbarattrs=[], + epsilon=1e-10): + self.size = size + self.errorbarattrs = errorbarattrs + self.epsilon = epsilon + + def columns(self, styledata, graph, columns): + for i in styledata.vposmissing: + if i in styledata.vrangemissing: + raise ValueError("position and range for a graph dimension missing") + return [] + + def selectstyle(self, styledata, graph, selectindex, selecttotal): + styledata.errorsize_pt = unit.topt(attr.selectattr(self.size, selectindex, selecttotal)) + styledata.errorbarattrs = attr.selectattrs(self.defaulterrorbarattrs + self.errorbarattrs, selectindex, selecttotal) + + def initdrawpoints(self, styledata, graph): + styledata.errorbarcanvas = graph.insert(canvas.canvas()) + if styledata.errorbarattrs is not None: + styledata.errorbarcanvas.set(styledata.errorbarattrs) + styledata.dimensionlist = range(len(styledata.vpos)) + + def drawpoint(self, styledata, graph): + if styledata.errorbarattrs is None: + return + for i in styledata.dimensionlist: + for j in styledata.dimensionlist: + if (i != j and + (styledata.vpos[j] is None or + styledata.vpos[j] < -self.epsilon or + styledata.vpos[j] > 1+self.epsilon)): + break + else: + if ((styledata.vrange[i][0] is None and styledata.vpos[i] is None) or + (styledata.vrange[i][1] is None and styledata.vpos[i] is None) or + (styledata.vrange[i][0] is None and styledata.vrange[i][1] is None)): + continue + vminpos = styledata.vpos[:] + if styledata.vrange[i][0] is not None: + vminpos[i] = styledata.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 = styledata.vpos[:] + if styledata.vrange[i][1] is not None: + vmaxpos[i] = styledata.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 + styledata.errorbarcanvas.stroke(graph.vgeodesic(*(vminpos + vmaxpos))) + for j in styledata.dimensionlist: + if i != j: + if mincap: + styledata.errorbarcanvas.stroke(graph.vcap_pt(j, styledata.errorsize_pt, *vminpos)) + if maxcap: + styledata.errorbarcanvas.stroke(graph.vcap_pt(j, styledata.errorsize_pt, *vmaxpos)) + + +# not yet ported to the new style scheme +# +# class text(symbol): +# +# defaulttextattrs = [textmodule.halign.center, textmodule.vshift.mathaxis] +# +# def __init__(self, textdx=0*unit.v_cm, textdy=0.3*unit.v_cm, textattrs=[], **kwargs): +# self.textdx = textdx +# self.textdy = textdy +# self.textattrs = textattrs +# symbol.__init__(self, **kwargs) +# +# def setdata(self, graph, columns, styledata): +# columns = columns.copy() +# styledata.textindex = columns["text"] +# del columns["text"] +# return symbol.setdata(self, graph, columns, styledata) +# +# def selectstyle(self, selectindex, selecttotal, styledata): +# if self.textattrs is not None: +# styledata.textattrs = attr.selectattrs(self.defaulttextattrs + self.textattrs, selectindex, selecttotal) +# else: +# styledata.textattrs = None +# symbol.selectstyle(self, selectindex, selecttotal, styledata) +# +# def drawsymbol_pt(self, c, x, y, styledata, point=None): +# symbol.drawsymbol_pt(self, c, x, y, styledata, point) +# if None not in (x, y, point[styledata.textindex]) and styledata.textattrs is not None: +# c.text_pt(x + styledata.textdx_pt, y + styledata.textdy_pt, str(point[styledata.textindex]), styledata.textattrs) +# +# def drawpoints(self, points, graph, styledata): +# styledata.textdx_pt = unit.topt(self.textdx) +# styledata.textdy_pt = unit.topt(self.textdy) +# symbol.drawpoints(self, points, graph, styledata) +# +# +# class arrow(_style): +# +# defaultlineattrs = [] +# defaultarrowattrs = [] +# +# def __init__(self, linelength=0.25*unit.v_cm, arrowsize=0.15*unit.v_cm, lineattrs=[], arrowattrs=[], epsilon=1e-10): +# self.linelength = linelength +# self.arrowsize = arrowsize +# self.lineattrs = lineattrs +# self.arrowattrs = arrowattrs +# self.epsilon = epsilon +# +# def setdata(self, graph, columns, styledata): +# if len(graph.axisnames) != 2: +# raise TypeError("arrow style restricted on two-dimensional graphs") +# columns = columns.copy() +# styledata.xaxis, styledata.xindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % graph.axisnames[0])) +# styledata.yaxis, styledata.yindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % graph.axisnames[1])) +# styledata.sizeindex = columns["size"] +# del columns["size"] +# styledata.angleindex = columns["angle"] +# del columns["angle"] +# return columns +# +# def adjustaxes(self, points, columns, styledata): +# if styledata.xindex in columns: +# styledata.xaxis.adjustrange(points, styledata.xindex) +# if styledata.yindex in columns: +# styledata.yaxis.adjustrange(points, styledata.yindex) +# +# def selectstyle(self, selectindex, selecttotal, styledata): +# if self.lineattrs is not None: +# styledata.lineattrs = attr.selectattrs(self.defaultlineattrs + self.lineattrs, selectindex, selecttotal) +# else: +# styledata.lineattrs = None +# if self.arrowattrs is not None: +# styledata.arrowattrs = attr.selectattrs(self.defaultarrowattrs + self.arrowattrs, selectindex, selecttotal) +# else: +# styledata.arrowattrs = None +# +# def drawpoints(self, points, graph, styledata): +# if styledata.lineattrs is not None and styledata.arrowattrs is not None: +# linelength_pt = unit.topt(self.linelength) +# for point in points: +# xpos, ypos = graph.pos_pt(point[styledata.xindex], point[styledata.yindex], xaxis=styledata.xaxis, yaxis=styledata.yaxis) +# if point[styledata.sizeindex] > self.epsilon: +# dx = math.cos(point[styledata.angleindex]*math.pi/180) +# dy = math.sin(point[styledata.angleindex]*math.pi/180) +# x1 = xpos-0.5*dx*linelength_pt*point[styledata.sizeindex] +# y1 = ypos-0.5*dy*linelength_pt*point[styledata.sizeindex] +# x2 = xpos+0.5*dx*linelength_pt*point[styledata.sizeindex] +# y2 = ypos+0.5*dy*linelength_pt*point[styledata.sizeindex] +# graph.stroke(path.line_pt(x1, y1, x2, y2), styledata.lineattrs + +# [deco.earrow(styledata.arrowattrs, size=self.arrowsize*point[styledata.sizeindex])]) +# +# +# class rect(_style): +# +# def __init__(self, palette=color.palette.Gray): +# self.palette = palette +# +# def setdata(self, graph, columns, styledata): +# if len(graph.axisnames) != 2: +# raise TypeError("arrow style restricted on two-dimensional graphs") +# columns = columns.copy() +# styledata.xaxis, styledata.xminindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)min$" % graph.axisnames[0])) +# styledata.yaxis, styledata.yminindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)min$" % graph.axisnames[1])) +# xaxis, styledata.xmaxindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)max$" % graph.axisnames[0])) +# yaxis, styledata.ymaxindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)max$" % graph.axisnames[1])) +# if xaxis != styledata.xaxis or yaxis != styledata.yaxis: +# raise ValueError("min/max values should use the same axes") +# styledata.colorindex = columns["color"] +# del columns["color"] +# return columns +# +# def selectstyle(self, selectindex, selecttotal, styledata): +# pass +# +# def adjustaxes(self, points, columns, styledata): +# if styledata.xminindex in columns: +# styledata.xaxis.adjustrange(points, styledata.xminindex) +# if styledata.xmaxindex in columns: +# styledata.xaxis.adjustrange(points, styledata.xmaxindex) +# if styledata.yminindex in columns: +# styledata.yaxis.adjustrange(points, styledata.yminindex) +# if styledata.ymaxindex in columns: +# styledata.yaxis.adjustrange(points, styledata.ymaxindex) +# +# def drawpoints(self, points, graph, styledata): +# # TODO: bbox shortcut +# c = graph.insert(canvas.canvas()) +# lastcolorvalue = None +# for point in points: +# try: +# xvmin = styledata.xaxis.convert(point[styledata.xminindex]) +# xvmax = styledata.xaxis.convert(point[styledata.xmaxindex]) +# yvmin = styledata.yaxis.convert(point[styledata.yminindex]) +# yvmax = styledata.yaxis.convert(point[styledata.ymaxindex]) +# colorvalue = point[styledata.colorindex] +# if colorvalue != lastcolorvalue: +# color = self.palette.getcolor(point[styledata.colorindex]) +# except: +# continue +# if ((xvmin < 0 and xvmax < 0) or (xvmin > 1 and xvmax > 1) or +# (yvmin < 0 and yvmax < 0) or (yvmin > 1 and yvmax > 1)): +# continue +# if xvmin < 0: +# xvmin = 0 +# elif xvmin > 1: +# xvmin = 1 +# if xvmax < 0: +# xvmax = 0 +# elif xvmax > 1: +# xvmax = 1 +# if yvmin < 0: +# yvmin = 0 +# elif yvmin > 1: +# yvmin = 1 +# if yvmax < 0: +# yvmax = 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()) +# if colorvalue != lastcolorvalue: +# c.set([color]) +# c.fill(p) +# +# +# class bar(_style): +# +# defaultfrompathattrs = [] +# defaultbarattrs = [color.palette.Rainbow, deco.stroked([color.gray.black])] +# +# def __init__(self, fromvalue=None, frompathattrs=[], barattrs=[], subnames=None, epsilon=1e-10): +# self.fromvalue = fromvalue +# self.frompathattrs = frompathattrs +# self.barattrs = barattrs +# self.subnames = subnames +# self.epsilon = epsilon +# +# def setdata(self, graph, columns, styledata): +# # TODO: remove limitation to 2d graphs +# if len(graph.axisnames) != 2: +# raise TypeError("arrow style currently restricted on two-dimensional graphs") +# columns = columns.copy() +# xvalue = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % graph.axisnames[0])) +# yvalue = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)$" % graph.axisnames[1])) +# if (xvalue is None and yvalue is None) or (xvalue is not None and yvalue is not None): +# raise TypeError("must specify exactly one value axis") +# if xvalue is not None: +# styledata.valuepos = 0 +# styledata.nameaxis, styledata.nameindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)name$" % graph.axisnames[1])) +# styledata.valueaxis = xvalue[0] +# styledata.valueindices = [xvalue[1]] +# else: +# styledata.valuepos = 1 +# styledata.nameaxis, styledata.nameindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)name$" % graph.axisnames[0])) +# styledata.valueaxis = yvalue[0] +# styledata.valueindices = [yvalue[1]] +# i = 1 +# while 1: +# try: +# valueaxis, valueindex = _style.setdatapattern(self, graph, columns, re.compile(r"(%s([2-9]|[1-9][0-9]+)?)stack%i$" % (graph.axisnames[styledata.valuepos], i))) +# except: +# break +# if styledata.valueaxis != valueaxis: +# raise ValueError("different value axes for stacked bars") +# styledata.valueindices.append(valueindex) +# i += 1 +# return columns +# +# def selectstyle(self, selectindex, selecttotal, styledata): +# if selectindex: +# styledata.frompathattrs = None +# else: +# styledata.frompathattrs = self.defaultfrompathattrs + self.frompathattrs +# if selecttotal > 1: +# if self.barattrs is not None: +# styledata.barattrs = attr.selectattrs(self.defaultbarattrs + self.barattrs, selectindex, selecttotal) +# else: +# styledata.barattrs = None +# else: +# styledata.barattrs = self.defaultbarattrs + self.barattrs +# styledata.selectindex = selectindex +# styledata.selecttotal = selecttotal +# if styledata.selecttotal != 1 and self.subnames is not None: +# raise ValueError("subnames not allowed when iterating over bars") +# +# def adjustaxes(self, points, columns, styledata): +# if styledata.nameindex in columns: +# if styledata.selecttotal == 1: +# styledata.nameaxis.adjustrange(points, styledata.nameindex, subnames=self.subnames) +# else: +# for i in range(styledata.selecttotal): +# styledata.nameaxis.adjustrange(points, styledata.nameindex, subnames=[i]) +# for valueindex in styledata.valueindices: +# if valueindex in columns: +# styledata.valueaxis.adjustrange(points, valueindex) +# +# def drawpoints(self, points, graph, styledata): +# if self.fromvalue is not None: +# vfromvalue = styledata.valueaxis.convert(self.fromvalue) +# if vfromvalue < -self.epsilon: +# vfromvalue = 0 +# if vfromvalue > 1 + self.epsilon: +# vfromvalue = 1 +# if styledata.frompathattrs is not None and vfromvalue > self.epsilon and vfromvalue < 1 - self.epsilon: +# if styledata.valuepos: +# p = graph.vgeodesic(0, vfromvalue, 1, vfromvalue) +# else: +# p = graph.vgeodesic(vfromvalue, 0, vfromvalue, 1) +# graph.stroke(p, styledata.frompathattrs) +# else: +# vfromvalue = 0 +# l = len(styledata.valueindices) +# if l > 1: +# barattrslist = [] +# for i in range(l): +# barattrslist.append(attr.selectattrs(styledata.barattrs, i, l)) +# else: +# barattrslist = [styledata.barattrs] +# for point in points: +# vvaluemax = vfromvalue +# for valueindex, barattrs in zip(styledata.valueindices, barattrslist): +# vvaluemin = vvaluemax +# try: +# vvaluemax = styledata.valueaxis.convert(point[valueindex]) +# except: +# continue +# +# if styledata.selecttotal == 1: +# try: +# vnamemin = styledata.nameaxis.convert((point[styledata.nameindex], 0)) +# except: +# continue +# try: +# vnamemax = styledata.nameaxis.convert((point[styledata.nameindex], 1)) +# except: +# continue +# else: +# try: +# vnamemin = styledata.nameaxis.convert((point[styledata.nameindex], styledata.selectindex, 0)) +# except: +# continue +# try: +# vnamemax = styledata.nameaxis.convert((point[styledata.nameindex], styledata.selectindex, 1)) +# except: +# continue +# +# if styledata.valuepos: +# p = graph.vgeodesic(vnamemin, vvaluemin, vnamemin, vvaluemax) +# p.append(graph.vgeodesic_el(vnamemin, vvaluemax, vnamemax, vvaluemax)) +# p.append(graph.vgeodesic_el(vnamemax, vvaluemax, vnamemax, vvaluemin)) +# p.append(graph.vgeodesic_el(vnamemax, vvaluemin, vnamemin, vvaluemin)) +# p.append(path.closepath()) +# else: +# p = graph.vgeodesic(vvaluemin, vnamemin, vvaluemin, vnamemax) +# p.append(graph.vgeodesic_el(vvaluemin, vnamemax, vvaluemax, vnamemax)) +# p.append(graph.vgeodesic_el(vvaluemax, vnamemax, vvaluemax, vnamemin)) +# p.append(graph.vgeodesic_el(vvaluemax, vnamemin, vvaluemin, vnamemin)) +# p.append(path.closepath()) +# if barattrs is not None: +# graph.fill(p, barattrs) +# +# def key_pt(self, c, x_pt, y_pt, width_pt, height_pt, styledata): +# l = len(styledata.valueindices) +# if l > 1: +# for i in range(l): +# c.fill(path.rect_pt(x_pt+i*width_pt/l, y_pt, width_pt/l, height_pt), attr.selectattrs(styledata.barattrs, i, l)) +# else: +# c.fill(path.rect_pt(x_pt, y_pt, width_pt, height_pt), styledata.barattrs) diff --git a/test/functional/test_graph.py b/test/functional/test_graph.py index 857c057e..d315d7ee 100755 --- a/test/functional/test_graph.py +++ b/test/functional/test_graph.py @@ -21,7 +21,7 @@ def test_multiaxes_data(c, x, y): graph.data.file("data/testdata", x=1, y2=4), graph.data.file("data/testdata", x=1, y3=5, title=None), graph.data.file("data/testdata", x=1, y5=6)), - styles=[graph.style.pointpos(), graph.style.symbol(symbolattrs=[deco.stroked.clear, color.palette.RedGreen, graph.style.symbol.changestrokedfilled], symbol=graph.style.symbol.changesquaretwice)]) + styles=[graph.style.symbol(symbolattrs=[deco.stroked.clear, color.palette.RedGreen, graph.style.symbol.changestrokedfilled], symbol=graph.style.symbol.changesquaretwice)]) g.finish() def test_piaxis_function(c, x, y): @@ -29,16 +29,16 @@ def test_piaxis_function(c, x, y): g = c.insert(graph.graphxy(x, y, height=5, x=xaxis)) # g = c.insert(graph.graphxy(x, y, height=5, x=xaxis, x2=xaxis)) # TODO g.plot([graph.data.function("y=sin(x-i*pi/10)", context={"i": i}) for i in range(20)], - styles=[graph.style.pointpos(), graph.style.line(lineattrs=[color.palette.Hue])]) + styles=[graph.style.line(lineattrs=[color.palette.Hue])]) g.finish() def test_textaxis_errorbars(c, x, y): g = c.insert(graph.graphxy(x, y, height=5, x=graph.axis.lin(min=0.5, max=12.5, parter=graph.axis.parter.lin("1", extendtick=None)), - y=graph.axis.lin(min=-10, max=30, title="Temperature [$^\circ$C]"), - x2=graph.axis.lin(), y2=graph.axis.lin())) - g.plot(graph.data.file("data/testdata2", x=0, ymin="min", ymax="max")) - g.plot(graph.data.paramfunction("k", 0, 2*math.pi, "x2, y2, dx2, dy2 = 0.8*sin(k), 0.8*cos(3*k), 0.05, 0.05"), style = graph.style.symbol(symbol=graph.style.symbol.triangle)) + y=graph.axis.lin(title="Temperature [$^\circ$C]"), + ))#x2=graph.axis.lin(), y2=graph.axis.lin())) + g.plot(graph.data.file("data/testdata2", x=0, ymin="min", ymax="max"), [graph.style.errorbar()]) + #g.plot(graph.data.paramfunction("k", 0, 2*math.pi, "x2, y2, dx2, dy2 = 0.8*sin(k), 0.8*cos(3*k), 0.05, 0.05"), [graph.style.pointpos(), graph.style.rangepos(), graph.style.symbol(symbol=graph.style.symbol.triangle), graph.style.errorbar()]) g.finish() def test_ownmark(c, x, y): @@ -87,8 +87,8 @@ def test_split2(c, x, y): c = canvas.canvas() test_multiaxes_data(c, 0, 21) -test_piaxis_function(c, 0, 14) -#test_textaxis_errorbars(c, 0, 7) +#test_piaxis_function(c, 0, 14) +test_textaxis_errorbars(c, 0, 7) #test_ownmark(c, 0, 0) #test_allerrorbars(c, -7, 0) #test_split(c, -7, 7) -- 2.11.4.GIT