From: <jd...@us...> - 2007-07-20 03:37:02
|
Revision: 3581 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3581&view=rev Author: jdh2358 Date: 2007-07-19 20:36:59 -0700 (Thu, 19 Jul 2007) Log Message: ----------- added mpl like line and rect artists to sketch Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-20 02:10:43 UTC (rev 3580) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-20 03:36:59 UTC (rev 3581) @@ -252,7 +252,7 @@ raise NotImplementedError -mtraits.Model = traits.Trait(Identity(), Polar()) +mtraits.Model = traits.Trait(None, Identity, Polar) @@ -265,12 +265,12 @@ MOVETO, LINETO, CLOSEPOLY = range(3) strokecolor = mtraits.Color('black') - fillcolor = mtraits.Color('blue') - alpha = mtraits.Alpha(1.0) - linewidth = mtraits.LineWidth(1.0) + fillcolor = mtraits.Color('blue') + alpha = mtraits.Alpha(1.0) + linewidth = mtraits.LineWidth(1.0) antialiased = mtraits.FlexibleTrueTrait() - pathdata = mtraits.PathData() - affine = mtraits.Affine() + pathdata = mtraits.PathData() + affine = mtraits.Affine() def __init__(self): @@ -279,11 +279,10 @@ # instances, which is not what I want self.strokecolor = 'black' self.fillcolor = 'blue' - self.pathdata = (npy.array([0,0], npy.uint8), # codes - npy.array([[0,0], [0,0]])) # verts self.affine = affine_identity() - - + self.pathdata = (npy.array([0,0], npy.uint8), # codes + npy.array([[0,0], [0,0]])) # verts + mtraits.Path = traits.Trait(Path()) class AggPath(Path): @@ -350,10 +349,10 @@ return agg.rgba8(*rgba) class Markers(traits.HasTraits): - verts = mtraits.Verts() # locations to draw the markers at - path = mtraits.Path() # marker path in points + verts = mtraits.Verts() # locations to draw the markers at + path = mtraits.Path() # marker path in points affine = mtraits.Affine() # transformation for the verts - x = traits.Float(1.0) + x = traits.Float(1.0) def __init__(self): # this is a quick workaround to prevent sharing obs; see Path @@ -405,10 +404,10 @@ artistID = IDGenerator() class Artist(traits.HasTraits): - zorder = traits.Float(1.0) - alpha = mtraits.Alpha(1.0) + zorder = traits.Float(1.0) + alpha = mtraits.Alpha(1.0) visible = mtraits.FlexibleTrueTrait() - affine = mtraits.Affine() + affine = mtraits.Affine() def __init__(self): self.artistid = artistID() @@ -439,8 +438,8 @@ path = mtraits.Path() markers = mtraits.Markers() X = mtraits.Verts() - model = mtraits.Model(Identity()) - + model = mtraits.Model + zorder = traits.Float(2.0) def __init__(self): """ @@ -449,6 +448,19 @@ """ Artist.__init__(self) + # this is potentially a big problem because you have to know + # which attrs may be shared and hence have to be initialized + # and which ones don't. Eg, if you comment out the self.path + # init, the code breaks + self.color = 'blue' + self.markerfacecolor = 'blue' + self.markeredgecolor = 'black' + self.path = Path() + self.markers = Markers() + self.X = npy.array([[0,1], [0,1]], npy.float_) + self.model = Identity() + #self.model = None # switch comments with above to reveal bug + self.sync_trait('linewidth', self.path, 'linewidth', mutual=False) self.sync_trait('color', self.path, 'strokecolor', mutual=False) self.sync_trait('markerfacecolor', self.markers.path, 'fillcolor', mutual=False) @@ -494,11 +506,15 @@ codes = Path.LINETO*npy.ones(N, dtype=npy.uint8) codes[0] = Path.MOVETO - modelx = self.model(newx) + # todo, having touble setting Model to default to Identity so + # allowing None as a hack workaround + if self.model is not None: + modelx = self.model(newx) + else: + modelx = newx self.path.pathdata = codes, modelx self.markers.verts = modelx - def _markersize_changed(self, oldX, newX): self._refresh_markers() @@ -526,25 +542,27 @@ facecolor = mtraits.Color('Yellow') edgecolor = mtraits.Color('Black') edgewidth = mtraits.LineWidth(1.0) - lbwh = traits.Array('d', (4,), [0,0,1,1]) - path = mtraits.Path() - + lbwh = traits.Array('d', (4,), [0,0,1,1]) + path = mtraits.Path() + zorder = traits.Float(1.0) + def __init__(self): + Artist.__init__(self) self.facecolor = 'yellow' self.edgecolor = 'black' self.edgewidth = 1.0 self.lbwh = 0,0,1,1 self.path = Path() - self.sync_trait('facecolor', self, 'fillcolor', True) - self.sync_trait('edgecolor', self, 'strokecolor', True) - self.sync_trait('edgewidth', self, 'linewidth', True) - self.sync_trait('affine', self.markers) + self.sync_trait('facecolor', self.path, 'fillcolor', mutual=False) + self.sync_trait('edgecolor', self.path, 'strokecolor', mutual=False) + self.sync_trait('edgewidth', self.path, 'linewidth', mutual=False) + self.sync_trait('affine', self.path, mutual=False) self.pathid = primitiveID() - + + def _lbwh_changed(self, old, new): - print 'lbwh changed' l,b,w,h = new t = b+h r = l+w @@ -567,7 +585,6 @@ raise RuntimeError('First call set_renderer') if not self.visible: return - self.renderer.render_path(self.pathid) class Figure: @@ -593,11 +610,11 @@ class AxesCoords(traits.HasTraits): - xviewlim = mtraits.Interval() - yviewlim = mtraits.Interval() + xviewlim = mtraits.Interval() + yviewlim = mtraits.Interval() affineview = mtraits.Affine() affineaxes = mtraits.Affine() - affine = mtraits.Affine() + affine = mtraits.Affine() def __init__(self): self.xviewlim = npy.array([0., 1.]) @@ -630,7 +647,7 @@ self.affine = npy.dot(self.affineaxes, self.affineview) -x1 = npy.arange(0, 10., 0.1) +x1 = npy.arange(0, 10., 0.05) x2 = npy.arange(0, 10., 0.1) y1 = npy.cos(2*npy.pi*x1) y2 = 10*npy.exp(-x1) @@ -646,26 +663,26 @@ line1.X = npy.array([x1,y1]).T line1.setp(color='blue', linewidth=2.0, marker='s', markersize=5.0, - markerfacecolor='green', markeredgewidth=0.5) + markerfacecolor='green', markeredgewidth=0.5) coords1.sync_trait('affine', line1, mutual=False) fig.artistd[line1.artistid] = line1 + rect1 = Rectangle() rect1.lbwh = [0,0,1,1] rect1.facecolor = 'white' fig.artistd[rect1.artistid] = rect1 +coords1.sync_trait('affineaxes', rect1, 'affine', mutual=False) -#coords1.sync_trait('affineaxes', rect1, 'affine') - # update the view limits, all the affines should be automagically updated coords1.xviewlim = 0, 10 coords1.yviewlim = -1.1, 1.1 -if 0: - # the axes rectangle + +if 1: axrect2 = [0.55, 0.55, 0.4, 0.4] coords2 = AxesCoords() coords2.affineaxes = affine_axes(axrect2) @@ -673,16 +690,22 @@ r = npy.arange(0.0, 1.0, 0.01) theta = r*4*npy.pi + + line2 = Line() + line2.model = Polar() + line2.setp(color='#ee8d18', linewidth=2.0) + line2.X = npy.array([r, theta]).T + coords2.sync_trait('affine', line2, mutual=False) - line2 = Line(r, theta, model=Polar(), color='#ee8d18', linewidth=2.0) - rect2 = Rectangle([0,0,1,1], facecolor='#d5de9c') - coords2.sync_trait('affine', line2, mutual=False) + rect2 = Rectangle() + rect2.lbwh = [0,0,1,1] + rect2.facecolor = '#d5de9c' coords2.sync_trait('affineaxes', rect2, 'affine', mutual=False) - fig.add_path(rect2) - fig.add_path(line2) + fig.artistd[line2.artistid] = line2 + fig.artistd[rect2.artistid] = rect2 - # update the view limits, all the affines should be automagically updated + coords2.xviewlim = -1.1, 1.1 coords2.yviewlim = -1.1, 1.1 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-07-20 13:46:26
|
Revision: 3587 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3587&view=rev Author: jdh2358 Date: 2007-07-20 06:46:20 -0700 (Fri, 20 Jul 2007) Log Message: ----------- traits cleanup Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-20 13:44:10 UTC (rev 3586) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-20 13:46:20 UTC (rev 3587) @@ -1,3 +1,22 @@ +""" +Install instructions for traits 2.0 + + # blow away old enthought + rm -rf ~/dev/lib/python2.4/site-packages/enthought.* + + # get easy_install, if necessary + wget http://peak.telecommunity.com/dist/ez_setup.py + sudo python sez_setup.py + + sudo easy_install -f http://code.enthought.com/enstaller/eggs/source/unstable "enthought.etsconfig < 3.0a" "enthought.util <3.0a" "enthought.debug <3.0a" + + svn co https://svn.enthought.com/svn/enthought/branches/enthought.traits_2.0 enthought_traits + + cd enthought_traits/ + sudo python setup.py install + +""" + # see install instructions for enthrought traits2 in mtraits import enthought.traits.api as traits @@ -7,6 +26,22 @@ class ColorHandler(traits.TraitHandler): + """ + This is a clever little traits mechanism -- users can specify the + color as any mpl color, and the traited object will keep the + original color, but will add a new attribute with a '_' postfix + which is the color rgba tuple. + + Eg + + class C(HasTraits): + fillcolor = traits.Trait('black', ColorHandler()) + + c = C() + c.fillcolor = 'red' + print c.fillcolor + print c.fillcolor_ # + """ is_mapped = True def post_setattr(self, object, name, value): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-07-20 15:17:39
|
Revision: 3591 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3591&view=rev Author: jdh2358 Date: 2007-07-20 08:17:37 -0700 (Fri, 20 Jul 2007) Log Message: ----------- affines are first class objects Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-20 14:19:48 UTC (rev 3590) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-20 15:17:37 UTC (rev 3591) @@ -22,9 +22,89 @@ from matplotlib import agg from matplotlib import colors as mcolors +from matplotlib import cbook import numpy as npy +is_string_like = cbook.is_string_like + + +class Affine(traits.HasTraits): + """ + An affine 3x3 matrix that supports matrix multiplication with + other Affine instances or numpy arrays. + + a = Affine() + a.translate = 10,20 + a.scale = 20, 40 + + Be careful not to do *inplace* operations on the array components + or the update callbacks will not be triggered, eg DO NOT + + a.translate += 10, 20 + + rather DO + + a.translate_delta(10, 20) + """ + data = traits.Array('d', (3,3), npy.array([[1,0,0],[0,1,0],[0,0,1]], npy.float_)) + + translate = traits.Array('d', (2,), [0,0]) + scale = traits.Array('d', (2,), [0,0]) + vec6 = traits.Array('d', (6,), [1,0,0,1,0,0]) + + def translate_delta(self, tx, ty): + oldtx, oldty = self.translate + self.translate = oldtx + tx, oldty + ty + + def _translate_changed(self, old, new): + #print 'translate change', new + tx, ty = new + self.data[0][-1] = tx + self.data[1][-1] = ty + self.vec6[-2:] = tx, ty + + def _vec6_changed(self, old, new): + #print 'vec6 change', new + sx, b, c, sy, tx, ty = new + self.data[0] = sx, b, tx + self.data[1] = c, sy, ty + self.translate = tx, ty + self.scale = sx, sy + + def _scale_changed(self, old, new): + #print 'scale change', new + sx, sy = new + self.data[0][0] = sx + self.data[1][1] = sy + + + def _data_changed(self, old, new): + #print 'data change', new + + sx, b, tx = self.data[0] + c, sy, ty = self.data[1] + + self.translate = tx, ty + self.scale = sx, sy + self.vec6 = sx, b, c, sy, tx, ty + + + def __mul__(self, other): + if isinstance(other, Affine): + new = Affine() + new.data = npy.dot(self.data, other.data) + return new + elif isinstance(other, npy.ndarray): + return npy.dot(self.data, other) + raise TypeError('Do not know how to multiply Affine by %s'%type(other)) + + + def __repr__(self): + + return 'AFFINE:\n%s'%self.data + + class ColorHandler(traits.TraitHandler): """ This is a clever little traits mechanism -- users can specify the @@ -39,8 +119,8 @@ c = C() c.fillcolor = 'red' - print c.fillcolor - print c.fillcolor_ # + print c.fillcolor # prints red + print c.fillcolor_ # print (1,0,0,1) """ is_mapped = True @@ -49,9 +129,9 @@ def mapped_value(self, value ): if value is None: return None + if is_string_like(value): value = value.lower() return mcolors.colorConverter.to_rgba(value) - - + def validate(self, object, name, value): try: self.mapped_value(value) @@ -68,8 +148,9 @@ class MTraitsNamespace: DPI = traits.Float(72.) - Affine = traits.Array('d', (3,3), npy.array([[1,0,0],[0,1,0],[0,0,1]], npy.float_)) + Alpha = traits.Range(0., 1., 0.) + Affine = traits.Trait(Affine()) AntiAliased = traits.true Codes = traits.Array('b', value=npy.array([0,0], dtype=npy.uint8)) Color = traits.Trait('black', ColorHandler()) @@ -82,54 +163,39 @@ 'p', '1', '2', '3', '4') MarkerSize = traits.Float(6) Verts = traits.Array('d', value=npy.array([[0,0],[0,0]], npy.float_)) - PathData = traits.Tuple(Codes, Verts) + PathData = traits.Tuple(Codes(), Verts()) Visible = traits.true mtraits = MTraitsNamespace() -def affine_axes(rect): - 'make an affine for a typical l,b,w,h axes rectangle' - l,b,w,h = rect - return npy.array([[w, 0, l], [0, h, b], [0, 0, 1]], dtype=npy.float_) + + + -def affine_identity(): - return npy.array([[1,0,0], - [0,1,0], - [0,0,1]], - dtype=npy.float_) -def affine_translation(tx, ty): - return npy.array([[1,0,tx], - [0,1,ty], - [0,0,1]], - dtype=npy.float_) +class Renderer(traits.HasTraits): + dpi = mtraits.DPI + size = traits.Tuple(traits.Int(600), traits.Int(400)) -def affine_rotation(theta): - a = npy.cos(theta) - b = -npy.sin(theta) - c = npy.sin(theta) - d = npy.cos(theta) - - return npy.array([[a,b,0], - [c,d,0], - [0,0,1]], - dtype=npy.float_) + affinerenderer = mtraits.Affine + def __init__(self, size=(600,400)): -class Renderer(traits.HasTraits): - dpi = traits.Float(72.) - - def __init__(self, width, height): - self.width, self.height = width, height + self.pathd = dict() # path id -> Path instance + self.markersd = dict() # path id -> Markers instance + self._size_changed(None, size) + + def _size_changed(self, old, new): + width, height = new + # almost all renderers assume 0,0 is left, upper, so we'll flip y here by default - self.affinerenderer = npy.array( - [[width, 0, 0], [0, -height, height], [0, 0, 1]], dtype=npy.float_) - self.pathd = dict() # dict mapping path id -> Path instance - self.markersd = dict() # dict mapping path id -> Markers instance + self.affinerenderer.translate = 0, height + self.affinerenderer.scale = width, -height + def add_path(self, pathid, path): self.pathd[pathid] = path @@ -156,9 +222,11 @@ blue = agg.rgba8(0,0,255,255) black = agg.rgba8(0,0,0,0) - def __init__(self, width, height): - Renderer.__init__(self, width, height) - + + def _size_changed(self, old, new): + Renderer._size_changed(self, old, new) + + width, height = self.size stride = width*4 self.buf = buf = agg.buffer(width, height, stride) @@ -198,13 +266,11 @@ render_scanlines = agg.render_scanlines_bin_rgba - affine = npy.dot(self.affinerenderer, path.affine) + affine = self.affinerenderer * path.affine #print 'display affine:\n', self.affinerenderer #print 'path affine:\n', path.affine #print 'product affine:\n', affine - a, b, tx = affine[0] - c, d, ty = affine[1] - aggaffine = agg.trans_affine(a,b,c,d,tx,ty) + aggaffine = agg.trans_affine(*affine.vec6) transpath = agg.conv_transform_path(path.agg_path, aggaffine) if path.fillcolor is not None: @@ -235,13 +301,14 @@ - affineverts = npy.dot(self.affinerenderer, markers.affine) + affineverts = self.affinerenderer * markers.affine Nmarkers = markers.verts.shape[0] Locs = npy.ones((3, Nmarkers)) Locs[0] = markers.verts[:,0] Locs[1] = markers.verts[:,1] - Locs = npy.dot(affineverts, Locs) + + Locs = affineverts * Locs dpiscale = self.dpi/72. # for some reason this is broken @@ -280,7 +347,8 @@ # we'll cheat a little and use pylab for display X = npy.fromstring(self.buf.to_string(), npy.uint8) - X.shape = self.height, self.width, 4 + width, height = self.size + X.shape = height, width, 4 if 1: import pylab fig = pylab.figure() @@ -353,22 +421,17 @@ alpha = mtraits.Alpha(1.0) linewidth = mtraits.LineWidth(1.0) antialiased = mtraits.AntiAliased - pathdata = mtraits.PathData() - affine = mtraits.Affine() + pathdata = mtraits.PathData + affine = mtraits.Affine def __init__(self): # this is a quick workaround to deal with the problem that # traits inited at the class level are shared between # instances, which is not what I want - self.strokecolor = 'black' - self.fillcolor = 'blue' - self.affine = affine_identity() self.pathdata = (npy.array([0,0], npy.uint8), # codes npy.array([[0,0], [0,0]])) # verts -mtraits.Path = traits.Trait(Path()) - class AggPath(Path): def __init__(self, path): @@ -432,20 +495,17 @@ rgba = [int(255*c) for c in color] return agg.rgba8(*rgba) + +mtraits.Path = traits.Instance(Path, ()) class Markers(traits.HasTraits): - verts = mtraits.Verts() # locations to draw the markers at - path = mtraits.Path() # marker path in points - affine = mtraits.Affine() # transformation for the verts + verts = mtraits.Verts # locations to draw the markers at + path = mtraits.Path # marker path in points + affine = mtraits.Affine # transformation for the verts x = traits.Float(1.0) - def __init__(self): - # this is a quick workaround to prevent sharing obs; see Path - self.verts = npy.array([[0,0], [0,0]], npy.float_) - self.path = Path() - self.affine = affine_identity() -mtraits.Markers = traits.Trait(Markers()) +mtraits.Markers = traits.Instance(Markers, ()) # coordinates: # # artist model : a possibly nonlinear transformation (Func instance) @@ -491,12 +551,12 @@ zorder = traits.Float(1.0) alpha = mtraits.Alpha() visible = mtraits.Visible() - affine = mtraits.Affine() + affine = mtraits.Affine def __init__(self): self.artistid = artistID() self.renderer = None - self.affine = affine_identity() + def set_renderer(self, renderer): self.renderer = renderer @@ -519,9 +579,9 @@ markeredgecolor = mtraits.Color('black') markeredgewidth = mtraits.LineWidth(0.5) markersize = mtraits.MarkerSize(6.0) - path = mtraits.Path() - markers = mtraits.Markers() - X = mtraits.Verts() + path = mtraits.Path + markers = mtraits.Markers + X = mtraits.Verts model = mtraits.Model zorder = traits.Float(2.0) @@ -536,14 +596,7 @@ # which attrs may be shared and hence have to be initialized # and which ones don't. Eg, if you comment out the self.path # init, the code breaks - self.color = 'blue' - self.markerfacecolor = 'blue' - self.markeredgecolor = 'black' - self.path = Path() - self.markers = Markers() - self.X = npy.array([[0,1], [0,1]], npy.float_) - self.model = Identity() - #self.model = None # switch comments with above to reveal bug + self.sync_trait('linewidth', self.path, 'linewidth', mutual=False) self.sync_trait('color', self.path, 'strokecolor', mutual=False) @@ -623,20 +676,16 @@ class Rectangle(Artist): - facecolor = mtraits.Color('Yellow') - edgecolor = mtraits.Color('Black') + facecolor = mtraits.Color('yellow') + edgecolor = mtraits.Color('black') edgewidth = mtraits.LineWidth(1.0) lbwh = traits.Array('d', (4,), [0,0,1,1]) - path = mtraits.Path() + path = mtraits.Path zorder = traits.Float(1.0) def __init__(self): Artist.__init__(self) - self.facecolor = 'yellow' - self.edgecolor = 'black' - self.edgewidth = 1.0 - self.lbwh = 0,0,1,1 - self.path = Path() + self.sync_trait('facecolor', self.path, 'fillcolor', mutual=False) self.sync_trait('edgecolor', self.path, 'strokecolor', mutual=False) @@ -694,41 +743,34 @@ class AxesCoords(traits.HasTraits): - xviewlim = mtraits.Interval() - yviewlim = mtraits.Interval() - affineview = mtraits.Affine() - affineaxes = mtraits.Affine() - affine = mtraits.Affine() - - def __init__(self): - self.xviewlim = npy.array([0., 1.]) - self.yviewlim = npy.array([0., 1.]) - self.affineview = affine_identity() - self.affineaxes = affine_identity() - self.affine = affine_identity() + xviewlim = mtraits.Interval + yviewlim = mtraits.Interval + affineview = mtraits.Affine + affineaxes = mtraits.Affine + affine = mtraits.Affine def _affineview_changed(self, old, new): - self.affine = npy.dot(self.affineaxes, new) + self.affine = self.affineaxes * new def _affineaxes_changed(self, old, new): - self.affine = npy.dot(new, self.affineview) + self.affine = new * self.affineview def _xviewlim_changed(self, old, new): xmin, xmax = new scale = 1./(xmax-xmin) tx = -xmin*scale - self.affineview[0][0] = scale - self.affineview[0][-1] = tx - self.affine = npy.dot(self.affineaxes, self.affineview) + self.affineview.data[0][0] = scale + self.affineview.data[0][-1] = tx + self.affine = self.affineaxes * self.affineview def _yviewlim_changed(self, old, new): ymin, ymax = new scale = 1./(ymax-ymin) ty = -ymin*scale - self.affineview[1][1] = scale - self.affineview[1][-1] = ty - self.affine = npy.dot(self.affineaxes, self.affineview) + self.affineview.data[1][1] = scale + self.affineview.data[1][-1] = ty + self.affine = self.affineaxes * self.affineview x1 = npy.arange(0, 10., 0.05) @@ -737,9 +779,10 @@ y2 = 10*npy.exp(-x1) # the axes rectangle -axrect1 = [0.1, 0.1, 0.4, 0.4] coords1 = AxesCoords() -coords1.affineaxes = affine_axes(axrect1) +coords1.affineaxes = Affine() +coords1.affineaxes.scale = 0.4, 0.4 +coords1.affineaxes.translate = 0.1, 0.1 fig = Figure() @@ -767,11 +810,12 @@ if 1: - axrect2 = [0.55, 0.55, 0.4, 0.4] coords2 = AxesCoords() - coords2.affineaxes = affine_axes(axrect2) + coords2.affineaxes = Affine() + coords2.affineaxes.scale = 0.4, 0.4 + coords2.affineaxes.translate = 0.55, 0.55 + - r = npy.arange(0.0, 1.0, 0.01) theta = r*4*npy.pi @@ -796,7 +840,7 @@ if 1: - renderer = RendererAgg(600,400) + renderer = RendererAgg() fig.set_renderer(renderer) fig.draw() renderer.show() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-07-20 23:45:26
|
Revision: 3599 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3599&view=rev Author: jdh2358 Date: 2007-07-20 16:45:24 -0700 (Fri, 20 Jul 2007) Log Message: ----------- refactored affines abd artists Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-20 23:11:17 UTC (rev 3598) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-20 23:45:24 UTC (rev 3599) @@ -46,50 +46,187 @@ rather DO a.translate_delta(10, 20) + + Multiplication works as expected: + + a1 = Affine() + a1.scale = 10, 20 + a2 = Affine() + a2.scale = 4, 5 + print a1*a2 + + x = numpy.random(3, 10) + print a1*x + + All of the translate, scale, xlim, ylim and vec6 properties are + simply views into the data matrix, and are updated by reference """ - data = traits.Array('d', (3,3), npy.array([[1,0,0],[0,1,0],[0,0,1]], npy.float_)) + # connect to the data_modified event if you want a callback + data = traits.Array('d', (3,3)) - translate = traits.Array('d', (2,), [0,0]) - scale = traits.Array('d', (2,), [0,0]) - vec6 = traits.Array('d', (6,), [1,0,0,1,0,0]) + translate = traits.Property(traits.Array('d', (2,))) + scale = traits.Property(traits.Array('d', (2,))) + vec6 = traits.Property(traits.Array('d', (6,))) - def translate_delta(self, tx, ty): - oldtx, oldty = self.translate - self.translate = oldtx + tx, oldty + ty + xlim = traits.Property(traits.Array('d', (2,))) + ylim = traits.Property(traits.Array('d', (2,))) + + #data_modified = traits.Event + + def _data_default(self): + return npy.array([[1,0,0],[0,1,0],[0,0,1]], npy.float_) + + + def _get_xlim(self): + sx, b, tx = self.data[0] + + return self._get_lim(sx, tx) + + def _set_xlim(self, xlim): + xmin, xmax = xlim + + oldsx, oldb, oldtx = self.data[0] - def _translate_changed(self, old, new): - #print 'translate change', new - tx, ty = new - self.data[0][-1] = tx - self.data[1][-1] = ty - self.vec6[-2:] = tx, ty - - def _vec6_changed(self, old, new): - #print 'vec6 change', new - sx, b, c, sy, tx, ty = new - self.data[0] = sx, b, tx - self.data[1] = c, sy, ty - self.translate = tx, ty - self.scale = sx, sy + sx = 1./(xmax-xmin) + tx = -xmin*sx + + forward = oldsx!=sx or oldtx!=tx + if forward: + old = self.data.copy() + self.data[0][0] = sx + self.data[0][-1] = tx + self._data_changed(old, self.data) + + def _get_ylim(self): + c, sy, ty = self.data[1] + + return self._get_lim(sy, ty) + + def _set_ylim(self, ylim): + ymin, ymax = ylim + + oldc, oldsy, oldty = self.data[1] - def _scale_changed(self, old, new): - #print 'scale change', new - sx, sy = new - self.data[0][0] = sx - self.data[1][1] = sy + sy = 1./(ymax-ymin) + ty = -ymin*sy + forward = oldsy!=sy or oldty!=ty + if forward: + old = self.data.copy() + self.data[1][1] = sy + self.data[1][-1] = ty + self._data_changed(old, self.data) + + def _get_translate(self): + return [self.data[0][-1], self.data[1][-1]] + + def _set_translate(self, s): + oldtx = self.data[0][-1] + oldty = self.data[1][-1] + + tx, ty = s + + forward = tx!=oldtx or ty!=oldty + if forward: + old = self.data.copy() + self.data[0][-1] = tx + self.data[1][-1] = ty + self._data_changed(old, self.data) + + def _get_scale(self): + return [self.data[0][0], self.data[1][1]] + + def _set_scale(self, s): + oldsx = self.data[0][0] + oldsy = self.data[1][1] + + sx, sy = s + + forward = sx!=oldsx or sy!=oldsy + if forward: + old = self.data.copy() + self.data[0][0] = sx + self.data[1][1] = sy + self._data_changed(old, self.data) + + def _get_vec6(self): + a,b,tx = self.data[0] + c,d,ty = self.data[1] + return [a,b,c,d,tx,ty] + + def _set_vec6(self, v): + a,b,c,d,tx,ty = v + + olda, oldb, oldtx = self.data[0] + oldc, oldd, oldty = self.data[1] + + forward = a!=olda or b!=oldb or c!=oldc or d!=oldd or tx!=oldtx or ty!=oldty + if forward: + old = self.data.copy() + self.data[0] = a,b,tx + self.data[1] = c,d,ty + self._data_changed(old, self.data) + + + def _get_lim(self, s, t): + lmin = -t/s + lmax = 1./s + lmin + return lmin, lmax + def _data_changed(self, old, new): - #print 'data change', new + # Make it known if the translate changed + oldtx, oldty = old[0][-1], old[1][-1] + tx, ty = new[0][-1], new[1][-1] - sx, b, tx = self.data[0] - c, sy, ty = self.data[1] + oldsx, oldsy = old[0][0], old[1][1] + sx, sy = new[0][0], new[1][1] - self.translate = tx, ty - self.scale = sx, sy - self.vec6 = sx, b, c, sy, tx, ty + + oldb, oldc = old[0][1], old[1][0] + b, c = new[0][1], new[1][0] + + tchanged = False + schanged = False + vchanged = False + + tchanged = oldtx!=tx or oldty!=ty + schanged = oldsx!=sx or oldsy!=sy + vchanged = tchanged or schanged or b!=oldb or c!=oldc + xchanged = oldtx!=tx or oldsx!=sx + ychanged = oldty!=ty or oldsy!=sy + if tchanged: + self.trait_property_changed('translate', [oldtx, oldty], [tx, ty]) + + if schanged: + self.trait_property_changed('scale', [oldsx, oldsy], [sx, sy]) + + if xchanged: + oldxmin, oldxmax = self._get_lim(oldsx, oldtx) + xmin, xmax = self._get_lim(sx, tx) + self.trait_property_changed('xlim', [oldxmin, oldxmax], [xmin, xmax]) + + if ychanged: + oldymin, oldymax = self._get_lim(oldsy, oldty) + ymin, ymax = self._get_lim(sy, ty) + self.trait_property_changed('ylim', [oldymin, oldymax], [ymin, ymax]) + + if vchanged: + self.trait_property_changed( + 'vec6', + [oldsx, oldb, oldc, oldsy, oldtx, oldty], + [sx, b, c, sy, tx, ty]) + + if tchanged or schanged or vchanged: + #self._data_modified = True + self.trait_property_changed('data_modified', old, new) + + def follow(self, othervec6): + self.vec6 = othervec6 + + def __mul__(self, other): if isinstance(other, Affine): new = Affine() @@ -101,9 +238,10 @@ def __repr__(self): - - return 'AFFINE:\n%s'%self.data + return 'AFFINE: %s'%', '.join([str(val) for val in self.vec6]) + #return 'AFFINE:\n%s'%self.data + class ColorHandler(traits.TraitHandler): """ @@ -169,17 +307,11 @@ mtraits = MTraitsNamespace() - - - - - - class Renderer(traits.HasTraits): dpi = mtraits.DPI size = traits.Tuple(traits.Int(600), traits.Int(400)) - affinerenderer = mtraits.Affine + adisplay = traits.Instance(Affine, ()) def __init__(self, size=(600,400)): @@ -192,12 +324,15 @@ width, height = new # almost all renderers assume 0,0 is left, upper, so we'll flip y here by default - self.affinerenderer.translate = 0, height - self.affinerenderer.scale = width, -height + self.adisplay.translate = 0, height + self.adisplay.scale = width, -height def add_path(self, pathid, path): + # todo, we could use a traits dict here + if not isinstance(path, Path): + raise TypeError('add_path takes an ID and a Path instance') self.pathd[pathid] = path def remove_path(self, pathid): @@ -205,6 +340,9 @@ del self.pathd[pathid] def add_markers(self, markersid, markers): + # todo, we could use a traits dict here + if not isinstance(markers, Markers): + raise TypeError('add_markers takes an ID and a Markers instance') self.markersd[markersid] = markers def remove_markers(self, markersid): @@ -266,8 +404,8 @@ render_scanlines = agg.render_scanlines_bin_rgba - affine = self.affinerenderer * path.affine - #print 'display affine:\n', self.affinerenderer + affine = self.adisplay * path.affine + #print 'display affine:\n', self.adisplay #print 'path affine:\n', path.affine #print 'product affine:\n', affine aggaffine = agg.trans_affine(*affine.vec6) @@ -301,7 +439,7 @@ - affineverts = self.affinerenderer * markers.affine + affineverts = self.adisplay * markers.affine Nmarkers = markers.verts.shape[0] Locs = npy.ones((3, Nmarkers)) @@ -422,7 +560,7 @@ linewidth = mtraits.LineWidth(1.0) antialiased = mtraits.AntiAliased pathdata = mtraits.PathData - affine = mtraits.Affine + affine = traits.Instance(Affine, ()) def __init__(self): @@ -451,8 +589,11 @@ path.sync_trait('linewidth', self, mutual=False) path.sync_trait('antialiased', self, mutual=False) path.sync_trait('pathdata', self, mutual=False) - path.sync_trait('affine', self, mutual=False) + path.on_trait_change(self.affine.follow, 'vec6') + + + # hmm, I would have thought these would be called by the attr # setting above self._pathdata_changed(None, self.pathdata) @@ -500,10 +641,10 @@ class Markers(traits.HasTraits): verts = mtraits.Verts # locations to draw the markers at path = mtraits.Path # marker path in points - affine = mtraits.Affine # transformation for the verts - x = traits.Float(1.0) + affine = traits.Instance(Affine, ()) # transformation for the verts + mtraits.Markers = traits.Instance(Markers, ()) # coordinates: @@ -512,16 +653,16 @@ # to a separable cartesian coordinate, eg for polar is takes r, # theta -> r*cos(theta), r*sin(theta) # -# AxesCoords.affineview : an affine 3x3 matrix that takes model output and +# AxesCoords.adata : an affine 3x3 matrix that takes model output and # transforms it to axes 0,1. We are kind of stuck with the # mpl/matlab convention that 0,0 is the bottom left of the axes, # even though it contradicts pretty much every GUI layout in the # world # -# AxesCoords.affineaxes: an affine 3x3 that transforms an axesview into figure +# AxesCoords.aview: an affine 3x3 that transforms an axesview into figure # 0,1 # -# Renderer.affinerenderer : takes an affine 3x3 and puts figure view into display. 0, +# Renderer.adisplay : takes an affine 3x3 and puts figure view into display. 0, # 0 is left, top, which is the typical coordinate system of most # graphics formats @@ -551,22 +692,77 @@ zorder = traits.Float(1.0) alpha = mtraits.Alpha() visible = mtraits.Visible() - affine = mtraits.Affine + + adata = traits.Instance(Affine, ()) # the data affine + aview = traits.Instance(Affine, ()) # the view affine + affine = traits.Instance(Affine, ()) # the product of the data and view affine + + renderer = traits.Trait(None, Renderer) + + def __init__(self): self.artistid = artistID() - self.renderer = None + self.artistd = dict() - - def set_renderer(self, renderer): - self.renderer = renderer + # track affine as the product of the view and the data affines + # -- this should be a property, but I had trouble making a + # property on my custom class affine so this is a workaround + def product(ignore): + # modify in place + self.affine.follow((self.aview * self.adata).vec6) + product(None) # force an affine product updated + self.adata.on_trait_change(product, 'vec6') + self.aview.on_trait_change(product, 'vec6') + + def _get_affine(self): + return self.aview * self.adata + + def add_artist(self, artist, followdata=True, followview=True): + # this is a very interesting change from matplotlib -- every + # artist acts as a container that can hold other artists, and + # respects zorder drawing internally. This makes zordering + # much more flexibel + self.artistd[artist.artistid] = artist + artist.renderer = self.renderer + + artist.followdata = followdata + artist.followview = followview + if followdata: + # set the data affines to be the same + artist.adata.follow(self.adata.vec6) + self.adata.on_trait_change(artist.adata.follow, 'vec6') + + if followview: + # set the view affines to be the same + artist.aview.follow(self.aview.vec6) + self.aview.on_trait_change(artist.aview.follow, 'vec6') + + + self.sync_trait('renderer', artist, mutual=False) + + def remove_artist(self, artist): + + if artist.followview: + self.aview.on_trait_change(artist.aview.follow, 'vec6', remove=True) + del artist.followview + + if artist.followdata: + self.adata.on_trait_change(artist.adata.follow, 'vec6', remove=True) + del artist.followdata + + self.sync_trait('renderer', artist, remove=True) + del self.artistd[artist.artistid] + def draw(self): - pass + if self.renderer is None or not self.visible: return - def setp(self, **kwargs): - for k, v in kwargs.items(): - setattr(self, k, v) + dsu = [(artist.zorder, artist.artistid, artist) for artist in self.artistd.values()] + dsu.sort() + for zorder, artistid, artist in dsu: + artist.draw() + class Line(Artist): @@ -603,9 +799,16 @@ self.sync_trait('markerfacecolor', self.markers.path, 'fillcolor', mutual=False) self.sync_trait('markeredgecolor', self.markers.path, 'strokecolor', mutual=False) self.sync_trait('markeredgewidth', self.markers.path, 'linewidth', mutual=False) - self.sync_trait('affine', self.markers, mutual=False) - self.sync_trait('affine', self.path, mutual=False) + # sync up the markers affine + self.markers.affine.follow(self.affine.vec6) + self.affine.on_trait_change(self.markers.affine.follow, 'vec6') + + # sync up the path affine + self.path.affine.follow(self.affine.vec6) + self.affine.on_trait_change(self.path.affine.follow, 'vec6') + + self.path.fillcolor = None self.pathid = primitiveID() @@ -616,39 +819,44 @@ } def draw(self): - if self.renderer is None: - raise RuntimeError('First call set_renderer') - if not self.visible: return + if self.renderer is None or not self.visible: return + Artist.draw(self) - if self.linestyle is not None: self.renderer.render_path(self.pathid) if self.marker is not None: self.renderer.render_markers(self.markerid) - def set_renderer(self, renderer): - if self.renderer is not None: - self.renderer.remove_path(self.pathid) - self.renderer.remove_markers(self.markerid) + def _renderer_changed(self, old, new): + if old is not None: + old.remove_path(self.pathid) + old.remove_markers(self.markerid) - renderer.add_path(self.pathid, self.path) - renderer.add_markers(self.markerid, self.markers) + if new is not None: + new.add_path(self.pathid, self.path) + new.add_markers(self.markerid, self.markers) - Artist.set_renderer(self, renderer) - def _X_changed(self, old, newx): - N = newx.shape[0] + def _model_changed(self, old, new): + self._update_data() + + def _X_changed(self, old, new): + self._update_data() + + def _update_data(self): + N = self.X.shape[0] codes = Path.LINETO*npy.ones(N, dtype=npy.uint8) codes[0] = Path.MOVETO # todo, having touble setting Model to default to Identity so # allowing None as a hack workaround + #print 'X changed', self.model if self.model is not None: - modelx = self.model(newx) + modelx = self.model(self.X) else: - modelx = newx + modelx = self.X self.path.pathdata = codes, modelx self.markers.verts = modelx @@ -674,6 +882,7 @@ self.markers.path.pathdata = codes, verts +mtraits.Line = traits.Instance(Line, ()) class Rectangle(Artist): facecolor = mtraits.Color('yellow') @@ -690,11 +899,15 @@ self.sync_trait('facecolor', self.path, 'fillcolor', mutual=False) self.sync_trait('edgecolor', self.path, 'strokecolor', mutual=False) self.sync_trait('edgewidth', self.path, 'linewidth', mutual=False) - self.sync_trait('affine', self.path, mutual=False) self.pathid = primitiveID() + # sync up the path affine + self.path.affine.follow(self.affine.vec6) + self.affine.on_trait_change(self.path.affine.follow, 'vec6') + + def _lbwh_changed(self, old, new): l,b,w,h = new t = b+h @@ -706,141 +919,127 @@ self.path.pathdata = codes, verts - def set_renderer(self, renderer): - if self.renderer is not None: - self.renderer.remove_path(self.pathid) + def _renderer_changed(self, old, new): + if old is not None: + old.remove_path(self.pathid) - renderer.add_path(self.pathid, self.path) - Artist.set_renderer(self, renderer) + if new is not None: + new.add_path(self.pathid, self.path) def draw(self): - if self.renderer is None: - raise RuntimeError('First call set_renderer') - - if not self.visible: return + if self.renderer is None or not self.visible: return + Artist.draw(self) self.renderer.render_path(self.pathid) - -class Figure: - def __init__(self): - self.renderer = None - self.artistd = dict() - def draw(self): - if self.renderer is None: - raise RuntimeError('call set_renderer renderer first') - dsu = [(artist.zorder, artist.artistid, artist) for artist in self.artistd.values()] - dsu.sort() - for zorder, artistid, artist in dsu: - artist.draw() - - def set_renderer(self, renderer): - self.renderer = renderer +mtraits.Rectangle = traits.Instance(Rectangle, ()) - for artist in self.artistd.values(): - artist.set_renderer(renderer) +class Figure(Artist): + pass + + + +class Axes(Artist): + zorder = traits.Float(0.5) + xtickmarkers = mtraits.Markers + xaxisline = mtraits.Line + xticklocs = traits.Array('d') + xaxislocy = traits.Float(0.) # the y location of the x-axis + ytickmarkers = mtraits.Markers + yaxisline = mtraits.Line + yticklocs = traits.Array('d') + yaxislocx = traits.Float(0.) # the x location of the y-axis -class AxesCoords(traits.HasTraits): - xviewlim = mtraits.Interval - yviewlim = mtraits.Interval - affineview = mtraits.Affine - affineaxes = mtraits.Affine - affine = mtraits.Affine - - def _affineview_changed(self, old, new): - self.affine = self.affineaxes * new + def __init__(self): + Artist.__init__(self) + self.xtickmarkersid = primitiveID() - def _affineaxes_changed(self, old, new): - self.affine = new * self.affineview - - def _xviewlim_changed(self, old, new): + self.xtickmarkers.affine.follow(self.adata.vec6) + self.adata.on_trait_change(self.xtickmarkers.affine.follow, 'vec6') - xmin, xmax = new - scale = 1./(xmax-xmin) - tx = -xmin*scale - self.affineview.data[0][0] = scale - self.affineview.data[0][-1] = tx - self.affine = self.affineaxes * self.affineview + def _xticklocs_changed(self, old, new): + self._update_xtick_markers() + + def _xaxislocy_changed(self, old, new): + self._update_xtick_markers() - def _yviewlim_changed(self, old, new): - ymin, ymax = new - scale = 1./(ymax-ymin) - ty = -ymin*scale - self.affineview.data[1][1] = scale - self.affineview.data[1][-1] = ty - self.affine = self.affineaxes * self.affineview - + def _update_xtick_markers(self): + verts = self.xaxislocy*npy.ones(len(new)) + verts[:,1] = new + self.xtickmarkers.verts = verts + + def _renderer_changed(self, old, new): + if old is not None: + old.remove_markers(self.xtickmarkersid) + + if new is not None: + new.add_markers(self.xtickmarkersid, self.xtickmarkers) + + def draw(self): + if self.renderer is None or not self.visible: return + #print 'Axes data affine:\n', self.adata + #print 'Axes view affine :\n', self.aview + #print 'Axes affine :\n', self.affine + Artist.draw(self) + self.renderer.render_markers(self.xtickmarkersid) + x1 = npy.arange(0, 10., 0.05) x2 = npy.arange(0, 10., 0.1) y1 = npy.cos(2*npy.pi*x1) y2 = 10*npy.exp(-x1) -# the axes rectangle -coords1 = AxesCoords() -coords1.affineaxes = Affine() -coords1.affineaxes.scale = 0.4, 0.4 -coords1.affineaxes.translate = 0.1, 0.1 - fig = Figure() +axes = Axes() -line1 = Line() -line1.X = npy.array([x1,y1]).T +axes.aview.scale = 0.4, 0.4 +axes.aview.translate = 0.1, 0.1 -line1.setp(color='blue', linewidth=2.0, marker='s', markersize=5.0, - markerfacecolor='green', markeredgewidth=0.5) -coords1.sync_trait('affine', line1, mutual=False) +fig.add_artist(axes, followdata=False, followview=False) -fig.artistd[line1.artistid] = line1 +line1 = Line().set(X=npy.array([x1,y1]).T, + color='blue', linewidth=2.0, marker='s', markersize=5.0, + markerfacecolor='green', markeredgewidth=0.5) -rect1 = Rectangle() -rect1.lbwh = [0,0,1,1] -rect1.facecolor = 'white' -fig.artistd[rect1.artistid] = rect1 -coords1.sync_trait('affineaxes', rect1, 'affine', mutual=False) +axes.add_artist(line1) +rect1 = Rectangle().set(lbwh=[0,0,1,1], facecolor='white') +axes.add_artist(rect1, followdata=False) + + + # update the view limits, all the affines should be automagically updated -coords1.xviewlim = 0, 10 -coords1.yviewlim = -1.1, 1.1 +axes.adata.xlim = 0, 10 +axes.adata.ylim = -1.1, 1.1 +if 1: + axes2 = Axes() -if 1: - coords2 = AxesCoords() - coords2.affineaxes = Affine() - coords2.affineaxes.scale = 0.4, 0.4 - coords2.affineaxes.translate = 0.55, 0.55 + axes2.aview.scale = 0.4, 0.4 + axes2.aview.translate = 0.55, 0.55 + fig.add_artist(axes2, followdata=False, followview=False) r = npy.arange(0.0, 1.0, 0.01) theta = r*4*npy.pi - line2 = Line() - line2.model = Polar() - line2.setp(color='#ee8d18', linewidth=2.0) - line2.X = npy.array([r, theta]).T - coords2.sync_trait('affine', line2, mutual=False) + line2 = Line().set(X=npy.array([r, theta]).T, model=Polar(), color='#ee8d18', linewidth=2.0) + axes2.add_artist(line2) - rect2 = Rectangle() - rect2.lbwh = [0,0,1,1] - rect2.facecolor = '#d5de9c' - coords2.sync_trait('affineaxes', rect2, 'affine', mutual=False) + rect2 = Rectangle().set(lbwh=[0,0,1,1], facecolor='#d5de9c') + axes2.add_artist(rect2, followdata=False) - fig.artistd[line2.artistid] = line2 - fig.artistd[rect2.artistid] = rect2 + axes2.adata.xlim = -1.1, 1.1 + axes2.adata.ylim = -1.1, 1.1 - coords2.xviewlim = -1.1, 1.1 - coords2.yviewlim = -1.1, 1.1 - - - if 1: renderer = RendererAgg() - fig.set_renderer(renderer) + fig.renderer = renderer fig.draw() renderer.show() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-07-21 02:40:55
|
Revision: 3601 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3601&view=rev Author: jdh2358 Date: 2007-07-20 19:40:51 -0700 (Fri, 20 Jul 2007) Log Message: ----------- added axis artists Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-21 01:51:44 UTC (rev 3600) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-21 02:40:51 UTC (rev 3601) @@ -412,6 +412,7 @@ transpath = agg.conv_transform_path(path.agg_path, aggaffine) if path.fillcolor is not None: + print 'render path', path.fillcolor, path.agg_fillcolor self.rasterizer.add_path(transpath) renderer.color_rgba8( path.agg_fillcolor ) render_scanlines(self.rasterizer, scanline, renderer); @@ -463,8 +464,9 @@ for xv,yv,tmp in Locs.T: - XY[:,0] = pathx + xv - XY[:,1] = pathy + yv + XY[:,0] = (pathx + xv).astype(int) + 0.5 + XY[:,1] = (pathy + yv).astype(int) + 0.5 + pathdata = pathcodes, XY aggpath = AggPath.make_agg_path(pathdata) @@ -626,7 +628,7 @@ self.agg_fillcolor = self.color_to_rgba8(newcolor) def _strokecolor__changed(self, oldcolor, newcolor): - + print 'stroke color changed', newcolor c = self.color_to_rgba8(newcolor) self.agg_strokecolor = c @@ -725,8 +727,11 @@ # respects zorder drawing internally. This makes zordering # much more flexibel self.artistd[artist.artistid] = artist + artist.renderer = self.renderer + self.sync_trait('renderer', artist, mutual=False) + artist.followdata = followdata artist.followview = followview if followdata: @@ -740,7 +745,6 @@ self.aview.on_trait_change(artist.aview.follow, 'vec6') - self.sync_trait('renderer', artist, mutual=False) def remove_artist(self, artist): @@ -761,6 +765,7 @@ dsu = [(artist.zorder, artist.artistid, artist) for artist in self.artistd.values()] dsu.sort() for zorder, artistid, artist in dsu: + #print 'artist draw', self, artist, zorder artist.draw() @@ -938,86 +943,198 @@ pass - -class Axes(Artist): - zorder = traits.Float(0.5) +class Axis(Artist): + zorder = traits.Float(1.5) + tickmarkers = mtraits.Markers + linepath = mtraits.Path + linecolor = mtraits.Color('black') + linewidth = mtraits.LineWidth(1.0) + ticklocs = traits.Array('d') + ticksize = traits.Float(7.0) + ticklinewidth = mtraits.LineWidth(1.0) + tickcolor = mtraits.Color('black') - xtickmarkers = mtraits.Markers - xaxisline = mtraits.Line - xticklocs = traits.Array('d') - xaxislocy = traits.Float(0.) # the y location of the x-axis - - ytickmarkers = mtraits.Markers - yaxisline = mtraits.Line - yticklocs = traits.Array('d') - yaxislocx = traits.Float(0.) # the x location of the y-axis - + loc = traits.Float(0.) # the y location of the x-axis + tickoffset = traits.Float(-0.5) # -1 for outer, -0.5 for centered, 0 for inner + def __init__(self): Artist.__init__(self) - self.xtickmarkersid = primitiveID() + self.tickmarkersid = primitiveID() + self.linepathid = primitiveID() - self.xtickmarkers.affine.follow(self.adata.vec6) - self.adata.on_trait_change(self.xtickmarkers.affine.follow, 'vec6') + self.affine.on_trait_change(self._update_blended_affine, 'vec6') + self.tickmarkers.path.antialiased = False + self.linepath.antialiased = False - def _xticklocs_changed(self, old, new): - self._update_xtick_markers() + self.sync_trait('linewidth', self.linepath, mutual=False) + self.sync_trait('linecolor', self.linepath, 'strokecolor', mutual=False) + self.sync_trait('ticklinewidth', self.tickmarkers.path, 'linewidth', mutual=False) + self.sync_trait('tickcolor', self.tickmarkers.path, 'strokecolor', mutual=False) - def _xaxislocy_changed(self, old, new): - self._update_xtick_markers() + # XXX, do we have to manually call these or will they get + # calle dautomagically in init + self._update_tick_path() + self._update_marker_locations() + self._update_blended_affine() + self._update_linepath() + + def _ticklocs_changed(self, old, new): + self._update_marker_locations() + + def _loc_changed(self, old, new): + self._update_blended_affine() + + def _ticksize_changed(self, old, new): + self._update_tick_path() + + def _tickoffset_changed(self, old, new): + self._update_tick_path(self) + + def _update_blended_affine(self): + 'blend of xdata and y axis affine' + raise NotImplementedError - def _update_xtick_markers(self): - verts = self.xaxislocy*npy.ones(len(new)) - verts[:,1] = new - self.xtickmarkers.verts = verts + def _update_marker_locations(self): + raise NotImplementedError + + def _update_tick_path(self): + raise NotImplementedError + def _update_linepath(self): + raise NotImplementedError def _renderer_changed(self, old, new): if old is not None: - old.remove_markers(self.xtickmarkersid) + old.remove_markers(self.tickmarkersid) + old.remove_path(self.linepathid) if new is not None: - new.add_markers(self.xtickmarkersid, self.xtickmarkers) + new.add_markers(self.tickmarkersid, self.tickmarkers) + new.add_path(self.linepathid, self.linepath) def draw(self): if self.renderer is None or not self.visible: return - #print 'Axes data affine:\n', self.adata - #print 'Axes view affine :\n', self.aview - #print 'Axes affine :\n', self.affine Artist.draw(self) - self.renderer.render_markers(self.xtickmarkersid) + self.renderer.render_markers(self.tickmarkersid) + self.renderer.render_path(self.linepathid) -x1 = npy.arange(0, 10., 0.05) -x2 = npy.arange(0, 10., 0.1) -y1 = npy.cos(2*npy.pi*x1) -y2 = 10*npy.exp(-x1) +class XAxis(Axis): -fig = Figure() -axes = Axes() + def _update_blended_affine(self): + 'blend of xdata and y axis affine' + sx, b, tx = self.adata.data[0] + a = Affine() + a.vec6 = sx, b, 0, 1, tx, self.loc + self.tickmarkers.affine.vec6 = (self.aview * a).vec6 + + a = Affine() + a.translate = 0, self.loc + self.linepath.affine.vec6 = (self.aview * a).vec6 -axes.aview.scale = 0.4, 0.4 -axes.aview.translate = 0.1, 0.1 + def _update_marker_locations(self): + Nticks = len(self.ticklocs) + verts = self.loc*npy.ones((Nticks,2)) + verts[:,0] = self.ticklocs + self.tickmarkers.verts = verts -fig.add_artist(axes, followdata=False, followview=False) + def _update_tick_path(self): + codes = Path.MOVETO, Path.LINETO + verts = npy.array([[0., self.tickoffset], [0, self.tickoffset+1]])*self.ticksize + self.tickmarkers.path.pathdata = codes, verts -line1 = Line().set(X=npy.array([x1,y1]).T, - color='blue', linewidth=2.0, marker='s', markersize=5.0, - markerfacecolor='green', markeredgewidth=0.5) + def _update_linepath(self): + codes = Path.MOVETO, Path.LINETO + X = npy.array([[0, 1], [0, 0]], npy.float_).T + self.linepath.pathdata = codes, X +class YAxis(Axis): -axes.add_artist(line1) + def _update_blended_affine(self): + 'blend of xdata and y axis affine' + c, sy, ty = self.adata.data[1] + a = Affine() + a.vec6 = 1, 0, 0, sy, self.loc, ty + self.tickmarkers.affine.vec6 = (self.aview * a).vec6 + + a = Affine() + a.translate = self.loc, 0 + self.linepath.affine.vec6 = (self.aview * a).vec6 -rect1 = Rectangle().set(lbwh=[0,0,1,1], facecolor='white') -axes.add_artist(rect1, followdata=False) + def _update_marker_locations(self): + Nticks = len(self.ticklocs) + verts = self.loc*npy.ones((Nticks,2)) + verts[:,1] = self.ticklocs + self.tickmarkers.verts = verts + def _update_tick_path(self): + codes = Path.MOVETO, Path.LINETO + verts = npy.array([[self.tickoffset,0], [self.tickoffset+1,0]])*self.ticksize + self.tickmarkers.path.pathdata = codes, verts + def _update_linepath(self): + codes = Path.MOVETO, Path.LINETO + X = npy.array([[0, 0], [0, 1]], npy.float_).T + self.linepath.pathdata = codes, X -# update the view limits, all the affines should be automagically updated -axes.adata.xlim = 0, 10 -axes.adata.ylim = -1.1, 1.1 +class Axes(Artist): + zorder = traits.Float(0.5) + -if 1: + + ytickmarkers = mtraits.Markers + yaxisline = mtraits.Line + yticklocs = traits.Array('d') + yticksize = traits.Float(5.0) + yaxislocx = traits.Float(0.) # the x location of the y-axis + + + + +def make_subplot_ll(fig): + x1 = npy.arange(0, 10., 0.05) + x2 = npy.arange(0, 10., 0.1) + y1 = npy.cos(2*npy.pi*x1) + y2 = 10*npy.exp(-x1) + + + axes = Axes() + fig.add_artist(axes, followdata=False, followview=False) + + axes.aview.scale = 0.4, 0.4 + axes.aview.translate = 0.1, 0.1 + + xaxis = XAxis() + axes.add_artist(xaxis) + + yaxis = YAxis() + axes.add_artist(yaxis) + + line1 = Line().set(X=npy.array([x1,y1]).T, + color='blue', linewidth=2.0, marker='s', markersize=5.0, + markerfacecolor='green', markeredgewidth=0.5) + + + axes.add_artist(line1) + + + rect1 = Rectangle().set(lbwh=[0,0,1,1], facecolor='white') + axes.add_artist(rect1, followdata=False) + + # update the view limits, all the affines should be automagically updated + axes.adata.xlim = 0, 10 + axes.adata.ylim = -1.1, 1.1 + xaxis.ticklocs = npy.arange(0., 11., 1.) + xaxis.loc = -0.1 + xaxis.linecolor = 'red' + + yaxis.ticklocs = npy.arange(-1.0, 1.1, 0.2) + yaxis.loc = -0.1 + yaxis.linecolor = 'blue' + yaxis.tickcolor = 'blue' + +def make_subplot_ur(fig): axes2 = Axes() axes2.aview.scale = 0.4, 0.4 @@ -1038,8 +1155,11 @@ axes2.adata.ylim = -1.1, 1.1 -if 1: +if __name__=='__main__': renderer = RendererAgg() + fig = Figure() fig.renderer = renderer + make_subplot_ll(fig) + make_subplot_ur(fig) fig.draw() renderer.show() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-07-22 16:53:43
|
Revision: 3604 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3604&view=rev Author: jdh2358 Date: 2007-07-22 09:53:37 -0700 (Sun, 22 Jul 2007) Log Message: ----------- added (broken) axis support Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-21 23:30:03 UTC (rev 3603) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-22 16:53:37 UTC (rev 3604) @@ -8,17 +8,18 @@ wget http://peak.telecommunity.com/dist/ez_setup.py sudo python sez_setup.py - sudo easy_install -f http://code.enthought.com/enstaller/eggs/source/unstable "enthought.etsconfig < 3.0a" "enthought.util <3.0a" "enthought.debug <3.0a" - svn co https://svn.enthought.com/svn/enthought/branches/enthought.traits_2.0 enthought_traits - - cd enthought_traits/ - sudo python setup.py install - + sudo rm -rf /usr/local/lib/python2.5/site-packages/enthought* + sudo easy_install \ + -f http://code.enthought.com/enstaller/eggs/source/unstable \ + "enthought.resource <3.0a" "enthought.traits < 3.0a" + """ # see install instructions for enthrought traits2 in mtraits import enthought.traits.api as traits +from enthought.traits.api import HasTraits, Instance, Trait, Float, Int, \ + Array, Tuple from matplotlib import agg from matplotlib import colors as mcolors @@ -27,9 +28,10 @@ is_string_like = cbook.is_string_like +## begin core infrastructure -class Affine(traits.HasTraits): +class Affine(HasTraits): """ An affine 3x3 matrix that supports matrix multiplication with other Affine instances or numpy arrays. @@ -62,14 +64,14 @@ simply views into the data matrix, and are updated by reference """ # connect to the data_modified event if you want a callback - data = traits.Array('d', (3,3)) + data = Array('d', (3,3)) - translate = traits.Property(traits.Array('d', (2,))) - scale = traits.Property(traits.Array('d', (2,))) - vec6 = traits.Property(traits.Array('d', (6,))) + translate = traits.Property(Array('d', (2,))) + scale = traits.Property(Array('d', (2,))) + vec6 = traits.Property(Array('d', (6,))) - xlim = traits.Property(traits.Array('d', (2,))) - ylim = traits.Property(traits.Array('d', (2,))) + xlim = traits.Property(Array('d', (2,))) + ylim = traits.Property(Array('d', (2,))) #data_modified = traits.Event @@ -242,18 +244,18 @@ #return 'AFFINE:\n%s'%self.data -class Box(traits.HasTraits): +class Box(HasTraits): # left, bottom, width, height - bounds = traits.List - left = traits.Property(traits.Float) - bottom = traits.Property(traits.Float) - width = traits.Property(traits.Float) - height = traits.Property(traits.Float) + bounds = traits.Array('d', (4,)) + left = traits.Property(Float) + bottom = traits.Property(Float) + width = traits.Property(Float) + height = traits.Property(Float) - right = traits.Property(traits.Float) # read only - top = traits.Property(traits.Float) # read only + right = traits.Property(Float) # read only + top = traits.Property(Float) # read only - def ___bounds_default(self): + def _bounds_default(self): return [0.0, 0.0, 1.0, 1.0] def _get_left(self): @@ -295,7 +297,7 @@ return self.bottom + self.height def _bounds_changed(self, old, new): - print 'base bounds changed' + pass class ColorHandler(traits.TraitHandler): """ @@ -307,12 +309,12 @@ Eg class C(HasTraits): - fillcolor = traits.Trait('black', ColorHandler()) + facecolor = Trait('black', ColorHandler()) c = C() - c.fillcolor = 'red' - print c.fillcolor # prints red - print c.fillcolor_ # print (1,0,0,1) + c.facecolor = 'red' + print c.facecolor # prints red + print c.facecolor_ # print (1,0,0,1) """ is_mapped = True @@ -338,40 +340,98 @@ name like 'orange', a hex color like '#efefef', a grayscale intensity like '0.5', or an RGBA tuple (1,0,0,1)""" + + + + class MTraitsNamespace: - DPI = traits.Float(72.) + DPI = Float(72.) Alpha = traits.Range(0., 1., 0.) - Affine = traits.Trait(Affine()) + Affine = Trait(Affine()) AntiAliased = traits.true - Codes = traits.Array('b', value=npy.array([0,0], dtype=npy.uint8)) - Color = traits.Trait('black', ColorHandler()) - DPI = traits.Float(72.) - Interval = traits.Array('d', (2,), npy.array([0.0, 1.0], npy.float_)) - LineStyle = traits.Trait('-', '--', '-.', ':', 'steps', None) - LineWidth = traits.Float(1.0) - Marker = traits.Trait(None, '.', ',', 'o', '^', 'v', '<', '>', 's', + Color = Trait('black', ColorHandler()) + DPI = Float(72.) + Interval = Array('d', (2,), npy.array([0.0, 1.0], npy.float_)) + LineStyle = Trait('-', '--', '-.', ':', 'steps', None) + LineWidth = Float(1.0) + Marker = Trait(None, '.', ',', 'o', '^', 'v', '<', '>', 's', '+', 'x', 'd', 'D', '|', '_', 'h', 'H', 'p', '1', '2', '3', '4') - MarkerSize = traits.Float(6) - Verts = traits.Array('d', value=npy.array([[0,0],[0,0]], npy.float_)) - PathData = traits.Tuple(Codes(), Verts()) + MarkerSize = Float(6) Visible = traits.true mtraits = MTraitsNamespace() -class Renderer(traits.HasTraits): - dpi = mtraits.DPI - size = traits.Tuple(traits.Int(600), traits.Int(400)) +def Alias(name): + return Property(lambda obj: getattr(obj, name), + lambda obj, val: setattr(obj, name, val)) - adisplay = traits.Instance(Affine, ()) - def __init__(self, size=(600,400)): +class IDGenerator: + def __init__(self): + self._id = 0 - self.pathd = dict() # path id -> Path instance - self.markersd = dict() # path id -> Markers instance + def __call__(self): + _id = self._id + self._id += 1 + return _id + + + +## begin backend API +MOVETO, LINETO, CLOSEPOLY = range(3) +class PathPrimitive(HasTraits): + """ + The path is an object that talks to the backends, and is an + intermediary between the high level path artists like Line and + Polygon, and the backend renderer + """ + + + color = mtraits.Color('black') + facecolor = mtraits.Color('blue') + alpha = mtraits.Alpha(1.0) + linewidth = mtraits.LineWidth(1.0) + antialiased = mtraits.AntiAliased + pathdata =Tuple(Array('b'), Array('d')) + affine = Instance(Affine, ()) + + def _pathdata_default(self): + return (npy.array([0,0], dtype=npy.uint8), + npy.array([[0,0],[0,0]], npy.float_)) + + def _pathdata_changed(self, old, new): + codes, xy = new + + if len(xy.shape)!=2: + raise ValueError('xy in path data must be Nx2') + Ncodes = len(codes) + Nxy = xy.shape[0] + if Ncodes!=Nxy: + raise ValueError('codes and xy must have equal rows') + +class MarkerPrimitive(HasTraits): + locs = Array('d') + path = Instance(PathPrimitive, ()) # marker path in points + affine = Instance(Affine, ()) # transformation for the verts + + def _locs_default(self): + return npy.array([[0,0],[0,0]], npy.float_) + +class Renderer(HasTraits): + dpi = mtraits.DPI + size = traits.Tuple(Int(600), Int(400)) + + adisplay = Instance(Affine, ()) + pathd = traits.Dict(Int, PathPrimitive) + markerd = traits.Dict(Int, MarkerPrimitive) + + def __init__(self, size=(600,400)): + self.pathd = dict() + self.markerd = dict() self._size_changed(None, size) def _size_changed(self, old, new): @@ -383,37 +443,83 @@ - def add_path(self, pathid, path): - # todo, we could use a traits dict here - if not isinstance(path, Path): - raise TypeError('add_path takes an ID and a Path instance') - self.pathd[pathid] = path + def render_path(self, pathid): + pass + - def remove_path(self, pathid): - if pathid in self.pathd: - del self.pathd[pathid] + def new_path_primitive(self): + """ + return a PathPrimitive (or derived); these instances will be + added and removed later through add_path and remove path + """ + return PathPrimitive() - def add_markers(self, markersid, markers): - # todo, we could use a traits dict here - if not isinstance(markers, Markers): - raise TypeError('add_markers takes an ID and a Markers instance') - self.markersd[markersid] = markers + def new_marker_primitive(self): + """ + return a MarkerPrimitive (or derived); these instances will be + added and removed later through add_maker and remove_marker + """ + return MarkerPrimitive() + - def remove_markers(self, markersid): - if markersid in self.markersd: - del self.markersd[markersid] +## begin backend agg +class PathPrimitiveAgg(PathPrimitive): - def render_path(self, pathid): - pass - + def __init__(self): + self._pathdata_changed(None, self.pathdata) + self._facecolor_changed(None, self.facecolor) + self._color_changed(None, self.color) + + @staticmethod + def make_agg_path(pathdata): + agg_path = agg.path_storage() + codes, xy = pathdata + + Ncodes = len(codes) + + for i in range(Ncodes): + x, y = xy[i] + code = codes[i] + if code==MOVETO: + agg_path.move_to(x, y) + elif code==LINETO: + agg_path.line_to(x, y) + elif code==CLOSEPOLY: + agg_path.close_polygon() + return agg_path + + def _pathdata_changed(self, olddata, newdata): + self.agg_path = PathPrimitiveAgg.make_agg_path(newdata) + + + def _facecolor_changed(self, oldcolor, newcolor): + self.agg_facecolor = self.color_to_rgba8(self.facecolor_) + + def _color_changed(self, oldcolor, newcolor): + #print 'stroke color changed', newcolor + c = self.color_to_rgba8(self.color_) + self.agg_color = c + + def color_to_rgba8(self, color): + if color is None: return None + rgba = [int(255*c) for c in color] + return agg.rgba8(*rgba) + +class MarkerPrimitiveAgg(MarkerPrimitive): + path = Instance(PathPrimitiveAgg, ()) + + + class RendererAgg(Renderer): gray = agg.rgba8(128,128,128,255) white = agg.rgba8(255,255,255,255) blue = agg.rgba8(0,0,255,255) black = agg.rgba8(0,0,0,0) + pathd = traits.Dict(Int, PathPrimitiveAgg) + markerd = traits.Dict(Int, MarkerPrimitiveAgg) def _size_changed(self, old, new): Renderer._size_changed(self, old, new) @@ -440,9 +546,13 @@ self.scanlinebin = agg.scanline_bin() - def add_path(self, pathid, path): - self.pathd[pathid] = AggPath(path) + def new_path_primitive(self): + 'return a PathPrimitive (or derived)' + return PathPrimitiveAgg() + def new_marker_primitive(self): + 'return a MarkerPrimitive (or derived)' + return MarkerPrimitiveAgg() def render_path(self, pathid): @@ -465,23 +575,23 @@ aggaffine = agg.trans_affine(*affine.vec6) transpath = agg.conv_transform_path(path.agg_path, aggaffine) - if path.fillcolor is not None: - #print 'render path', path.fillcolor, path.agg_fillcolor + if path.facecolor is not None: + #print 'render path', path.facecolor, path.agg_facecolor self.rasterizer.add_path(transpath) - renderer.color_rgba8( path.agg_fillcolor ) + renderer.color_rgba8( path.agg_facecolor ) render_scanlines(self.rasterizer, scanline, renderer); - if path.strokecolor is not None: + if path.color is not None: stroke = agg.conv_stroke_transpath(transpath) stroke.width(path.linewidth) self.rasterizer.add_path(stroke) - renderer.color_rgba8( path.agg_strokecolor ) + renderer.color_rgba8( path.agg_color ) render_scanlines(self.rasterizer, scanline, renderer); - def render_markers(self, markerid): - markers = self.markersd[markerid] + def render_marker(self, markerid): + marker = self.markerd[markerid] - path = AggPath(markers.path) + path = marker.path if path.antialiased: renderer = self.renderer @@ -494,14 +604,14 @@ - affineverts = self.adisplay * markers.affine + affinelocs = self.adisplay * marker.affine - Nmarkers = markers.verts.shape[0] + Nmarkers = marker.locs.shape[0] Locs = npy.ones((3, Nmarkers)) - Locs[0] = markers.verts[:,0] - Locs[1] = markers.verts[:,1] + Locs[0] = marker.locs[:,0] + Locs[1] = marker.locs[:,1] - Locs = affineverts * Locs + Locs = affinelocs * Locs dpiscale = self.dpi/72. # for some reason this is broken @@ -509,32 +619,34 @@ # extension code using cached marker rasters as we now do in # _backend_agg - pathcodes, pathverts = markers.path.pathdata - pathx = dpiscale*pathverts[:,0] - pathy = dpiscale*pathverts[:,1] + pathcodes, pathxy = marker.path.pathdata + + pathx = dpiscale*pathxy[:,0] + pathy = dpiscale*pathxy[:,1] + Npath = len(pathcodes) XY = npy.ones((Npath, 2)) + for xv,yv,tmp in Locs.T: XY[:,0] = (pathx + xv).astype(int) + 0.5 XY[:,1] = (pathy + yv).astype(int) + 0.5 - pathdata = pathcodes, XY - aggpath = AggPath.make_agg_path(pathdata) + aggpath = PathPrimitiveAgg.make_agg_path(pathdata) - if path.fillcolor is not None: + if path.facecolor is not None: self.rasterizer.add_path(aggpath) - renderer.color_rgba8( path.agg_fillcolor ) + renderer.color_rgba8( path.agg_facecolor ) render_scanlines(self.rasterizer, scanline, renderer); - if path.strokecolor is not None: + if path.color is not None: stroke = agg.conv_stroke_path(aggpath) stroke.width(path.linewidth) self.rasterizer.add_path(stroke) - renderer.color_rgba8( path.agg_strokecolor ) + renderer.color_rgba8( path.agg_color ) render_scanlines(self.rasterizer, scanline, renderer); def show(self): @@ -552,7 +664,7 @@ pylab.show() -class Func(traits.HasTraits): +class Func(HasTraits): def __call__(self, X): 'transform the numpy array with shape N,2' return X @@ -598,111 +710,15 @@ raise NotImplementedError -mtraits.Model = traits.Instance(Func, ()) +mtraits.Model = Instance(Func, ()) -class Path(traits.HasTraits): - """ - The path is an object that talks to the backends, and is an - intermediary between the high level path artists like Line and - Polygon, and the backend renderer - """ - MOVETO, LINETO, CLOSEPOLY = range(3) - - strokecolor = mtraits.Color('black') - fillcolor = mtraits.Color('blue') - alpha = mtraits.Alpha(1.0) - linewidth = mtraits.LineWidth(1.0) - antialiased = mtraits.AntiAliased - pathdata = mtraits.PathData - affine = traits.Instance(Affine, ()) - def __init__(self): - # this is a quick workaround to deal with the problem that - # traits inited at the class level are shared between - # instances, which is not what I want - self.pathdata = (npy.array([0,0], npy.uint8), # codes - npy.array([[0,0], [0,0]])) # verts - -class AggPath(Path): - def __init__(self, path): - self.strokecolor = path.strokecolor - self.fillcolor = path.fillcolor - self.alpha = path.alpha - self.linewidth = path.linewidth - self.antialiased = path.antialiased - self.pathdata = path.pathdata - self.affine = path.affine - - - path.sync_trait('strokecolor', self, mutual=False) - path.sync_trait('fillcolor', self, mutual=False) - path.sync_trait('alpha', self, mutual=False) - path.sync_trait('linewidth', self, mutual=False) - path.sync_trait('antialiased', self, mutual=False) - path.sync_trait('pathdata', self, mutual=False) - - path.on_trait_change(self.affine.follow, 'vec6') - - - - # hmm, I would have thought these would be called by the attr - # setting above - self._pathdata_changed(None, self.pathdata) - self._fillcolor__changed(None, self.fillcolor_) - self._strokecolor__changed(None, self.strokecolor_) - - - @staticmethod - def make_agg_path(pathdata): - MOVETO, LINETO, CLOSEPOLY = Path.MOVETO, Path.LINETO, Path.CLOSEPOLY - agg_path = agg.path_storage() - codes, verts = pathdata - N = len(codes) - for i in range(N): - x, y = verts[i] - code = codes[i] - if code==MOVETO: - agg_path.move_to(x, y) - elif code==LINETO: - agg_path.line_to(x, y) - elif code==CLOSEPOLY: - agg_path.close_polygon() - return agg_path - - def _pathdata_changed(self, olddata, newdata): - self.agg_path = AggPath.make_agg_path(newdata) - - - def _fillcolor__changed(self, oldcolor, newcolor): - self.agg_fillcolor = self.color_to_rgba8(newcolor) - - def _strokecolor__changed(self, oldcolor, newcolor): - #print 'stroke color changed', newcolor - c = self.color_to_rgba8(newcolor) - self.agg_strokecolor = c - - - def color_to_rgba8(self, color): - if color is None: return None - rgba = [int(255*c) for c in color] - return agg.rgba8(*rgba) - - -mtraits.Path = traits.Instance(Path, ()) -class Markers(traits.HasTraits): - verts = mtraits.Verts # locations to draw the markers at - path = mtraits.Path # marker path in points - affine = traits.Instance(Affine, ()) # transformation for the verts - - - - -mtraits.Markers = traits.Instance(Markers, ()) +## begin Artist layer # coordinates: # # artist model : a possibly nonlinear transformation (Func instance) @@ -722,39 +738,20 @@ # 0 is left, top, which is the typical coordinate system of most # graphics formats - - - - -def Alias(name): - return Property(lambda obj: getattr(obj, name), - lambda obj, val: setattr(obj, name, val)) - - -class IDGenerator: - def __init__(self): - self._id = 0 - - def __call__(self): - _id = self._id - self._id += 1 - return _id - - primitiveID = IDGenerator() artistID = IDGenerator() -class Artist(traits.HasTraits): - zorder = traits.Float(1.0) + +class Artist(HasTraits): + zorder = Float(1.0) alpha = mtraits.Alpha() visible = mtraits.Visible() - adata = traits.Instance(Affine, ()) # the data affine - aview = traits.Instance(Affine, ()) # the view affine - - affine = traits.Instance(Affine, ()) # the product of the data and view affine + adata = Instance(Affine, ()) # the data affine + aview = Instance(Affine, ()) # the view affine + affine = Instance(Affine, ()) # the product of the data and view affine - renderer = traits.Trait(None, Renderer) + renderer = Trait(None, Renderer) # every artist defines a string which is the name of the attr that # containers should put it into when added. Eg, an Axes is an @@ -785,7 +782,7 @@ class ArtistContainer(Artist): - artistd = traits.Dict(traits.Int, Artist) + artistd = traits.Dict(Int, Artist) sequence = 'containers' def __init__(self): Artist.__init__(self) @@ -842,182 +839,280 @@ #print 'artist draw', self, artist, zorder artist.draw() -class Line(Artist): - linestyle = mtraits.LineStyle('-') - antialiased = mtraits.AntiAliased() - color = mtraits.Color('blue') - linewidth = mtraits.LineWidth(1.0) - marker = mtraits.Marker(None) - markerfacecolor = mtraits.Color('blue') - markeredgecolor = mtraits.Color('black') - markeredgewidth = mtraits.LineWidth(0.5) - markersize = mtraits.MarkerSize(6.0) - path = mtraits.Path - markers = mtraits.Markers - X = mtraits.Verts - model = mtraits.Model - zorder = traits.Float(2.0) - sequence = 'lines' +class Path(Artist): + """ + An interface class between the higher level artists and the path + primitive that needs to talk to the renderers + """ + _path = traits.Instance(PathPrimitive, ()) + antialiased = mtraits.AntiAliased() + color = mtraits.Color('blue') + facecolor = mtraits.Color('yellow') + linestyle = mtraits.LineStyle('-') + linewidth = mtraits.LineWidth(1.0) + model = mtraits.Model + pathdata = traits.Tuple(Array('b'), Array('d')) + sequence = 'paths' + zorder = Float(1.0) + # why have an extra layer separating the PathPrimitive from the + # Path artist? The reasons are severalfold, but it is still not + # clear if this is the better solution. Doing it this way enables + # the backends to create their own derived primitves (eg + # RendererAgg creates PathPrimitiveAgg, and in that class sets up + # trait listeners to create agg colors and agg paths when the + # PathPrimitive traits change. Another reason is that it allows + # us to handle nonlinear transformation (the "model") at the top + # layer w/o making the backends understand them. The current + # design is create a mapping between backend primitives and + # primitive artists (Path, Text, Image, etc...) and all of the + # higher level Artists (Line, Polygon, Axis) will use the + # primitive artitsts. So only a few artists will need to know how + # to talk to the backend. The alternative is to make the backends + # track and understand the primitive artists themselves. + def __init__(self): """ The model is a function taking Nx2->Nx2. This is where the nonlinear transformation can be used """ Artist.__init__(self) + self._pathid = primitiveID() - # this is potentially a big problem because you have to know - # which attrs may be shared and hence have to be initialized - # and which ones don't. Eg, if you comment out the self.path - # init, the code breaks + def _pathdata_default(self): + return (npy.array([0,0], dtype=npy.uint8), + npy.array([[0,0],[0,0]], npy.float_)) + + def _update_path(self): + 'sync the Path traits with the path primitive' + self.sync_trait('linewidth', self._path, mutual=False) + self.sync_trait('color', self._path, mutual=False) + self.sync_trait('facecolor', self._path, mutual=False) + self.sync_trait('antialiased', self._path, mutual=False) - - self.sync_trait('linewidth', self.path, 'linewidth', mutual=False) - self.sync_trait('color', self.path, 'strokecolor', mutual=False) - self.sync_trait('markerfacecolor', self.markers.path, 'fillcolor', mutual=False) - self.sync_trait('markeredgecolor', self.markers.path, 'strokecolor', mutual=False) - self.sync_trait('markeredgewidth', self.markers.path, 'linewidth', mutual=False) + # sync up the path affine + self._path.affine.follow(self.affine.vec6) + self.affine.on_trait_change(self._path.affine.follow, 'vec6') + self._update_pathdata() - # sync up the markers affine - self.markers.affine.follow(self.affine.vec6) - self.affine.on_trait_change(self.markers.affine.follow, 'vec6') + def _update_pathdata(self): + #print 'PATH: update pathdata' - # sync up the path affine - self.path.affine.follow(self.affine.vec6) - self.affine.on_trait_change(self.path.affine.follow, 'vec6') - + codes, xy = self.pathdata - self.path.fillcolor = None + #print ' PATH: shapes', codes.shape, xy.shape + if self.model is not None: + xy = self.model(xy) - self.pathid = primitiveID() - self.markerid = primitiveID() + pathdata = codes, xy - self.markerfuncd = { - 's': self._markers_square, - } - + self._path.pathdata = pathdata + def draw(self): - if self.renderer is None or not self.visible: return Artist.draw(self) - - if self.linestyle is not None: - self.renderer.render_path(self.pathid) - if self.marker is not None: - self.renderer.render_markers(self.markerid) + self.renderer.render_path(self._pathid) def _renderer_changed(self, old, new): if old is not None: - old.remove_path(self.pathid) - old.remove_markers(self.markerid) + del old.pathd[self._pathid] + + if new is None: return - if new is not None: - new.add_path(self.pathid, self.path) - new.add_markers(self.markerid, self.markers) + #print 'PATH renderer_changed; updating' + self._path = renderer.new_path_primitive() + new.pathd[self._pathid] = self._path + self._update_path() + + def _model_changed(self, old, new): + self._update_pathdata() + def _pathdata_changed(self, old, new): + #print 'PATH: pathdata changed' + self._update_pathdata() - def _model_changed(self, old, new): - self._update_data() - def _X_changed(self, old, new): - self._update_data() - - def _update_data(self): - N = self.X.shape[0] - codes = Path.LINETO*npy.ones(N, dtype=npy.uint8) - codes[0] = Path.MOVETO +class Marker(Artist): + """ + An interface class between the higher level artists and the marker + primitive that needs to talk to the renderers + """ + _marker = traits.Instance(MarkerPrimitive, ()) + locs = Array('d') + path = Instance(Path, ()) + model = mtraits.Model + sequence = 'markers' + size = Float(1.0) # size of the marker in points - # todo, having touble setting Model to default to Identity so - # allowing None as a hack workaround - #print 'X changed', self.model - if self.model is not None: - modelx = self.model(self.X) - else: - modelx = self.X - self.path.pathdata = codes, modelx - self.markers.verts = modelx + def __init__(self): + """ + The model is a function taking Nx2->Nx2. This is where the + nonlinear transformation can be used + """ + Artist.__init__(self) + self._markerid = primitiveID() - def _markersize_changed(self, oldX, newX): - self._refresh_markers() + def _locs_default(self): + return npy.array([[0,1],[0,1]], npy.float_) - def _marker_changed(self, oldX, newX): - self._refresh_markers() + + def _path_default(self): + bounds = npy.array([-0.5, -0.5, 1, 1])*self.size + return Rectangle().set(bounds=bounds) + def _path_changed(self, old, new): + if self.renderer is None: + # we can't sync up to the underlying path yet + return + print 'MARKER _path_changed', self.path._path.pathdata, self._marker.path.pathdata + old.sync_trait('_path', self._marker, 'path', remove=True) + new.sync_trait('_path', self._marker, 'path', mutual=False) - def _refresh_markers(self): - if self.marker is not None: - markerfunc = self.markerfuncd.get(self.marker) - if markerfunc is not None: markerfunc() + def _update_marker(self): + 'sync the Marker traits with the marker primitive' + if self.renderer is None: + # we can't sync up to the underlying path yet + return - def _markers_square(self): + # sync up the marker affine + self.path.sync_trait('_path', self._marker, 'path', mutual=False) + self._marker.affine.follow(self.affine.vec6) + self.affine.on_trait_change(self._marker.affine.follow, 'vec6') + self._update_locs() - verts = self.markersize*npy.array([[-0.5,-0.5], [-0.5,0.5], [0.5,0.5], [0.5,-0.5], [0,0]]) - codes = Path.LINETO*npy.ones(len(verts), dtype=npy.uint8) - codes[0] = Path.MOVETO - codes[-1] = Path.CLOSEPOLY + print 'MARKER _update_marker', self.path._path.pathdata, self._marker.path.pathdata + + def _update_locs(self): + print 'MARKER: update markerdata' + xy = self.locs + if self.model is not None: + xy = self.model(xy) + + self._marker.locs = xy - self.markers.path.pathdata = codes, verts + def draw(self): + if self.renderer is None or not self.visible: return + Artist.draw(self) + self.renderer.render_marker(self._markerid) + -mtraits.Line = traits.Instance(Line, ()) + def _renderer_changed(self, old, new): + # we must make sure the contained artist gets the callback + # first so we can update the path primitives properly + self.path._renderer_changed(old, new) + if old is not None: + del old.markerd[self._markerid] + + if new is None: return -class Rectangle(Artist, Box): - facecolor = mtraits.Color('yellow') - edgecolor = mtraits.Color('black') - edgewidth = mtraits.LineWidth(1.0) - path = mtraits.Path - zorder = traits.Float(1.0) - sequence = 'rectangles' + print 'MARKER renderer_changed; updating' + self._marker = renderer.new_marker_primitive() + new.markerd[self._markerid] = self._marker + self._update_marker() + + def _model_changed(self, old, new): + self._update_locs() - def __init__(self): - Artist.__init__(self) - - self.sync_trait('facecolor', self.path, 'fillcolor', mutual=False) - self.sync_trait('edgecolor', self.path, 'strokecolor', mutual=False) - self.sync_trait('edgewidth', self.path, 'linewidth', mutual=False) + def _locs_changed(self, old, new): + if len(new.shape)!=2: + raise ValueError('new must be nx2 array') + self._update_locs() - self.pathid = primitiveID() - # sync up the path affine - self.path.affine.follow(self.affine.vec6) - self.affine.on_trait_change(self.path.affine.follow, 'vec6') +class Line(Path): + + XY = Array('d') + sequence = 'lines' + + def _facecolor_default(self): + return None + + def _XY_default(self): + return npy.array([[0,1],[0,1]], npy.float_) + + def _XY_changed(self): + #print 'LINE: XY changed' + codes = npy.ones(len(self.XY), npy.uint8) + codes[0] = MOVETO + #print 'LINE shapes', codes.shape, self.XY.shape + self.pathdata = codes, self.XY + + # XXX: to we need to push pathdata changed here or will it + # happen automagically + + +class Polygon(Path): + zorder = Float(2.0) + vertices = Array('d') + sequence = 'polygons' + + def _vertices_default(self): + return npy.array([[-1,0], [0,1], [1,0], [0,0]], npy.float_) + + def _vertices_changed(self, old, new): + #print 'POLY: verts changed' + N = len(new) - def _hidebounds_changed(self, old, new): - Box._bounds_changed(self, old, new) - print 'rectangle bounds changed' + codes = LINETO*npy.ones(N, npy.uint8) + codes[0] = MOVETO + codes[-1] = CLOSEPOLY + pathdata = codes, new + self.pathdata = pathdata + self._pathdata_changed(None, pathdata) + +class Rectangle(Polygon, Box): + sequence = 'rectangles' + def __init__(self): + Polygon.__init__(self) + self._bounds_changed(None, self.bounds) + def _bounds_changed(self, old, new): Box._bounds_changed(self, old, new) - print 'rectangle bounds changed' + #print 'RECT: bounds changed' l,b,w,h = new t = b+h r = l+w - verts = npy.array([(l,b), (l,t), (r, t), (r, b), (0,0)], npy.float_) - codes = Path.LINETO*npy.ones(5, npy.uint8) - codes[0] = Path.MOVETO - codes[-1] = Path.CLOSEPOLY + self.vertices = npy.array([(l,b), (l,t), (r, t), (r, b), (0,0)], npy.float_) + #XXX: do we need to otify traits change + self._vertices_changed(None, self.vertices) - self.path.pathdata = codes, verts +class RegularPolygon(Polygon): + center = Tuple(Float, Float) + sides = Int(6) + radius = Float(1.0) + theta = Float(0.) # orientation in radians + sequence = 'rectangles' - def _renderer_changed(self, old, new): - if old is not None: - old.remove_path(self.pathid) + def __init__(self): + self._update_vertices() + + def _sides_changed(self, old, new): + self._update_verts() - if new is not None: - new.add_path(self.pathid, self.path) + def _theta_changed(self, old, new): + self._update_verts() - def draw(self): - if self.renderer is None or not self.visible: return - Artist.draw(self) - self.renderer.render_path(self.pathid) + def _radius_changed(self, old, new): + self._update_verts() + + def _update_verts(self): + theta = 2*npy.pi/self.sides*npy.arange(self.sides) + self.theta + x, y = self.center -mtraits.Rectangle = traits.Instance(Rectangle, ()) + xy = npy.zeros((self.sides,2)) + xy[:,0] = x + self.radius*npy.cos(theta) + xy[:,1] = y + self.radius*npy.sin(theta) + + self.vertices = xy + class Figure(ArtistContainer): - rectangle = traits.Instance(Rectangle, ()) + rectangle = Instance(Rectangle, ()) sequence = None # figure is top level container def __init__(self): ArtistContainer.__init__(self) @@ -1027,34 +1122,26 @@ self.add_artist(self.rectangle) class Axis(ArtistContainer): - zorder = traits.Float(1.5) - tickmarkers = mtraits.Markers - linepath = mtraits.Path - linecolor = mtraits.Color('black') - linewidth = mtraits.LineWidth(1.0) - ticklocs = traits.Array('d') - ticksize = traits.Float(5.0) - ticklinewidth = mtraits.LineWidth(1.0) - tickcolor = mtraits.Color('black') + zorder = Float(1.5) + tickmarker = Instance(Marker, ()) + line = Instance(Line, ()) + ticklocs = Array('d') + ticksize = Float(5.0) + - loc = traits.Float(0.) # the y location of the x-axis - tickoffset = traits.Float(0) # -1 for outer, -0.5 for centered, 0 for inner + loc = Float(0.) # the y location of the x-axis + tickoffset = Float(0) # -1 for outer, -0.5 for centered, 0 for inner sequence = 'axes' def __init__(self): ArtistContainer.__init__(self) - self.tickmarkersid = primitiveID() - self.linepathid = primitiveID() - self.affine.on_trait_change(self._update_blended_affine, 'vec6') - self.tickmarkers.path.antialiased = False - self.linepath.antialiased = False - - self.sync_trait('linewidth', self.linepath, mutual=False) - self.sync_trait('linecolor', self.linepath, 'strokecolor', mutual=False) - self.sync_trait('ticklinewidth', self.tickmarkers.path, 'linewidth', mutual=False) - self.sync_trait('tickcolor', self.tickmarkers.path, 'strokecolor', mutual=False) + self.tickmarker.antialiased = False + self.line.antialiased = False + self.add_artist(self.line, followdata=False) + self.add_artist(self.tickmarker, followdata=False) + # XXX, do we have to manually call these or will they get # calle dautomagically in init self._update_tick_path() @@ -1087,21 +1174,6 @@ def _update_linepath(self): raise NotImplementedError - def _renderer_changed(self, old, new): - if old is not None: - old.remove_markers(self.tickmarkersid) - old.remove_path(self.linepathid) - - if new is not None: - new.add_markers(self.tickmarkersid, self.tickmarkers) - new.add_path(self.linepathid, self.linepath) - - def draw(self): - if self.renderer is None or not self.visible: return - Artist.draw(self) - self.renderer.render_markers(self.tickmarkersid) - self.renderer.render_path(self.linepathid) - class XAxis(Axis): sequence = 'xaxes' def _update_blended_affine(self): @@ -1109,27 +1181,31 @@ sx, b, tx = self.adata.data[0] a = Affine() a.vec6 = sx, b, 0, 1, tx, self.loc - self.tickmarkers.affine.vec6 = (self.aview * a).vec6 + self.tickmarker.affine.vec6 = (self.aview * a).vec6 a = Affine() a.translate = 0, self.loc - self.linepath.affine.vec6 = (self.aview * a).vec6 + self.line.affine.vec6 = (self.aview * a).vec6 def _update_marker_locations(self): Nticks = len(self.ticklocs) - verts = self.loc*npy.ones((Nticks,2)) - verts[:,0] = self.ticklocs - self.tickmarkers.verts = verts + locs = self.loc*npy.ones((Nticks,2)) + locs[:,0] = self.ticklocs + self.tickmarker.locs = locs def _update_tick_path(self): - codes = Path.MOVETO, Path.LINETO + codes = MOVETO, LINETO verts = npy.array([[0., self.tickoffset], [0, self.tickoffset-1]])*self.ticksize - self.tickmarkers.path.pathdata = codes, verts + pathdata = codes, verts + self.tickmarker.path.pathdata = pathdata + def _update_linepath(self): - codes = Path.MOVETO, Path.LINETO + + codes = MOVETO, LINETO X = npy.array([[0, 1], [0, 0]], npy.float_).T - self.linepath.pathdata = codes, X + pathdata = codes, X + self.line.pathdata = pathdata class YAxis(Axis): sequence = 'yaxes' @@ -1139,28 +1215,31 @@ c, sy, ty = self.adata.data[1] a = Affine() a.vec6 = 1, 0, 0, sy, self.loc, ty - self.tickmarkers.affine.vec6 = (self.aview * a).vec6 + self.tickmarker.affine.vec6 = (self.aview * a).vec6 a = Affine() a.translate = self.loc, 0 - self.linepath.affine.vec6 = (self.aview * a).vec6 + self.line.affine.vec6 = (self.aview * a).vec6 def _update_marker_locations(self): Nticks = len(self.ticklocs) - verts = self.loc*npy.ones((Nticks,2)) - verts[:,1] = self.ticklocs - self.tickmarkers.verts = verts + locs = self.loc*npy.ones((Nticks,2)) + locs[:,1] = self.ticklocs + self.tickmarker.locs = locs def _update_tick_path(self): - codes = Path.MOVETO, Path.LINETO + codes = MOVETO, LINETO verts = npy.array([[self.tickoffset,0], [self.tickoffset+1,0]])*self.ticksize - self.tickmarkers.path.pathdata = codes, verts + pathdata = codes, verts + self.tickmarker.path.pathdata = pathdata def _update_linepath(self): - codes = Path.MOVETO, Path.LINETO + codes = MOVETO, LINETO X = npy.array([[0, 0], [0, 1]], npy.float_).T - self.linepath.pathdata = codes, X + pathdata = codes, X + self.line.pathdata = pathdata + class FigurePane(ArtistContainer, Box): """ The figure pane conceptually like the matplotlib Axes, but now @@ -1168,10 +1247,10 @@ Affine instances. It is a shell of it's former self: it has a rectangle and a default x and y axis instance """ - rectangle = traits.Instance(Rectangle, ()) + rectangle = Instance(Rectangle, ()) #gridabove = traits.false # TODO handle me - xaxis = traits.Instance(XAxis, ()) - yaxis = traits.Instance(YAxis, ()) + xaxis = Instance(XAxis, ()) + yaxis = Instance(YAxis, ()) sequence = 'panes' def __init__(self): @@ -1181,56 +1260,120 @@ self.rectangle.edgecolor = 'white' self.rectangle.linewidth = 0 - print 'setting rect bounds' self.rectangle.bounds = [0,0,1,1] - print 'set rect bounds' self.add_artist(self.rectangle, followdata=False) self.add_artist(self.xaxis) self.add_artist(self.yaxis) def _bounds_changed(self, old, new): Box._bounds_changed(self, old, new) - print 'pane bounds changed' l,b,w,h = self.bounds self.aview.scale = w, h self.aview.translate = l, b - +## begin examples + def classic(fig): - x = npy.arange(0, 10., 0.01) - y = npy.sin(2*npy.pi*x) pane = FigurePane().set(bounds=[0.1, 0.1, 0.8, 0.8]) fig.add_artist(pane, followdata=False, followview=False) - - - line1 = Line().set(X=npy.array([x,y]).T, - color='blue', linewidth=2.0, marker=None, + # update the view limits, all the affines should be automagically updated + x = npy.arange(0, 10., 0.01) + y = npy.sin(2*npy.pi*x) + y = npy.exp(-x/2.) + line1 = Line().set(XY=npy.array([x,y]).T, + color='blue', linewidth=2.0, ) pane.add_artist(line1) - - # update the view limits, all the affines should be automagically updated pane.adata.xlim = 0, 10 - pane.adata.ylim = -1.1, 1.1 + pane.adata.ylim = -0.1, 1.1 - pane.xaxis.ticklocs = npy.arange(0., 11., 1.) - pane.yaxis.ticklocs = npy.arange(-1.0, 1.1, 0.2) + # axis placement is still broken + xax, yax = pane.xaxis, pane.yaxis + xax.ticklocs = npy.arange(0., 11., 1) + xax.tickoffset = 0.5 + xax.loc = -0.05 + xax.line.color = 'black' + xax.tickmarker.path.color = 'black' + yax.ticklocs = npy.arange(-1.0, 1.1, 0.2) + yax.tickoffset = -0.5 + yax.loc = -0.05 + yax.line.color = 'black' + yax.tickmarker.path.color = 'black' - # add a right and top axis - xaxis2 = XAxis().set(loc=1, tickoffset=-1) - yaxis2 = YAxis().set(loc=1, tickoffset=-1) - xaxis2.ticklocs = npy.arange(0., 11., 0.5) - yaxis2.ticklocs = npy.arange(-1.0, 1.1, 0.1) + if 0: + x = npy.arange(0, 10., 0.1) + y = npy.sin(2*npy.pi*x) - pane.add_artist(xaxis2) - pane.add_artist(yaxis2) - # uncomment to change Axes wwidth - #pane.width = 0.8 + marker = Marker().set( + locs=npy.array([x,y]).T, color='ref', linewidth=1.0, + size=10) + + pane.add_artist(marker) + + + if 0: + xax, yax = pane.xaxis, pane.yaxis + xax.ticklocs = npy.arange(0., 11., 1) + xax.ticksize = 8 + xax.line.color = 'black' + xax.line.linewidth = 2.0 + xax.tickoffset = .5 + xax.tickmarker.path.color = 'black' + xax.tickmarker.path.linewidth = 2 + + xax.loc = 0.5 + xax.zorder = 10 + + yax.ticklocs = npy.arange(-1.0, 1.1, 0.2) + yax.line.color = 'black' + yax.line.linewidth = 2.0 + yax.tickmarker.path.color = 'black' + yax.ticksize = 8 + yax.tickoffset = -0.5 + yax.tickmarker.path.linewidth = 2 + yax.loc = 0.5 + yax.zorder = 10 + if 0: + # add a right and top axis; the markers are getting the loc + # but the line path isn't... It appears all the line paths + # are getting 0 + xaxis2 = XAxis() + xaxis2.loc = 0.6 + xaxis2.tickoffset = -1 + xaxis2.ticklocs = npy.arange(0., 10.1, 0.5) + yaxis2 = YAxis().set(loc=0.6, tickoffset=-1) + + yaxis2.tickmarker.path.color = 'green' + yaxis2.loc = 0.5 + yaxis2.ticksize = 10 + yaxis2.tickmarker.path.linewidth = 1 + yaxis2.line.color = 'green' + yaxis2.tickmarker.path.color = 'green' + yaxis2.ticklocs = npy.arange(-1.0, 1.1, 0.1) + + pane.add_artist(xaxis2) + pane.add_artist(yaxis2) + # uncomment to change Axes wwidth + #pane.width = 0.8 + + if 0: + # XXX: axes lines and tick markes are placed in vastly + # different locations depending on whether this is commented + # or uncommented, suggesting that the problem is caused by + # events not propogating unless lim are changed. If we set + # these lim to be the same as the lim above (note they are + # almost identical) then the graphs are the same regardless of + # whether the lim are set + pane.adata.xlim = 0.01, 10.01 + pane.adata.ylim = -0.101, 1.101 + + def make_subplot_ll(fig): x1 = npy.arange(0, 10., 0.05) x2 = npy.arange(0, 10., 0.1) @@ -1243,9 +1386,7 @@ line1 = Line().set(X=npy.array([x1,y1]).T, - color='blue', linewidth=2.0, marker='s', - markersize=5.0, markerfacecolor='green', - markeredgewidth=0.5) + color='blue', linewidth=2.0) pane.add_artist(line1) @@ -1257,14 +1398,14 @@ pane.xaxis.ticklocs = npy.arange(0., 11., 1.) pane.xaxis.loc = -0.1 pane.xaxis.tickoffset = -0.5 - pane.xaxis.linecolor = 'red' + pane.xaxis.line.color = 'red' - pane.yaxis.ticklocs = npy.arange(-1.0, 1.1, 0.2) + Pane.yaxis.ticklocs = npy.arange(-1.0, 1.1, 0.2) pane.yaxis.loc = -0.1 pane.xaxis.tickoffset = -0.5 - pane.yaxis.linecolor = 'blue' - pane.yaxis.tickcolor = 'blue' + pane.yaxis.line.color = 'blue' + pane.yaxis.tickmarker.color = 'blue' # uncomment to change Axes wwidth #pane.width = 0.8 @@ -1291,19 +1432,6 @@ -class TestContainer(ArtistContainer, Box): - rectangle = traits.Instance(Rectangle, ()) - sequence = 'panes' - - def __init__(self): - ArtistContainer.__init__(self) - self.rectangle.zorder = 0 - self.rectangle.facecolor = 'white' - - print 'setting rect bounds' - self.rectangle.bounds = [0,0,1,1] - print 'set rect bounds' - if __name__=='__main__': renderer = RendererAgg() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-07-22 19:53:46
|
Revision: 3605 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3605&view=rev Author: jdh2358 Date: 2007-07-22 12:53:44 -0700 (Sun, 22 Jul 2007) Log Message: ----------- added vertex array trait handler Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-22 16:53:37 UTC (rev 3604) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-22 19:53:44 UTC (rev 3605) @@ -20,7 +20,7 @@ import enthought.traits.api as traits from enthought.traits.api import HasTraits, Instance, Trait, Float, Int, \ Array, Tuple - +from enthought.traits.trait_numeric import TraitArray from matplotlib import agg from matplotlib import colors as mcolors from matplotlib import cbook @@ -30,7 +30,26 @@ ## begin core infrastructure +class TraitVertexArray(TraitArray): + def __init__ ( self, typecode = None, shape = None, coerce = False ): + TraitArray.__init__(self, typecode, shape, coerce) + + def validate(self, object, name, value): + orig = value + value = TraitArray.validate(self, object, name, value) + if len(value.shape)!=2 or value.shape[1]!=2: + return self.error(object, name, orig) + + return value + + def info(self): + return 'an Nx2 array of doubles which are x,y vertices' + +VertexArray = Trait(npy.array([[0,0], [1,1]], npy.float_), + TraitVertexArray('d')) + + class Affine(HasTraits): """ An affine 3x3 matrix that supports matrix multiplication with @@ -396,23 +415,14 @@ alpha = mtraits.Alpha(1.0) linewidth = mtraits.LineWidth(1.0) antialiased = mtraits.AntiAliased - pathdata =Tuple(Array('b'), Array('d')) + pathdata =Tuple(Array('b'), VertexArray) affine = Instance(Affine, ()) def _pathdata_default(self): return (npy.array([0,0], dtype=npy.uint8), npy.array([[0,0],[0,0]], npy.float_)) - def _pathdata_changed(self, old, new): - codes, xy = new - if len(xy.shape)!=2: - raise ValueError('xy in path data must be Nx2') - Ncodes = len(codes) - Nxy = xy.shape[0] - if Ncodes!=Nxy: - raise ValueError('codes and xy must have equal rows') - class MarkerPrimitive(HasTraits): locs = Array('d') path = Instance(PathPrimitive, ()) # marker path in points @@ -852,7 +862,7 @@ linestyle = mtraits.LineStyle('-') linewidth = mtraits.LineWidth(1.0) model = mtraits.Model - pathdata = traits.Tuple(Array('b'), Array('d')) + pathdata = traits.Tuple(Array('b'), VertexArray) sequence = 'paths' zorder = Float(1.0) @@ -1039,8 +1049,6 @@ #print 'LINE shapes', codes.shape, self.XY.shape self.pathdata = codes, self.XY - # XXX: to we need to push pathdata changed here or will it - # happen automagically class Polygon(Path): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-07-22 20:03:11
|
Revision: 3606 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3606&view=rev Author: jdh2358 Date: 2007-07-22 13:03:08 -0700 (Sun, 22 Jul 2007) Log Message: ----------- added more path codes Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-22 19:53:44 UTC (rev 3605) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-22 20:03:08 UTC (rev 3606) @@ -30,25 +30,7 @@ ## begin core infrastructure -class TraitVertexArray(TraitArray): - def __init__ ( self, typecode = None, shape = None, coerce = False ): - TraitArray.__init__(self, typecode, shape, coerce) - - def validate(self, object, name, value): - orig = value - value = TraitArray.validate(self, object, name, value) - if len(value.shape)!=2 or value.shape[1]!=2: - return self.error(object, name, orig) - - return value - - def info(self): - return 'an Nx2 array of doubles which are x,y vertices' - -VertexArray = Trait(npy.array([[0,0], [1,1]], npy.float_), - TraitVertexArray('d')) - class Affine(HasTraits): """ @@ -317,7 +299,29 @@ def _bounds_changed(self, old, new): pass + + +## begin custom trait handlers +class TraitVertexArray(TraitArray): + + def __init__ ( self, typecode = None, shape = None, coerce = False ): + TraitArray.__init__(self, typecode, shape, coerce) + + def validate(self, object, name, value): + orig = value + value = TraitArray.validate(self, object, name, value) + if len(value.shape)!=2 or value.shape[1]!=2: + return self.error(object, name, orig) + + return value + + def info(self): + return 'an Nx2 array of doubles which are x,y vertices' + +VertexArray = Trait(npy.array([[0,0], [1,1]], npy.float_), + TraitVertexArray('d')) + class ColorHandler(traits.TraitHandler): """ This is a clever little traits mechanism -- users can specify the @@ -401,7 +405,17 @@ ## begin backend API -MOVETO, LINETO, CLOSEPOLY = range(3) +# PATH CODES +STOP = 0 +MOVETO = 1 +LINETO = 2 +CURVE3 = 3 +CURVE4 = 4 +CURVEN = 5 +CATROM = 6 +UBSPLINE = 7 +CLOSEPOLY = 0x0F + class PathPrimitive(HasTraits): """ The path is an object that talks to the backends, and is an @@ -1044,7 +1058,7 @@ def _XY_changed(self): #print 'LINE: XY changed' - codes = npy.ones(len(self.XY), npy.uint8) + codes = LINETO*npy.ones(len(self.XY), npy.uint8) codes[0] = MOVETO #print 'LINE shapes', codes.shape, self.XY.shape self.pathdata = codes, self.XY @@ -1134,7 +1148,7 @@ tickmarker = Instance(Marker, ()) line = Instance(Line, ()) ticklocs = Array('d') - ticksize = Float(5.0) + ticksize = Float(7.0) loc = Float(0.) # the y location of the x-axis This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2007-07-22 20:03:46
|
Revision: 3607 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=3607&view=rev Author: jdh2358 Date: 2007-07-22 13:03:45 -0700 (Sun, 22 Jul 2007) Log Message: ----------- added more path codes Modified Paths: -------------- trunk/matplotlib/mpl1/mpl1.py Modified: trunk/matplotlib/mpl1/mpl1.py =================================================================== --- trunk/matplotlib/mpl1/mpl1.py 2007-07-22 20:03:08 UTC (rev 3606) +++ trunk/matplotlib/mpl1/mpl1.py 2007-07-22 20:03:45 UTC (rev 3607) @@ -506,6 +506,7 @@ for i in range(Ncodes): x, y = xy[i] code = codes[i] + #XXX handle other path codes here if code==MOVETO: agg_path.move_to(x, y) elif code==LINETO: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |