From: <ds...@us...> - 2008-07-17 12:47:44
|
Revision: 5772 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5772&view=rev Author: dsdale Date: 2008-07-17 12:47:22 +0000 (Thu, 17 Jul 2008) Log Message: ----------- Merged revisions 5771 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_91_maint ........ r5771 | dsdale | 2008-07-17 08:01:50 -0400 (Thu, 17 Jul 2008) | 2 lines improve error reporting in texmanager ........ Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/texmanager.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-5723 + /branches/v0_91_maint:1-5771 Added: svn:mergeinfo + /branches/v0_91_maint:5753-5771 Modified: trunk/matplotlib/lib/matplotlib/texmanager.py =================================================================== --- trunk/matplotlib/lib/matplotlib/texmanager.py 2008-07-17 12:01:50 UTC (rev 5771) +++ trunk/matplotlib/lib/matplotlib/texmanager.py 2008-07-17 12:47:22 UTC (rev 5772) @@ -273,16 +273,22 @@ %(os.path.split(texfile)[-1], outfile)) mpl.verbose.report(command, 'debug') exit_status = os.system(command) - fh = file(outfile) + try: + fh = file(outfile) + report = fh.read() + fh.close() + except IOError: + report = 'No latex error report available.' if exit_status: raise RuntimeError(('LaTeX was not able to process the following \ -string:\n%s\nHere is the full report generated by LaTeX: \n\n'% repr(tex)) + fh.read()) - else: mpl.verbose.report(fh.read(), 'debug') - fh.close() +string:\n%s\nHere is the full report generated by LaTeX: \n\n'% repr(tex)) + report) + else: mpl.verbose.report(report, 'debug') for fname in glob.glob(basefile+'*'): if fname.endswith('dvi'): pass elif fname.endswith('tex'): pass - else: os.remove(fname) + else: + try: os.remove(fname) + except OSError: pass return dvifile @@ -305,14 +311,19 @@ os.path.split(dvifile)[-1], outfile)) mpl.verbose.report(command, 'debug') exit_status = os.system(command) - fh = file(outfile) + try: + fh = file(outfile) + report = fh.read() + fh.close() + except IOError: + report = 'No dvipng error report available.' if exit_status: raise RuntimeError('dvipng was not able to \ process the flowing file:\n%s\nHere is the full report generated by dvipng: \ -\n\n'% dvifile + fh.read()) - else: mpl.verbose.report(fh.read(), 'debug') - fh.close() - os.remove(outfile) +\n\n'% dvifile + report) + else: mpl.verbose.report(report, 'debug') + try: os.remove(outfile) + except OSError: pass return pngfile This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-07-17 17:40:49
|
Revision: 5775 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5775&view=rev Author: mdboom Date: 2008-07-17 17:40:47 +0000 (Thu, 17 Jul 2008) Log Message: ----------- Fix problem with NaNs at end of path. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/src/agg_py_path_iterator.h Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-17 17:16:12 UTC (rev 5774) +++ trunk/matplotlib/CHANGELOG 2008-07-17 17:40:47 UTC (rev 5775) @@ -1,3 +1,6 @@ +2008-07-17 Fix bug with NaNs at end of path (thanks, Andrew Straw for + the report) - MGD + 2008-07-12 Added support for external backends with the "module://my_backend" syntax - JDH Modified: trunk/matplotlib/src/agg_py_path_iterator.h =================================================================== --- trunk/matplotlib/src/agg_py_path_iterator.h 2008-07-17 17:16:12 UTC (rev 5774) +++ trunk/matplotlib/src/agg_py_path_iterator.h 2008-07-17 17:40:47 UTC (rev 5775) @@ -75,11 +75,13 @@ { if (m_iterator >= m_total_vertices) return agg::path_cmd_stop; unsigned code = vertex_with_code(m_iterator++, x, y); - while ((MPL_isnan64(*x) || MPL_isnan64(*y)) && - m_iterator < m_total_vertices) - { + if (MPL_isnan64(*x) || MPL_isnan64(*y)) { + do { vertex(m_iterator++, x, y); - code = agg::path_cmd_move_to; + } while ((MPL_isnan64(*x) || MPL_isnan64(*y)) && + m_iterator < m_total_vertices); + return (m_iterator >= m_total_vertices) ? agg::path_cmd_stop : + agg::path_cmd_move_to; } return code; } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-17 18:57:07
|
Revision: 5780 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5780&view=rev Author: jdh2358 Date: 2008-07-17 18:56:48 +0000 (Thu, 17 Jul 2008) Log Message: ----------- some fixes for numpy svn deprecation warnings Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py trunk/matplotlib/lib/mpl_toolkits/exceltools.py trunk/matplotlib/src/_path.cpp trunk/matplotlib/src/_png.cpp trunk/matplotlib/src/ft2font.cpp Modified: trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py 2008-07-17 18:27:36 UTC (rev 5779) +++ trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py 2008-07-17 18:56:48 UTC (rev 5780) @@ -459,7 +459,8 @@ if Gcf.get_num_fig_managers()==0 and \ not matplotlib.is_interactive() and \ gtk.main_level() >= 1: - gtk.main_quit() + #gtk.main_quit() + pass def show(self): # show the figure window Modified: trunk/matplotlib/lib/mpl_toolkits/exceltools.py =================================================================== --- trunk/matplotlib/lib/mpl_toolkits/exceltools.py 2008-07-17 18:27:36 UTC (rev 5779) +++ trunk/matplotlib/lib/mpl_toolkits/exceltools.py 2008-07-17 18:56:48 UTC (rev 5780) @@ -5,7 +5,7 @@ import matplotlib.mlab as mlab import mpl_toolkits.exceltools as exceltools - + r = mlab.csv2rec('somefile.csv', checkrows=0) formatd = dict( @@ -52,7 +52,7 @@ return format -def rec2excel(r, ws, formatd=None, rownum=0, colnum=0): +def rec2excel(r, ws, formatd=None, rownum=0, colnum=0, nanstr='NaN'): """ save record array r to excel pyExcelerator worksheet ws starting at rownum. if ws is string like, assume it is a @@ -62,6 +62,7 @@ formatd is a dictionary mapping dtype name -> mlab.Format instances + nanstr is the string that mpl will put into excel for np.nan value The next rownum after writing is returned """ @@ -106,7 +107,7 @@ ws.write(rownum, colnum+i, val) else: if mlab.safe_isnan(val): - ws.write(rownum, colnum+i, 'NaN') + ws.write(rownum, colnum+i, nanstr) else: ws.write(rownum, colnum+i, val, format.xlstyle) rownum += 1 Modified: trunk/matplotlib/src/_path.cpp =================================================================== --- trunk/matplotlib/src/_path.cpp 2008-07-17 18:27:36 UTC (rev 5779) +++ trunk/matplotlib/src/_path.cpp 2008-07-17 18:56:48 UTC (rev 5780) @@ -857,7 +857,7 @@ { size_t size = p->size(); dims[0] = p->size(); - PyArrayObject* pyarray = (PyArrayObject*)PyArray_FromDims(2, dims, PyArray_DOUBLE); + PyArrayObject* pyarray = (PyArrayObject*)PyArray_SimpleNew(2, dims, PyArray_DOUBLE); for (size_t i = 0; i < size; ++i) { ((double *)pyarray->data)[2*i] = (*p)[i].x; Modified: trunk/matplotlib/src/_png.cpp =================================================================== --- trunk/matplotlib/src/_png.cpp 2008-07-17 18:27:36 UTC (rev 5779) +++ trunk/matplotlib/src/_png.cpp 2008-07-17 18:56:48 UTC (rev 5780) @@ -254,7 +254,7 @@ dimensions[1] = width; //numcols dimensions[2] = 4; - PyArrayObject *A = (PyArrayObject *) PyArray_FromDims(3, dimensions, PyArray_FLOAT); + PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNew(3, dimensions, PyArray_FLOAT); for (png_uint_32 y = 0; y < height; y++) { Modified: trunk/matplotlib/src/ft2font.cpp =================================================================== --- trunk/matplotlib/src/ft2font.cpp 2008-07-17 18:27:36 UTC (rev 5779) +++ trunk/matplotlib/src/ft2font.cpp 2008-07-17 18:56:48 UTC (rev 5780) @@ -274,7 +274,7 @@ /* - PyArrayObject *A = (PyArrayObject *) PyArray_FromDims(2, dimensions, PyArray_UBYTE); + PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNew(2, dimensions, PyArray_UBYTE); unsigned char *src = _buffer; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-17 19:16:55
|
Revision: 5781 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5781&view=rev Author: jdh2358 Date: 2008-07-17 19:15:58 +0000 (Thu, 17 Jul 2008) Log Message: ----------- more fixes for numpy svn deprecation warnings Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/nan_test.py trunk/matplotlib/examples/pylab_examples/quadmesh_demo.py trunk/matplotlib/lib/matplotlib/__init__.py trunk/matplotlib/src/_backend_gdk.c trunk/matplotlib/src/_path.cpp trunk/matplotlib/src/_png.cpp trunk/matplotlib/src/cntr.c trunk/matplotlib/src/nxutils.c Modified: trunk/matplotlib/examples/pylab_examples/nan_test.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/nan_test.py 2008-07-17 18:56:48 UTC (rev 5780) +++ trunk/matplotlib/examples/pylab_examples/nan_test.py 2008-07-17 19:15:58 UTC (rev 5781) @@ -22,8 +22,7 @@ plot(t, s, '-', lw=2) xlabel('time (s)') -ylabel('voltage (mV)') -title('More NaNs at 0.0 and 1.0') +ylabel('more nans') grid(True) show() Modified: trunk/matplotlib/examples/pylab_examples/quadmesh_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/quadmesh_demo.py 2008-07-17 18:56:48 UTC (rev 5780) +++ trunk/matplotlib/examples/pylab_examples/quadmesh_demo.py 2008-07-17 19:15:58 UTC (rev 5781) @@ -40,6 +40,7 @@ # Or use the default, which is transparent: col = ax.pcolormesh(Qx,Qz,Zm) ax.set_title('With masked values') -show() + savefig("quadmesh_demo") +show() Modified: trunk/matplotlib/lib/matplotlib/__init__.py =================================================================== --- trunk/matplotlib/lib/matplotlib/__init__.py 2008-07-17 18:56:48 UTC (rev 5780) +++ trunk/matplotlib/lib/matplotlib/__init__.py 2008-07-17 19:15:58 UTC (rev 5781) @@ -89,7 +89,7 @@ """ from __future__ import generators -__version__ = '0.98.2' +__version__ = '0.98.3' __revision__ = '$Revision$' __date__ = '$Date$' Modified: trunk/matplotlib/src/_backend_gdk.c =================================================================== --- trunk/matplotlib/src/_backend_gdk.c 2008-07-17 18:56:48 UTC (rev 5780) +++ trunk/matplotlib/src/_backend_gdk.c 2008-07-17 19:15:58 UTC (rev 5781) @@ -28,7 +28,7 @@ PyGObject *py_pixbuf; GdkPixbuf *gdk_pixbuf; PyArrayObject *array; - int dims[3] = { 0, 0, 3 }; + npy_intp dims[3] = { 0, 0, 3 }; if (!PyArg_ParseTuple(args, "O!:pixbuf_get_pixels_array", &PyGdkPixbuf_Type, &py_pixbuf)) Modified: trunk/matplotlib/src/_path.cpp =================================================================== --- trunk/matplotlib/src/_path.cpp 2008-07-17 18:56:48 UTC (rev 5780) +++ trunk/matplotlib/src/_path.cpp 2008-07-17 19:15:58 UTC (rev 5781) @@ -846,7 +846,7 @@ ::clip_to_rect(path, x0, y0, x1, y1, inside, results); - int dims[2]; + npy_intp dims[2]; dims[1] = 2; PyObject* py_results = PyList_New(results.size()); if (!py_results) Modified: trunk/matplotlib/src/_png.cpp =================================================================== --- trunk/matplotlib/src/_png.cpp 2008-07-17 18:56:48 UTC (rev 5780) +++ trunk/matplotlib/src/_png.cpp 2008-07-17 19:15:58 UTC (rev 5781) @@ -249,7 +249,7 @@ - int dimensions[3]; + npy_intp dimensions[3]; dimensions[0] = height; //numrows dimensions[1] = width; //numcols dimensions[2] = 4; Modified: trunk/matplotlib/src/cntr.c =================================================================== --- trunk/matplotlib/src/cntr.c 2008-07-17 18:56:48 UTC (rev 5780) +++ trunk/matplotlib/src/cntr.c 2008-07-17 19:15:58 UTC (rev 5781) @@ -1333,7 +1333,7 @@ { PyObject *point, *all_contours; PyArrayObject *xv, *yv; - int dims[1]; + npy_intp dims[1]; int i; long j, k; @@ -1343,8 +1343,8 @@ for (i = 0; i < nparts; i++) { dims[0] = np[i]; - xv = (PyArrayObject *) PyArray_FromDims(1, dims, PyArray_DOUBLE); - yv = (PyArrayObject *) PyArray_FromDims(1, dims, PyArray_DOUBLE); + xv = (PyArrayObject *) PyArray_SimpleNew(1, dims, PyArray_DOUBLE); + yv = (PyArrayObject *) PyArray_SimpleNew(1, dims, PyArray_DOUBLE); if (xv == NULL || yv == NULL) goto error; for (j = 0; j < dims[0]; j++) { @@ -1370,7 +1370,7 @@ { PyObject *all_contours; PyArrayObject *xyv; - int dims[2]; + npy_intp dims[2]; int i; long j, k; @@ -1381,7 +1381,7 @@ { dims[0] = np[i]; dims[1] = 2; - xyv = (PyArrayObject *) PyArray_FromDims(2, dims, PyArray_DOUBLE); + xyv = (PyArrayObject *) PyArray_SimpleNew(2, dims, PyArray_DOUBLE); if (xyv == NULL) goto error; for (j = 0; j < dims[0]; j++) { Modified: trunk/matplotlib/src/nxutils.c =================================================================== --- trunk/matplotlib/src/nxutils.c 2008-07-17 18:56:48 UTC (rev 5780) +++ trunk/matplotlib/src/nxutils.c 2008-07-17 19:15:58 UTC (rev 5781) @@ -108,7 +108,7 @@ PyObject *xypointsarg, *vertsarg, *ret; PyArrayObject *xypoints, *verts; PyArrayObject *mask; - int dimensions[1]; + npy_intp dimensions[1]; if (! PyArg_ParseTuple(args, "OO", &xypointsarg, &vertsarg)) return NULL; @@ -187,7 +187,7 @@ npoints = xypoints->dimensions[0]; dimensions[0] = npoints; - mask = (PyArrayObject *)PyArray_FromDims(1,dimensions,PyArray_INT); + mask = (PyArrayObject *)PyArray_SimpleNew(1,dimensions,PyArray_INT); if (mask==NULL) { Py_XDECREF(verts); Py_XDECREF(xypoints); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-17 21:06:22
|
Revision: 5785 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5785&view=rev Author: jdh2358 Date: 2008-07-17 21:06:17 +0000 (Thu, 17 Jul 2008) Log Message: ----------- removed screen noise comments from gtkagg and image since I no longer consider this to be a bug Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py trunk/matplotlib/src/_image.cpp Modified: trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py 2008-07-17 20:51:38 UTC (rev 5784) +++ trunk/matplotlib/lib/matplotlib/backends/backend_gtkagg.py 2008-07-17 21:06:17 UTC (rev 5785) @@ -81,13 +81,6 @@ w = int(ren.width) h = int(ren.height) - # There apparently is a bug here on some versions of pygtk - # (2.6) that crops up in corner cases. Eg, in - # figimage_demo.py, there is background pixel noise because - # the figure frame is off and the blended image background has - # alpha 0. and you see the uninitialzed data ("screen noise"). - # If in _image.cpp Image:from_image you fill the background - # with a non transparent value, it goes away. pixbuf = gtk.gdk.pixbuf_new_from_data( buf, gtk.gdk.COLORSPACE_RGB, True, 8, w, h, w*4) pixmap.draw_pixbuf(pixmap.new_gc(), pixbuf, 0, 0, 0, 0, w, h, Modified: trunk/matplotlib/src/_image.cpp =================================================================== --- trunk/matplotlib/src/_image.cpp 2008-07-17 20:51:38 UTC (rev 5784) +++ trunk/matplotlib/src/_image.cpp 2008-07-17 21:06:17 UTC (rev 5785) @@ -743,9 +743,6 @@ renderer_base rb(pixf); - //clear the background of the rendering buffer with alpha 1 and the - //gtkagg screen noise problem in figimage_demo.py goes away -- see - //comment backend_gtkagg.py _render_figure method JDH rb.clear(agg::rgba(1, 1, 1, 1)); for (size_t imnum=0; imnum< N; imnum++) { This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dmk...@us...> - 2008-07-17 19:21:20
|
Revision: 5782 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5782&view=rev Author: dmkaplan Date: 2008-07-17 19:20:32 +0000 (Thu, 17 Jul 2008) Log Message: ----------- Adding more functionality for interacting with figure windows through mouse clicks and keyboard clicks. This includes adding a waitforbuttonpress function, as well as manual contour label location selection for clabel. The underlying Blocking* classes that drive this interaction have been moved into a separate file "lib/matplotlib/blocking_input.py". Also added a changelog entry. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/contour.py trunk/matplotlib/lib/matplotlib/figure.py trunk/matplotlib/lib/matplotlib/pyplot.py Added Paths: ----------- trunk/matplotlib/lib/matplotlib/blocking_input.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-17 19:15:58 UTC (rev 5781) +++ trunk/matplotlib/CHANGELOG 2008-07-17 19:20:32 UTC (rev 5782) @@ -1,3 +1,6 @@ +2008-07-17 Added ability to manually select contour label locations. + Also added a waitforbuttonpress function. - DMK + 2008-07-17 Fix bug with NaNs at end of path (thanks, Andrew Straw for the report) - MGD Added: trunk/matplotlib/lib/matplotlib/blocking_input.py =================================================================== --- trunk/matplotlib/lib/matplotlib/blocking_input.py (rev 0) +++ trunk/matplotlib/lib/matplotlib/blocking_input.py 2008-07-17 19:20:32 UTC (rev 5782) @@ -0,0 +1,540 @@ +""" +This provides several classes used for blocking interaction with figure windows: + +:class:`BlockingInput` + creates a callable object to retrieve events in a blocking way for interactive sessions + +:class:`BlockingKeyMouseInput` + creates a callable object to retrieve key or mouse clicks in a blocking way for interactive sessions. + Note: Subclass of BlockingInput. Used by waitforbuttonpress + +:class:`BlockingMouseInput` + creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions. + Note: Subclass of BlockingInput. Used by ginput + +:class:`BlockingContourLabeler` + creates a callable object to retrieve mouse clicks in a blocking way that will then be used to place labels on a ContourSet + Note: Subclass of BlockingMouseInput. Used by clabel +""" + +import time +import numpy as np +import matplotlib.path as path + +class BlockingInput(object): + """ + Class that creates a callable object to retrieve events in a + blocking way. + """ + def __init__(self, fig, eventslist=()): + self.fig = fig + assert isinstance(eventslist, tuple), "Requires a tuple of event name strings" + self.eventslist = eventslist + + def on_event(self, event): + """ + Event handler that will be passed to the current figure to + retrieve events. + """ + # Add a new event to list - using a separate function is + # overkill for the base class, but this is consistent with + # subclasses + self.add_event(event) + + if self.verbose: + print "Event %i" % len(self.events) + + # This will extract info from events + self.post_event() + + # Check if we have enough events already + if len(self.events) >= self.n and self.n > 0: + self.done = True + + def post_event(self): + """For baseclass, do nothing but collect events""" + pass + + def cleanup(self): + """Disconnect all callbacks""" + for cb in self.callbacks: + self.fig.canvas.mpl_disconnect(cb) + + self.callbacks=[] + + def add_event(self,event): + """For base class, this just appends an event to events.""" + self.events.append(event) + + def pop_event(self,index=-1): + """ + This removes an event from the event list. Defaults to + removing last event, but an index can be supplied. Note that + this does not check that there are events, much like the + normal pop method. If not events exist, this will throw an + exception. + """ + self.events.pop(index) + + def pop(self,index=-1): + self.pop_event(index) + pop.__doc__=pop_event.__doc__ + + def __call__(self, n=1, timeout=30, verbose=False ): + """ + Blocking call to retrieve n events + """ + + assert isinstance(n, int), "Requires an integer argument" + self.n = n + + self.events = [] + self.done = False + self.verbose = verbose + self.callbacks = [] + + # Ensure that the figure is shown + self.fig.show() + + # connect the events to the on_event function call + for n in self.eventslist: + self.callbacks.append( self.fig.canvas.mpl_connect(n, self.on_event) ) + + try: + # wait for n clicks + counter = 0 + while not self.done: + self.fig.canvas.flush_events() + time.sleep(0.01) + + # check for a timeout + counter += 1 + if timeout > 0 and counter > timeout/0.01: + print "Timeout reached"; + break; + finally: # Activated on exception like ctrl-c + self.cleanup() + + # Disconnect the callbacks + self.cleanup() + + # Return the events in this case + return self.events + +class BlockingMouseInput(BlockingInput): + """ + Class that creates a callable object to retrieve mouse clicks in a + blocking way. + """ + def __init__(self, fig): + BlockingInput.__init__(self, fig=fig, + eventslist=('button_press_event',) ) + + def post_event(self): + """ + This will be called to process events + """ + assert len(self.events)>0, "No events yet" + + event = self.events[-1] + button = event.button + + # Using additional methods for each button is a bit overkill + # for this class, but it allows for easy overloading. Also, + # this would make it easy to attach other type of non-mouse + # events to these "mouse" actions. For example, the matlab + # version of ginput also allows you to add points with + # keyboard clicks. This could easily be added to this class + # with relatively minor modification to post_event and + # __init__. + if button == 3: + self.button3(event) + elif button == 2: + self.button2(event) + else: + self.button1(event) + + def button1( self, event ): + """ + Will be called for any event involving a button other than + button 2 or 3. This will add a click if it is inside axes. + """ + if event.inaxes: + self.add_click(event) + else: # If not a valid click, remove from event list + BlockingInput.pop(self) + + def button2( self, event ): + """ + Will be called for any event involving button 2. + Button 2 ends blocking input. + """ + + # Remove last event just for cleanliness + BlockingInput.pop(self) + + # This will exit even if not in infinite mode. This is + # consistent with matlab and sometimes quite useful, but will + # require the user to test how many points were actually + # returned before using data. + self.done = True + + def button3( self, event ): + """ + Will be called for any event involving button 3. + Button 3 removes the last click. + """ + # Remove this last event + BlockingInput.pop(self) + + # Now remove any existing clicks if possible + if len(self.events)>0: + self.pop() + + def add_click(self,event): + """ + This add the coordinates of an event to the list of clicks + """ + self.clicks.append((event.xdata,event.ydata)) + if self.verbose: + print "input %i: %f,%f" % (len(self.clicks), + event.xdata, event.ydata) + + # If desired plot up click + if self.show_clicks: + self.marks.extend( + event.inaxes.plot([event.xdata,], [event.ydata,], 'r+') ) + self.fig.canvas.draw() + + def pop_click(self,index=-1): + """ + This removes a click from the list of clicks. Defaults to + removing the last click. + """ + self.clicks.pop(index) + + if self.show_clicks: + mark = self.marks.pop(index) + mark.remove() + self.fig.canvas.draw() + + def pop(self,index=-1): + """ + This removes a click and the associated event from the object. + Defaults to removing the last click, but any index can be + supplied. + """ + self.pop_click(index) + BlockingInput.pop(self,index) + + def cleanup(self): + # clean the figure + if self.show_clicks: + for mark in self.marks: + mark.remove() + self.marks = [] + self.fig.canvas.draw() + + # Call base class to remove callbacks + BlockingInput.cleanup(self) + + def __call__(self, n=1, timeout=30, verbose=False, show_clicks=True): + """ + Blocking call to retrieve n coordinate pairs through mouse + clicks. + """ + self.show_clicks = show_clicks + self.clicks = [] + self.marks = [] + BlockingInput.__call__(self,n=n,timeout=timeout,verbose=verbose) + + return self.clicks + +class BlockingContourLabeler( BlockingMouseInput ): + """ + Class that creates a callable object that uses mouse clicks on a + figure window to place contour labels. + """ + def __init__(self,cs): + self.cs = cs + BlockingMouseInput.__init__(self, fig=cs.ax.figure ) + + def button1(self,event): + """ + This will be called if an event involving a button other than + 2 or 3 occcurs. This will add a label to a contour. + """ + if event.inaxes == self.cs.ax: + conmin,segmin,imin,xmin,ymin = self.cs.find_nearest_contour( + event.x, event.y)[:5] + + paths = self.cs.collections[conmin].get_paths() + lc = paths[segmin].vertices + + # Figure out label rotation. This is very cludgy. + # Ideally, there would be one method in ContourLabeler + # that would figure out the best rotation for a label at a + # point, but the way automatic label rotation is done is + # quite mysterious to me and doesn't seem easy to + # generalize to non-automatic label placement. The method + # used below is not very robust! It basically looks one + # point before and one point after label location on + # contour and takes mean of angles of two vectors formed. + # This produces "acceptable" results, but not nearly as + # nice as automatic method. + ll = lc[max(0,imin-1):imin+2] # Get points around point + dd = np.diff(ll,axis=0) + rotation = np.mean( np.arctan2(dd[:,1], dd[:,0]) ) * 180 / np.pi + if rotation > 90: + rotation = rotation -180 + if rotation < -90: + rotation = 180 + rotation + + self.cs.add_label(xmin,ymin,rotation,conmin) + + if self.inline: + # Get label width for breaking contours + lw = self.cs.get_label_width(self.cs.label_levels[conmin], + self.cs.fmt, + self.cs.fslist[conmin]) + # Break contour + new=self.cs.break_linecontour(lc,rotation,lw,imin) + if len(new[0]): + paths[segmin] = path.Path(new[0]) + if len(new[1]): + paths.extend([path.Path(new[1])]) + + self.fig.canvas.draw() + else: # Remove event if not valid + BlockingInput.pop(self) + + def button3(self,event): + """ + This will be called if button 3 is clicked. This will remove + a label if not in inline mode. Unfortunately, if one is doing + inline labels, then there is currently no way to fix the + broken contour - once humpty-dumpty is broken, he can't be put + back together. In inline mode, this does nothing. + """ + if self.inline: + pass + else: + self.cs.pop_label() + self.cs.ax.figure.canvas.draw() + + def __call__(self,inline,n=-1,timeout=-1): + self.inline=inline + BlockingMouseInput.__call__(self,n=n,timeout=timeout,verbose=False, + show_clicks=False) + +class BlockingKeyMouseInput(BlockingInput): + """ + Class that creates a callable object to retrieve a single mouse or + keyboard click + """ + def __init__(self, fig): + BlockingInput.__init__(self, fig=fig, eventslist=('button_press_event','key_press_event') ) + + def post_event(self): + """ + Determines if it is a key event + """ + assert len(self.events)>0, "No events yet" + + self.keyormouse = self.events[-1].name == 'key_press_event' + + def __call__(self, timeout=30, verbose=False): + """ + Blocking call to retrieve a single mouse or key click + Returns True if key click, False if mouse, or None if timeout + """ + self.keyormouse = None + BlockingInput.__call__(self,n=1,timeout=timeout,verbose=verbose) + + return self.keyormouse + +""" +This provides several classes used for interaction with figure windows: + +:class:`BlockingInput` + creates a callable object to retrieve events in a blocking way for interactive sessions + +:class:`BlockingKeyMouseInput` + creates a callable object to retrieve key or mouse clicks in a blocking way for interactive sessions. + Note: Subclass of BlockingInput. Used by waitforbuttonpress + +:class:`BlockingMouseInput` + creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions. + Note: Subclass of BlockingInput. Used by ginput +""" + +import time + +class BlockingInput(object): + """ + Class that creates a callable object to retrieve events in a + blocking way. + """ + def __init__(self, fig, eventslist=()): + self.fig = fig + assert isinstance(eventslist, tuple), \ + "Requires a tuple of event name strings" + self.eventslist = eventslist + + def on_event(self, event): + """ + Event handler that will be passed to the current figure to + retrieve events. + """ + self.events.append(event) + + if self.verbose: + print "Event %i" % len(self.events) + + # This will extract info from events + self.post_event() + + if len(self.events) >= self.n and self.n > 0: + self.done = True + + def post_event(self): + """For baseclass, do nothing but collect events""" + pass + + def cleanup(self): + """Remove callbacks""" + for cb in self.callbacks: + self.fig.canvas.mpl_disconnect(cb) + + self.callbacks=[] + + def __call__(self, n=1, timeout=30, verbose=False ): + """ + Blocking call to retrieve n events + """ + + assert isinstance(n, int), "Requires an integer argument" + self.n = n + + self.events = [] + self.done = False + self.verbose = verbose + self.callbacks = [] + + # Ensure that the figure is shown + self.fig.show() + + # connect the events to the on_event function call + for n in self.eventslist: + self.callbacks.append( self.fig.canvas.mpl_connect(n, self.on_event) ) + + try: + # wait for n clicks + counter = 0 + while not self.done: + self.fig.canvas.flush_events() + time.sleep(0.01) + + # check for a timeout + counter += 1 + if timeout > 0 and counter > timeout/0.01: + print "Timeout reached"; + break; + finally: # Activated on exception like ctrl-c + self.cleanup() + + # Disconnect the callbacks + self.cleanup() + + # Return the events in this case + return self.events + +class BlockingMouseInput(BlockingInput): + """ + Class that creates a callable object to retrieve mouse clicks in a + blocking way. + """ + def __init__(self, fig): + BlockingInput.__init__(self, fig=fig, eventslist=('button_press_event',) ) + + def post_event(self): + """ + This will be called to process events + """ + assert len(self.events)>0, "No events yet" + + event = self.events[-1] + + if event.button == 3: + # If it's a right click, pop the last coordinates. + if len(self.clicks) > 0: + self.clicks.pop() + del self.events[-2:] # Remove button=3 event and previous event + + if self.show_clicks: + mark = self.marks.pop() + mark.remove() + self.fig.canvas.draw() + elif event.button == 2 and self.n < 0: + # If it's a middle click, and we are in infinite mode, finish + self.done = True + elif event.inaxes: + # If it's a valid click, append the coordinates to the list + self.clicks.append((event.xdata, event.ydata)) + if self.verbose: + print "input %i: %f,%f" % (len(self.clicks), + event.xdata, event.ydata) + if self.show_clicks: + self.marks.extend( + event.inaxes.plot([event.xdata,], [event.ydata,], 'r+') ) + self.fig.canvas.draw() + + def cleanup(self): + # clean the figure + if self.show_clicks: + for mark in self.marks: + mark.remove() + self.marks = [] + self.fig.canvas.draw() + + # Call base class to remove callbacks + BlockingInput.cleanup(self) + + def __call__(self, n=1, timeout=30, verbose=False, show_clicks=True): + """ + Blocking call to retrieve n coordinate pairs through mouse + clicks. + """ + self.show_clicks = show_clicks + self.clicks = [] + self.marks = [] + BlockingInput.__call__(self,n=n,timeout=timeout,verbose=verbose) + + return self.clicks + +class BlockingKeyMouseInput(BlockingInput): + """ + Class that creates a callable object to retrieve a single mouse or + keyboard click + """ + def __init__(self, fig): + BlockingInput.__init__(self, fig=fig, eventslist=('button_press_event','key_press_event') ) + + def post_event(self): + """ + Determines if it is a key event + """ + assert len(self.events)>0, "No events yet" + + self.keyormouse = self.events[-1].name == 'key_press_event' + + def __call__(self, timeout=30, verbose=False): + """ + Blocking call to retrieve a single mouse or key click + Returns True if key click, False if mouse, or None if timeout + """ + self.keyormouse = None + BlockingInput.__call__(self,n=1,timeout=timeout,verbose=verbose) + + return self.keyormouse + Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2008-07-17 19:15:58 UTC (rev 5781) +++ trunk/matplotlib/lib/matplotlib/contour.py 2008-07-17 19:20:32 UTC (rev 5782) @@ -17,6 +17,9 @@ import matplotlib.text as text import matplotlib.cbook as cbook +# Import needed for adding manual selection capability to clabel +from matplotlib.blocking_input import BlockingContourLabeler + # We can't use a single line collection for contour because a line # collection can have only a single line style, and we want to be able to have # dashed negative contours, for example, and solid positive contours. @@ -69,6 +72,13 @@ *fmt*: a format string for the label. Default is '%1.3f' + *manual*: + if *True*, contour labels will be placed manually using + mouse clicks. Click the first button near a contour to + add a label, click the second button (or potentially both + mouse buttons at once) to finish adding labels. The third + button can be used to remove the last label added, but + only if labels are not inline. """ fontsize = kwargs.get('fontsize', None) @@ -76,8 +86,9 @@ self.fmt = kwargs.get('fmt', '%1.3f') _colors = kwargs.get('colors', None) + # Detect if manual selection is desired and remove from argument list + self.manual_select=kwargs.get('manual',False) - if len(args) == 0: levels = self.levels indices = range(len(self.levels)) @@ -126,10 +137,16 @@ #self.cl_cvalues = [] # same self.cl_xy = [] - self.labels(inline) + if self.manual_select: + print 'Select label locations manually using first mouse button.' + print 'End manual selection with second mouse button.' + if not inline: + print 'Remove last label by clicking third mouse button.' - for label in self.cl: - self.ax.add_artist(label) + blocking_contour_labeler = BlockingContourLabeler(self) + blocking_contour_labeler(inline) + else: + self.labels(inline) self.label_list = cbook.silent_list('text.Text', self.cl) return self.label_list @@ -335,19 +352,85 @@ return x,y, rotation, dind + def add_label(self,x,y,rotation,icon): + dx,dy = self.ax.transData.inverted().transform_point((x,y)) + t = text.Text(dx, dy, rotation = rotation, + horizontalalignment='center', + verticalalignment='center') + + color = self.label_mappable.to_rgba(self.label_cvalues[icon], + alpha=self.alpha) + + _text = self.get_text(self.label_levels[icon],self.fmt) + self.set_label_props(t, _text, color) + self.cl.append(t) + self.cl_cvalues.append(self.label_cvalues[icon]) + + # Add label to plot here - useful for manual mode label selection + self.ax.add_artist(t) + + def pop_label(self,index=-1): + '''Defaults to removing last label, but any index can be supplied''' + self.cl_cvalues.pop(index) + t = self.cl.pop(index) + t.remove() + + def find_nearest_contour( self, x, y, pixel=True ): + """ + Finds contour that is closest to a point. Defaults to + measuring distance in pixels (screen space - useful for manual + contour labeling), but this can be controlled via a keyword + argument. + + Returns a tuple containing the contour, segment, index of + segment, x & y of segment point and distance to minimum point. + """ + + # This function uses a method that is probably quite + # inefficient based on converting each contour segment to + # pixel coordinates and then comparing the given point to + # those coordinates for each contour. This will probably be + # quite slow for complex contours, but for normal use it works + # sufficiently well that the time is not noticeable. + # Nonetheless, improvements could probably be made. + + dmin = 1e10 + conmin = None + segmin = None + xmin = None + ymin = None + + for icon in self.label_indices: + con = self.collections[icon] + paths = con.get_paths() + for segNum, linepath in enumerate(paths): + lc = linepath.vertices + + # transfer all data points to screen coordinates if desired + if pixel: + lc = self.ax.transData.transform(lc) + + ds = (lc[:,0]-x)**2 + (lc[:,1]-y)**2 + d = min( ds ) + if d < dmin: + dmin = d + conmin = icon + segmin = segNum + imin = mpl.mlab.find( ds == d )[0] + xmin = lc[imin,0] + ymin = lc[imin,1] + + return (conmin,segmin,imin,xmin,ymin,dmin) + def labels(self, inline): levels = self.label_levels fslist = self.fslist trans = self.ax.transData - _colors = self.label_mappable.to_rgba(self.label_cvalues, - alpha=self.alpha) - fmt = self.fmt - for icon, lev, color, cvalue, fsize in zip(self.label_indices, - self.label_levels, - _colors, - self.label_cvalues, fslist): + + for icon, lev, fsize in zip(self.label_indices, + self.label_levels, fslist): con = self.collections[icon] - lw = self.get_label_width(lev, fmt, fsize) + lw = self.get_label_width(lev, self.fmt, fsize) additions = [] paths = con.get_paths() for segNum, linepath in enumerate(paths): @@ -362,16 +445,8 @@ slc = trans.transform(linecontour) if self.print_label(slc,lw): x,y, rotation, ind = self.locate_label(slc, lw) - # transfer the location of the label back to - # data coordinates - dx,dy = trans.inverted().transform_point((x,y)) - t = text.Text(dx, dy, rotation = rotation, - horizontalalignment='center', - verticalalignment='center') - _text = self.get_text(lev,fmt) - self.set_label_props(t, _text, color) - self.cl.append(t) - self.cl_cvalues.append(cvalue) + self.add_label(x,y,rotation,icon) + if inline: new = self.break_linecontour(linecontour, rotation, lw, ind) if len(new[0]): Modified: trunk/matplotlib/lib/matplotlib/figure.py =================================================================== --- trunk/matplotlib/lib/matplotlib/figure.py 2008-07-17 19:15:58 UTC (rev 5781) +++ trunk/matplotlib/lib/matplotlib/figure.py 2008-07-17 19:20:32 UTC (rev 5782) @@ -6,9 +6,6 @@ :class:`SubplotParams` control the default spacing of the subplots -:class:`BlockingMouseInput` - creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions - :class:`Figure` top level container for all plot elements @@ -32,6 +29,7 @@ from transforms import Affine2D, Bbox, BboxTransformTo, TransformedBbox from projections import projection_factory, get_projection_names, \ get_projection_class +from matplotlib.blocking_input import BlockingMouseInput, BlockingKeyMouseInput import matplotlib.cbook as cbook @@ -117,87 +115,6 @@ setattr(self, s, val) - -class BlockingMouseInput(object): - """ - Class that creates a callable object to retrieve mouse clicks in a - blocking way. - """ - def __init__(self, fig): - self.fig = fig - - - def on_click(self, event): - """ - Event handler that will be passed to the current figure to - retrieve clicks. - """ - if event.button == 3: - # If it's a right click, pop the last coordinates. - if len(self.clicks) > 0: - self.clicks.pop() - if self.show_clicks: - mark = self.marks.pop() - mark.remove() - self.fig.canvas.draw() - elif event.button == 2 and self.n < 0: - # If it's a middle click, and we are in infinite mode, finish - self.done = True - elif event.inaxes: - # If it's a valid click, append the coordinates to the list - self.clicks.append((event.xdata, event.ydata)) - if self.verbose: - print "input %i: %f,%f" % (len(self.clicks), - event.xdata, event.ydata) - if self.show_clicks: - self.marks.extend( - event.inaxes.plot([event.xdata,], [event.ydata,], 'r+') ) - self.fig.canvas.draw() - if self.n > 0 and len(self.clicks) >= self.n: - self.done = True - - - def __call__(self, n=1, timeout=30, verbose=False, show_clicks=True): - """ - Blocking call to retrieve n coordinate pairs through mouse - clicks. - """ - self.verbose = verbose - self.done = False - self.clicks = [] - self.show_clicks = True - self.marks = [] - - assert isinstance(n, int), "Requires an integer argument" - self.n = n - - # Ensure that the figure is shown - self.fig.show() - # connect the click events to the on_click function call - self.callback = self.fig.canvas.mpl_connect('button_press_event', - self.on_click) - # wait for n clicks - counter = 0 - while not self.done: - self.fig.canvas.flush_events() - time.sleep(0.01) - - # check for a timeout - counter += 1 - if timeout > 0 and counter > timeout/0.01: - print "ginput timeout"; - break; - - # Disconnect the event, clean the figure, and return what we have - self.fig.canvas.mpl_disconnect(self.callback) - self.callback = None - if self.show_clicks: - for mark in self.marks: - mark.remove() - self.fig.canvas.draw() - return self.clicks - - class Figure(Artist): """ @@ -1121,10 +1038,27 @@ blocking_mouse_input = BlockingMouseInput(self) return blocking_mouse_input(n=n, timeout=timeout, - verbose=verbose, show_clicks=True) + verbose=verbose, show_clicks=show_clicks) + def waitforbuttonpress(self, timeout=-1): + """ + call signature:: + waitforbuttonpress(self, timeout=-1) + Blocking call to interact with the figure. + + This will return True is a key was pressed, False if a mouse + button was pressed and None if *timeout* was reached without + either being pressed. + + If *timeout* is negative, does not timeout. + """ + + blocking_input = BlockingKeyMouseInput(self) + return blocking_input(timeout=timeout) + + def figaspect(arg): """ Create a figure with specified aspect ratio. If *arg* is a number, Modified: trunk/matplotlib/lib/matplotlib/pyplot.py =================================================================== --- trunk/matplotlib/lib/matplotlib/pyplot.py 2008-07-17 19:15:58 UTC (rev 5781) +++ trunk/matplotlib/lib/matplotlib/pyplot.py 2008-07-17 19:20:32 UTC (rev 5782) @@ -320,7 +320,21 @@ if Figure.ginput.__doc__ is not None: ginput.__doc__ = dedent(Figure.ginput.__doc__) +def waitforbuttonpress(*args, **kwargs): + """ + Blocking call to interact with the figure. + This will wait for *n* key or mouse clicks from the user and + return a list containing True's for keyboard clicks and False's + for mouse clicks. + + If *timeout* is negative, does not timeout. + """ + return gcf().waitforbuttonpress(*args, **kwargs) +if Figure.waitforbuttonpress.__doc__ is not None: + waitforbuttonpress.__doc__ = dedent(Figure.waitforbuttonpress.__doc__) + + # Putting things in figures def figtext(*args, **kwargs): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-18 14:49:15
|
Revision: 5788 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5788&view=rev Author: jdh2358 Date: 2008-07-18 14:49:09 +0000 (Fri, 18 Jul 2008) Log Message: ----------- Merged revisions 5787 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_91_maint ........ r5787 | jdh2358 | 2008-07-18 09:46:32 -0500 (Fri, 18 Jul 2008) | 1 line added Tuukka's YAArrow fix for horiz and vertical lines ........ Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/patches.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-5771 + /branches/v0_91_maint:1-5787 Modified: trunk/matplotlib/lib/matplotlib/patches.py =================================================================== --- trunk/matplotlib/lib/matplotlib/patches.py 2008-07-18 14:46:32 UTC (rev 5787) +++ trunk/matplotlib/lib/matplotlib/patches.py 2008-07-18 14:49:09 UTC (rev 5788) @@ -858,6 +858,12 @@ *y2*) of the returned points is *k*. """ x1,y1,x2,y2,k = map(float, (x1,y1,x2,y2,k)) + + if y2-y1 == 0: + return x2, y2+k, x2, y2-k + elif x2-x1 == 0: + return x2+k, y2, x2-k, y2 + m = (y2-y1)/(x2-x1) pm = -1./m a = 1 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <as...@us...> - 2008-07-18 23:15:40
|
Revision: 5791 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5791&view=rev Author: astraw Date: 2008-07-18 23:15:37 +0000 (Fri, 18 Jul 2008) Log Message: ----------- Check for nan and inf in axes.delete_masked_points(). Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/axes.py Added Paths: ----------- trunk/matplotlib/unit/axes_unit.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-18 20:43:58 UTC (rev 5790) +++ trunk/matplotlib/CHANGELOG 2008-07-18 23:15:37 UTC (rev 5791) @@ -1,3 +1,6 @@ +2008-07-18 Check for nan and inf in axes.delete_masked_points(). + This should help hexbin and scatter deal with nans. - ADS + 2008-07-17 Added ability to manually select contour label locations. Also added a waitforbuttonpress function. - DMK Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-07-18 20:43:58 UTC (rev 5790) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-07-18 23:15:37 UTC (rev 5791) @@ -1,5 +1,5 @@ from __future__ import division, generators -import math, sys, warnings, datetime, new +import math, sys, warnings, datetime, new, types import numpy as np from numpy import ma @@ -36,6 +36,8 @@ Find all masked points in a set of arguments, and return the arguments with only the unmasked points remaining. + This will also delete any points that are not finite (nan or inf). + The overall mask is calculated from any masks that are present. If a mask is found, any argument that does not have the same dimensions is left unchanged; therefore the argument list may @@ -49,9 +51,11 @@ useful. """ masks = [ma.getmaskarray(x) for x in args if hasattr(x, 'mask')] + isfinite = [np.isfinite(x) for x in args] + masks.extend( [~x for x in isfinite if not isinstance(x,types.NotImplementedType)] ) if len(masks) == 0: return args - mask = reduce(ma.mask_or, masks) + mask = reduce(np.logical_or, masks) margs = [] for x in args: if (not is_string_like(x) Added: trunk/matplotlib/unit/axes_unit.py =================================================================== --- trunk/matplotlib/unit/axes_unit.py (rev 0) +++ trunk/matplotlib/unit/axes_unit.py 2008-07-18 23:15:37 UTC (rev 5791) @@ -0,0 +1,62 @@ +import unittest +import numpy as np +import matplotlib.axes as axes + +class TestAxes(unittest.TestCase): + def test_delete_masked_points_arrays(self): + input = ( [1,2,3,np.nan,5], + np.array((1,2,3,4,5)), + ) + expected = [np.array((1,2,3,5))]*2 + actual = axes.delete_masked_points(*input) + assert np.allclose(actual, expected) + + input = ( np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ), + np.array((1,2,3,4,5)), + ) + expected = [np.array((1,2,3,5))]*2 + actual = axes.delete_masked_points(*input) + assert np.allclose(actual, expected) + + input = ( [1,2,3,np.nan,5], + np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ), + np.array((1,2,3,4,5)), + ) + expected = [np.array((1,2,3,5))]*3 + actual = axes.delete_masked_points(*input) + assert np.allclose(actual, expected) + + input = () + expected = () + actual = axes.delete_masked_points(*input) + assert np.allclose(actual, expected) + + + input = ( [1,2,3,np.nan,5], + ) + expected = [np.array((1,2,3,5))]*1 + actual = axes.delete_masked_points(*input) + assert np.allclose(actual, expected) + + input = ( np.array((1,2,3,4,5)), + ) + expected = [np.array((1,2,3,4,5))]*1 + actual = axes.delete_masked_points(*input) + assert np.allclose(actual, expected) + + def test_delete_masked_points_strings(self): + input = ( 'hello', + ) + expected = ('hello',) + actual = axes.delete_masked_points(*input) + assert actual == expected + + input = ( u'hello', + ) + expected = (u'hello',) + actual = axes.delete_masked_points(*input) + assert actual == expected + + +if __name__=='__main__': + unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2008-07-20 18:21:17
|
Revision: 5796 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5796&view=rev Author: efiring Date: 2008-07-20 18:21:11 +0000 (Sun, 20 Jul 2008) Log Message: ----------- Moved and modified axes_unit.py to cbook_unit.py. At present it is only testing delete_masked_points, which was moved from axes to cbook. Modified Paths: -------------- trunk/matplotlib/CHANGELOG Added Paths: ----------- trunk/matplotlib/unit/cbook_unit.py Removed Paths: ------------- trunk/matplotlib/unit/axes_unit.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-20 07:14:46 UTC (rev 5795) +++ trunk/matplotlib/CHANGELOG 2008-07-20 18:21:11 UTC (rev 5796) @@ -1,8 +1,13 @@ + +2008-07-20 Renamed unit/axes_unit.py to cbook_unit.py and modified + in accord with Ryan's move of delete_masked_points from + axes to cbook. - EF + 2008-07-18 Check for nan and inf in axes.delete_masked_points(). - This should help hexbin and scatter deal with nans. - ADS + This should help hexbin and scatter deal with nans. - ADS 2008-07-17 Added ability to manually select contour label locations. - Also added a waitforbuttonpress function. - DMK + Also added a waitforbuttonpress function. - DMK 2008-07-17 Fix bug with NaNs at end of path (thanks, Andrew Straw for the report) - MGD Deleted: trunk/matplotlib/unit/axes_unit.py =================================================================== --- trunk/matplotlib/unit/axes_unit.py 2008-07-20 07:14:46 UTC (rev 5795) +++ trunk/matplotlib/unit/axes_unit.py 2008-07-20 18:21:11 UTC (rev 5796) @@ -1,62 +0,0 @@ -import unittest -import numpy as np -import matplotlib.axes as axes - -class TestAxes(unittest.TestCase): - def test_delete_masked_points_arrays(self): - input = ( [1,2,3,np.nan,5], - np.array((1,2,3,4,5)), - ) - expected = [np.array((1,2,3,5))]*2 - actual = axes.delete_masked_points(*input) - assert np.allclose(actual, expected) - - input = ( np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ), - np.array((1,2,3,4,5)), - ) - expected = [np.array((1,2,3,5))]*2 - actual = axes.delete_masked_points(*input) - assert np.allclose(actual, expected) - - input = ( [1,2,3,np.nan,5], - np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ), - np.array((1,2,3,4,5)), - ) - expected = [np.array((1,2,3,5))]*3 - actual = axes.delete_masked_points(*input) - assert np.allclose(actual, expected) - - input = () - expected = () - actual = axes.delete_masked_points(*input) - assert np.allclose(actual, expected) - - - input = ( [1,2,3,np.nan,5], - ) - expected = [np.array((1,2,3,5))]*1 - actual = axes.delete_masked_points(*input) - assert np.allclose(actual, expected) - - input = ( np.array((1,2,3,4,5)), - ) - expected = [np.array((1,2,3,4,5))]*1 - actual = axes.delete_masked_points(*input) - assert np.allclose(actual, expected) - - def test_delete_masked_points_strings(self): - input = ( 'hello', - ) - expected = ('hello',) - actual = axes.delete_masked_points(*input) - assert actual == expected - - input = ( u'hello', - ) - expected = (u'hello',) - actual = axes.delete_masked_points(*input) - assert actual == expected - - -if __name__=='__main__': - unittest.main() Copied: trunk/matplotlib/unit/cbook_unit.py (from rev 5795, trunk/matplotlib/unit/axes_unit.py) =================================================================== --- trunk/matplotlib/unit/cbook_unit.py (rev 0) +++ trunk/matplotlib/unit/cbook_unit.py 2008-07-20 18:21:11 UTC (rev 5796) @@ -0,0 +1,62 @@ +import unittest +import numpy as np +import matplotlib.cbook as cbook + +class TestAxes(unittest.TestCase): + def test_delete_masked_points_arrays(self): + input = ( [1,2,3,np.nan,5], + np.array((1,2,3,4,5)), + ) + expected = [np.array((1,2,3,5))]*2 + actual = cbook.delete_masked_points(*input) + assert np.allclose(actual, expected) + + input = ( np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ), + np.array((1,2,3,4,5)), + ) + expected = [np.array((1,2,3,5))]*2 + actual = cbook.delete_masked_points(*input) + assert np.allclose(actual, expected) + + input = ( [1,2,3,np.nan,5], + np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ), + np.array((1,2,3,4,5)), + ) + expected = [np.array((1,2,3,5))]*3 + actual = cbook.delete_masked_points(*input) + assert np.allclose(actual, expected) + + input = () + expected = () + actual = cbook.delete_masked_points(*input) + assert np.allclose(actual, expected) + + + input = ( [1,2,3,np.nan,5], + ) + expected = [np.array((1,2,3,5))]*1 + actual = cbook.delete_masked_points(*input) + assert np.allclose(actual, expected) + + input = ( np.array((1,2,3,4,5)), + ) + expected = [np.array((1,2,3,4,5))]*1 + actual = cbook.delete_masked_points(*input) + assert np.allclose(actual, expected) + + def test_delete_masked_points_strings(self): + input = ( 'hello', + ) + expected = ('hello',) + actual = cbook.delete_masked_points(*input) + assert actual == expected + + input = ( u'hello', + ) + expected = (u'hello',) + actual = cbook.delete_masked_points(*input) + assert actual == expected + + +if __name__=='__main__': + unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2008-07-21 00:22:33
|
Revision: 5797 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5797&view=rev Author: efiring Date: 2008-07-21 00:22:19 +0000 (Mon, 21 Jul 2008) Log Message: ----------- Expanded delete_masked_points to handle more types of argument Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/cbook.py trunk/matplotlib/lib/matplotlib/collections.py trunk/matplotlib/unit/cbook_unit.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-20 18:21:11 UTC (rev 5796) +++ trunk/matplotlib/CHANGELOG 2008-07-21 00:22:19 UTC (rev 5797) @@ -1,3 +1,6 @@ +2008-07-20 Rewrote cbook.delete_masked_points and corresponding + unit test to support rgb color array inputs, datetime + inputs, etc. - EF 2008-07-20 Renamed unit/axes_unit.py to cbook_unit.py and modified in accord with Ryan's move of delete_masked_points from Modified: trunk/matplotlib/lib/matplotlib/cbook.py =================================================================== --- trunk/matplotlib/lib/matplotlib/cbook.py 2008-07-20 18:21:11 UTC (rev 5796) +++ trunk/matplotlib/lib/matplotlib/cbook.py 2008-07-21 00:22:19 UTC (rev 5797) @@ -6,7 +6,7 @@ import re, os, errno, sys, StringIO, traceback, locale, threading, types import time, datetime import numpy as np -from numpy import ma +import numpy.ma as ma from weakref import ref major, minor1, minor2, s, tmp = sys.version_info @@ -1168,41 +1168,82 @@ def delete_masked_points(*args): """ - Find all masked points in a set of arguments, and return - the arguments with only the unmasked points remaining. + Find all masked and/or non-finite points in a set of arguments, + and return the arguments with only the unmasked points remaining. - This will also delete any points that are not finite (nan or inf). + Arguments can be in any of 5 categories: - The overall mask is calculated from any masks that are present. - If a mask is found, any argument that does not have the same - dimensions is left unchanged; therefore the argument list may - include arguments that can take string or array values, for - example. + 1) 1-D masked arrays + 2) 1-D ndarrays + 3) ndarrays with more than one dimension + 4) other non-string iterables + 5) anything else - Array arguments must have the same length; masked arguments must - be one-dimensional. + The first argument must be in one of the first four categories; + any argument with a length differing from that of the first + argument (and hence anything in category 5) then will be + passed through unchanged. - Written as a helper for scatter, but may be more generally - useful. + Masks are obtained from all arguments of the correct length + in categories 1, 2, and 4; a point is bad if masked in a masked + array or if it is a nan or inf. No attempt is made to + extract a mask from categories 2, 3, and 4 if *np.isfinite()* + does not yield a Boolean array. + + All input arguments that are not passed unchanged are returned + as ndarrays after removing the points or rows corresponding to + masks in any of the arguments. + + A vastly simpler version of this function was originally + written as a helper for Axes.scatter(). + """ - masks = [ma.getmaskarray(x) for x in args if hasattr(x, 'mask')] - isfinite = [np.isfinite(x) for x in args] - masks.extend( [~x for x in isfinite if not isinstance(x,types.NotImplementedType)] ) - if len(masks) == 0: - return args - mask = reduce(np.logical_or, masks) + if not len(args): + return () + if (is_string_like(args[0]) or not iterable(args[0])): + raise ValueError("First argument must be a sequence") + nrecs = len(args[0]) margs = [] - for x in args: - if (not is_string_like(x) - and iterable(x) - and len(x) == len(mask)): - if (hasattr(x, 'get_compressed_copy')): - compressed_x = x.get_compressed_copy(mask) + seqlist = [False] * len(args) + for i, x in enumerate(args): + if (not is_string_like(x)) and iterable(x) and len(x) == nrecs: + seqlist[i] = True + if ma.isMA(x): + if x.ndim > 1: + raise ValueError("Masked arrays must be 1-D") else: - compressed_x = ma.masked_array(x, mask=mask).compressed() - margs.append(compressed_x) - else: - margs.append(x) + x = np.asarray(x) + margs.append(x) + masks = [] # list of masks that are True where good + for i, x in enumerate(margs): + if seqlist[i]: + if x.ndim > 1: + continue # Don't try to get nan locations unless 1-D. + if ma.isMA(x): + masks.append(~ma.getmaskarray(x)) # invert the mask + xd = x.data + else: + xd = x + try: + mask = np.isfinite(xd) + if isinstance(mask, np.ndarray): + masks.append(mask) + except: #Fixme: put in tuple of possible exceptions? + pass + if len(masks): + mask = reduce(np.logical_and, masks) + igood = mask.nonzero()[0] + if len(igood) < nrecs: + for i, x in enumerate(margs): + if seqlist[i]: + if (hasattr(x, 'get_compressed_copy')): + compressed_x = x.get_compressed_copy(~mask) + else: + compressed_x = x.take(igood, axis=0) + margs[i] = compressed_x + for i, x in enumerate(margs): + if seqlist[i] and ma.isMA(x): + margs[i] = x.filled() return margs Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-07-20 18:21:11 UTC (rev 5796) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-07-21 00:22:19 UTC (rev 5797) @@ -90,7 +90,7 @@ self._uniform_offsets = None self._offsets = np.array([], np.float_) if offsets is not None: - offsets = np.asarray(offsets, np.float_) + offsets = np.asarray(offsets) if len(offsets.shape) == 1: offsets = offsets[np.newaxis,:] # Make it Nx2. if transOffset is not None: Modified: trunk/matplotlib/unit/cbook_unit.py =================================================================== --- trunk/matplotlib/unit/cbook_unit.py 2008-07-20 18:21:11 UTC (rev 5796) +++ trunk/matplotlib/unit/cbook_unit.py 2008-07-21 00:22:19 UTC (rev 5797) @@ -1,62 +1,52 @@ import unittest +from datetime import datetime + import numpy as np import matplotlib.cbook as cbook +import matplotlib.colors as mcolors -class TestAxes(unittest.TestCase): - def test_delete_masked_points_arrays(self): - input = ( [1,2,3,np.nan,5], - np.array((1,2,3,4,5)), - ) - expected = [np.array((1,2,3,5))]*2 - actual = cbook.delete_masked_points(*input) - assert np.allclose(actual, expected) +from matplotlib.cbook import delete_masked_points as dmp - input = ( np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ), - np.array((1,2,3,4,5)), - ) - expected = [np.array((1,2,3,5))]*2 - actual = cbook.delete_masked_points(*input) - assert np.allclose(actual, expected) +class Test_delete_masked_points(unittest.TestCase): + def setUp(self): + self.mask1 = [False, False, True, True, False, False] + self.arr0 = np.arange(1.0,7.0) + self.arr1 = [1,2,3,np.nan,np.nan,6] + self.arr2 = np.array(self.arr1) + self.arr3 = np.ma.array(self.arr2, mask=self.mask1) + self.arr_s = ['a', 'b', 'c', 'd', 'e', 'f'] + self.arr_s2 = np.array(self.arr_s) + self.arr_dt = [datetime(2008, 1, 1), datetime(2008, 1, 2), + datetime(2008, 1, 3), datetime(2008, 1, 4), + datetime(2008, 1, 5), datetime(2008, 1, 6)] + self.arr_dt2 = np.array(self.arr_dt) + self.arr_colors = ['r', 'g', 'b', 'c', 'm', 'y'] + self.arr_rgba = mcolors.colorConverter.to_rgba_array(self.arr_colors) - input = ( [1,2,3,np.nan,5], - np.ma.array( [1,2,3,4,5], mask=[False,False,False,True,False] ), - np.array((1,2,3,4,5)), - ) - expected = [np.array((1,2,3,5))]*3 - actual = cbook.delete_masked_points(*input) - assert np.allclose(actual, expected) + def test_bad_first_arg(self): + self.assertRaises(ValueError, dmp, 'a string', self.arr0) - input = () - expected = () - actual = cbook.delete_masked_points(*input) - assert np.allclose(actual, expected) + def test_string_seq(self): + actual = dmp(self.arr_s, self.arr1) + ind = [0, 1, 2, 5] + expected = (self.arr_s2.take(ind), self.arr2.take(ind)) + def test_datetime(self): + actual = dmp(self.arr_dt, self.arr3) + ind = [0, 1, 5] + expected = (self.arr_dt2.take(ind), + self.arr3.take(ind).compressed()) + self.assert_(np.all(actual[0] == expected[0]) and + np.all(actual[1] == expected[1])) - input = ( [1,2,3,np.nan,5], - ) - expected = [np.array((1,2,3,5))]*1 - actual = cbook.delete_masked_points(*input) - assert np.allclose(actual, expected) + def test_rgba(self): + actual = dmp(self.arr3, self.arr_rgba) + ind = [0, 1, 5] + expected = (self.arr3.take(ind).compressed(), + self.arr_rgba.take(ind, axis=0)) + self.assert_(np.all(actual[0] == expected[0]) and + np.all(actual[1] == expected[1])) - input = ( np.array((1,2,3,4,5)), - ) - expected = [np.array((1,2,3,4,5))]*1 - actual = cbook.delete_masked_points(*input) - assert np.allclose(actual, expected) - def test_delete_masked_points_strings(self): - input = ( 'hello', - ) - expected = ('hello',) - actual = cbook.delete_masked_points(*input) - assert actual == expected - - input = ( u'hello', - ) - expected = (u'hello',) - actual = cbook.delete_masked_points(*input) - assert actual == expected - - if __name__=='__main__': unittest.main() This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-21 09:51:40
|
Revision: 5798 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5798&view=rev Author: jdh2358 Date: 2008-07-21 09:50:36 +0000 (Mon, 21 Jul 2008) Log Message: ----------- Added Gael's backend fallback patch Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/scatter_demo.py trunk/matplotlib/lib/matplotlib/backends/__init__.py trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py trunk/matplotlib/lib/matplotlib/pyplot.py trunk/matplotlib/lib/matplotlib/rcsetup.py trunk/matplotlib/matplotlibrc.template Modified: trunk/matplotlib/examples/pylab_examples/scatter_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/scatter_demo.py 2008-07-21 00:22:19 UTC (rev 5797) +++ trunk/matplotlib/examples/pylab_examples/scatter_demo.py 2008-07-21 09:50:36 UTC (rev 5798) @@ -6,5 +6,6 @@ y = 0.9*rand(N) area = pi*(10 * rand(N))**2 # 0 to 10 point radiuses scatter(x,y,s=area, marker='^', c='r') - +savefig('test.ps') +savefig('test.pdf') show() Modified: trunk/matplotlib/lib/matplotlib/backends/__init__.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/__init__.py 2008-07-21 00:22:19 UTC (rev 5797) +++ trunk/matplotlib/lib/matplotlib/backends/__init__.py 2008-07-21 09:50:36 UTC (rev 5798) @@ -23,7 +23,9 @@ # Things we pull in from all backends new_figure_manager = backend_mod.new_figure_manager - + # image backends like pdf, agg or svg do not need to do anything + # for "show" or "draw_if_interactive", so if they are not defined + # by the backend, just do nothing def do_nothing(*args, **kwargs): pass backend_version = getattr(backend_mod,'backend_version', 'unknown') show = getattr(backend_mod, 'show', do_nothing) Modified: trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py 2008-07-21 00:22:19 UTC (rev 5797) +++ trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py 2008-07-21 09:50:36 UTC (rev 5798) @@ -67,7 +67,8 @@ for manager in Gcf.get_all_fig_managers(): manager.window.show() - if mainloop and gtk.main_level() == 0: + if mainloop and gtk.main_level() == 0 and \ + len(Gcf.get_all_fig_managers())>0: gtk.main() def new_figure_manager(num, *args, **kwargs): Modified: trunk/matplotlib/lib/matplotlib/pyplot.py =================================================================== --- trunk/matplotlib/lib/matplotlib/pyplot.py 2008-07-21 00:22:19 UTC (rev 5797) +++ trunk/matplotlib/lib/matplotlib/pyplot.py 2008-07-21 09:50:36 UTC (rev 5798) @@ -1,12 +1,13 @@ import sys import matplotlib -from matplotlib import _pylab_helpers +from matplotlib import _pylab_helpers, interactive from matplotlib.cbook import dedent, silent_list, is_string_like, is_numlike from matplotlib.figure import Figure, figaspect from matplotlib.backend_bases import FigureCanvasBase from matplotlib.image import imread as _imread from matplotlib import rcParams, rcParamsDefault, get_backend +from matplotlib.rcsetup import interactive_bk as _interactive_bk from matplotlib.artist import getp, get, Artist from matplotlib.artist import setp as _setp from matplotlib.axes import Axes @@ -32,7 +33,42 @@ MaxNLocator +## Backend detection ## +def _backend_selection(): + """ If rcParams['backend_fallback'] is true, check to see if the + current backend is compatible with the current running event + loop, and if not switches to a compatible one. + """ + backend = rcParams['backend'] + if not rcParams['backend_fallback'] or \ + backend not in _interactive_bk: + return + is_agg_backend = rcParams['backend'].endswith('Agg') + if 'wx' in sys.modules and not backend in ('WX', 'WXAgg'): + import wx + if wx.App.IsMainLoopRunning(): + rcParams['backend'] = 'wx' + 'Agg' * is_agg_backend + elif 'qt' in sys.modules and not backend == 'QtAgg': + import qt + if not qt.qApp.startingUp(): + # The mainloop is running. + rcParams['backend'] = 'qtAgg' + elif 'PyQt4.QtCore' in sys.modules and not backend == 'Qt4Agg': + import PyQt4.QtGui + if not PyQt4.QtGui.qApp.startingUp(): + # The mainloop is running. + rcParams['backend'] = 'qt4Agg' + elif 'gtk' in sys.modules and not backend in ('GTK', 'GTKAgg', + 'GTKCairo'): + import gobject + if gobject.MainLoop().is_running(): + rcParams['backend'] = 'gtk' + 'Agg' * is_agg_backend + elif 'Tkinter' in sys.modules and not backend == 'TkAgg': + #import Tkinter + pass #what if anything do we need to do for tkinter? +_backend_selection() + ## Global ## from matplotlib.backends import pylab_setup Modified: trunk/matplotlib/lib/matplotlib/rcsetup.py =================================================================== --- trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-07-21 00:22:19 UTC (rev 5797) +++ trunk/matplotlib/lib/matplotlib/rcsetup.py 2008-07-21 09:50:36 UTC (rev 5798) @@ -305,6 +305,7 @@ # a map from key -> value, converter defaultParams = { 'backend' : ['Agg', validate_backend], # agg is certainly present + 'backend_fallback' : [True, validate_bool], # agg is certainly present 'numerix' : ['numpy', validate_numerix], 'maskedarray' : [False, validate_bool], 'toolbar' : ['toolbar2', validate_toolbar], Modified: trunk/matplotlib/matplotlibrc.template =================================================================== --- trunk/matplotlib/matplotlibrc.template 2008-07-21 00:22:19 UTC (rev 5797) +++ trunk/matplotlib/matplotlibrc.template 2008-07-21 09:50:36 UTC (rev 5798) @@ -26,6 +26,11 @@ # to the module name (which must be in the PYTHONPATH) as # 'module://my_backend' backend : %(backend)s + +# if you are runing pyplot inside a GUI and your backend choice +# conflicts, we will automatically try and find a compatible one for +# you if backend_fallback is True +#backend_fallback: True numerix : %(numerix)s # numpy, Numeric or numarray #maskedarray : False # True to use external maskedarray module # instead of numpy.ma; this is a temporary This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dmk...@us...> - 2008-07-21 12:29:44
|
Revision: 5799 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5799&view=rev Author: dmkaplan Date: 2008-07-21 12:26:35 +0000 (Mon, 21 Jul 2008) Log Message: ----------- Recommitting my changes to clabel to allow for manual labeling after fixing problems with indexing identified by Eric Firing on 19 July 2008. In addition, I have made the following changes: 1) Placed more comments in contour.py with the intention of eventually doing a rewrite or restructuring of that code. 2) Added two pylab_examples: contour_label_demo.py that tests out some of the more advanced things that can now be done with contour labeling, and ginput_manual_clabel.py that demonstrates some uses of ginput, waitforbuttonpress, and clabel(...,manual=True). The first of these has been integrated into backend_driver.py, but the second cannot be because it requires interaction. Modified Paths: -------------- trunk/matplotlib/examples/tests/backend_driver.py trunk/matplotlib/lib/matplotlib/blocking_input.py trunk/matplotlib/lib/matplotlib/contour.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/contour_label_demo.py trunk/matplotlib/examples/pylab_examples/ginput_manual_clabel.py Added: trunk/matplotlib/examples/pylab_examples/contour_label_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/contour_label_demo.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/contour_label_demo.py 2008-07-21 12:26:35 UTC (rev 5799) @@ -0,0 +1,74 @@ +#!/usr/bin/env python +""" +Illustrate some of the more advanced things that one can do with +contour labels. + +See also contour_demo.py. +""" +import matplotlib +import numpy as np +import matplotlib.cm as cm +import matplotlib.mlab as mlab +import matplotlib.pyplot as plt + +matplotlib.rcParams['xtick.direction'] = 'out' +matplotlib.rcParams['ytick.direction'] = 'out' + +################################################## +# Define our surface +################################################## +delta = 0.025 +x = np.arange(-3.0, 3.0, delta) +y = np.arange(-2.0, 2.0, delta) +X, Y = np.meshgrid(x, y) +Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0) +Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1) +# difference of Gaussians +Z = 10.0 * (Z2 - Z1) + +################################################## +# Make contour labels using creative float classes +# Follows suggestion of Manuel Metz +################################################## +plt.figure() + +# Basic contour plot +CS = plt.contour(X, Y, Z) + +# Define a class that forces representation of float to look a certain way +# This remove trailing zero so '1.0' becomes '1' +class nf(float): + def __repr__(self): + str = '%.1f' % (self.__float__(),) + if str[-1]=='0': + return '%.0f' % self.__float__() + else: + return '%.1f' % self.__float__() + +# Recast levels to new class +CS.levels = [nf(val) for val in CS.levels ] + +# Label levels with specially formatted floats +plt.clabel(CS, CS.levels, inline=True, fmt='%r %%', fontsize=10) + +################################################## +# Label contours with arbitrary strings using a +# dictionary +################################################## +plt.figure() + +# Basic contour plot +CS = plt.contour(X, Y, Z) + +fmt = {} +strs = [ 'first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh' ] +for l,s in zip( CS.levels, strs ): + fmt[l] = s + +# Label every other level using strings +plt.clabel(CS,CS.levels[::2],inline=True,fmt=fmt,fontsize=10) + +################################################## +# Show the hole thing +################################################## +plt.show() Added: trunk/matplotlib/examples/pylab_examples/ginput_manual_clabel.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/ginput_manual_clabel.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/ginput_manual_clabel.py 2008-07-21 12:26:35 UTC (rev 5799) @@ -0,0 +1,88 @@ +#!/usr/bin/env python +""" +This provides examples of uses of interactive functions, such as ginput, +waitforbuttonpress and manual clabel placement. + +This script must be run interactively using a backend that has a +graphical user interface (for example, from inside ipython using +GTKAgg backend, but not PS backend). +""" +import time +import matplotlib +import numpy as np +import matplotlib.cm as cm +import matplotlib.mlab as mlab +import matplotlib.pyplot as plt + +def tellme(s): + print s + plt.title(s,fontsize=16) + plt.draw() + +################################################## +# Define a triangle by clicking three points +################################################## +plt.clf() +plt.axis([-1.,1.,-1.,1.]) +plt.setp(plt.gca(),autoscale_on=False) + +tellme('You will define a triangle, click to begin') + +plt.waitforbuttonpress() + +happy = False +while not happy: + pts = [] + while len(pts) < 3: + tellme('Select 3 corners with mouse') + pts = np.asarray( plt.ginput(3,timeout=-1) ) + if len(pts) < 3: + tellme('Too few points, starting over') + time.sleep(1) # Wait a second + + ph = plt.fill( pts[:,0], pts[:,1], 'r', lw=2 ) + + tellme('Happy? Key click for yes, mouse click for no') + + happy = plt.waitforbuttonpress() + + # Get rid of fill + if not happy: + for p in ph: p.remove() + +################################################## +# Now contour according to distance from triangle +# corners - just an example +################################################## + +# Define a nice function of distance from individual pts +def f(x,y,pts): + z = np.zeros_like(x) + for p in pts: + z = z + 1/(np.sqrt((x-p[0])**2+(y-p[1])**2)) + return 1/z + +X,Y = np.meshgrid( np.linspace(-1,1,51), np.linspace(-1,1,51) ) +Z = f(X,Y,pts) + +CS = plt.contour( X, Y, Z, 20 ) + +tellme( 'Use mouse to select contour label locations, middle button to finish' ) +CL = plt.clabel( CS, manual=True ) + +################################################## +# Now do a zoom +################################################## +tellme( 'Now do a nested zoom, click to begin' ) +plt.waitforbuttonpress() + +happy = False +while not happy: + tellme( 'Select two corners of zoom, middle mouse button to finish' ) + pts = np.asarray( plt.ginput(2,timeout=-1) ) + + happy = len(pts) < 2 + if happy: break + + pts = np.sort(pts,axis=0) + plt.axis( pts.T.ravel() ) Modified: trunk/matplotlib/examples/tests/backend_driver.py =================================================================== --- trunk/matplotlib/examples/tests/backend_driver.py 2008-07-21 09:50:36 UTC (rev 5798) +++ trunk/matplotlib/examples/tests/backend_driver.py 2008-07-21 12:26:35 UTC (rev 5799) @@ -39,6 +39,7 @@ 'color_demo.py', 'cohere_demo.py', 'contour_demo.py', + 'contour_label_demo.py', 'contourf_demo.py', 'csd_demo.py', 'custom_ticker1.py', Modified: trunk/matplotlib/lib/matplotlib/blocking_input.py =================================================================== --- trunk/matplotlib/lib/matplotlib/blocking_input.py 2008-07-21 09:50:36 UTC (rev 5798) +++ trunk/matplotlib/lib/matplotlib/blocking_input.py 2008-07-21 12:26:35 UTC (rev 5799) @@ -5,11 +5,11 @@ creates a callable object to retrieve events in a blocking way for interactive sessions :class:`BlockingKeyMouseInput` - creates a callable object to retrieve key or mouse clicks in a blocking way for interactive sessions. + creates a callable object to retrieve key or mouse clicks in a blocking way for interactive sessions. Note: Subclass of BlockingInput. Used by waitforbuttonpress :class:`BlockingMouseInput` - creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions. + creates a callable object to retrieve mouse clicks in a blocking way for interactive sessions. Note: Subclass of BlockingInput. Used by ginput :class:`BlockingContourLabeler` @@ -46,7 +46,7 @@ # This will extract info from events self.post_event() - + # Check if we have enough events already if len(self.events) >= self.n and self.n > 0: self.done = True @@ -84,7 +84,7 @@ """ Blocking call to retrieve n events """ - + assert isinstance(n, int), "Requires an integer argument" self.n = n @@ -94,7 +94,7 @@ # Ensure that the figure is shown self.fig.show() - + # connect the events to the on_event function call for n in self.eventslist: self.callbacks.append( self.fig.canvas.mpl_connect(n, self.on_event) ) @@ -124,7 +124,7 @@ blocking way. """ def __init__(self, fig): - BlockingInput.__init__(self, fig=fig, + BlockingInput.__init__(self, fig=fig, eventslist=('button_press_event',) ) def post_event(self): @@ -194,7 +194,7 @@ """ self.clicks.append((event.xdata,event.ydata)) - verbose.report("input %i: %f,%f" % + verbose.report("input %i: %f,%f" % (len(self.clicks),event.xdata, event.ydata)) # If desired plot up click @@ -209,7 +209,7 @@ removing the last click. """ self.clicks.pop(index) - + if self.show_clicks: mark = self.marks.pop(index) mark.remove() @@ -234,7 +234,7 @@ # Call base class to remove callbacks BlockingInput.cleanup(self) - + def __call__(self, n=1, timeout=30, show_clicks=True): """ Blocking call to retrieve n coordinate pairs through mouse @@ -261,11 +261,18 @@ This will be called if an event involving a button other than 2 or 3 occcurs. This will add a label to a contour. """ - if event.inaxes == self.cs.ax: - conmin,segmin,imin,xmin,ymin = self.cs.find_nearest_contour( - event.x, event.y)[:5] - paths = self.cs.collections[conmin].get_paths() + # Shorthand + cs = self.cs + + if event.inaxes == cs.ax: + conmin,segmin,imin,xmin,ymin = cs.find_nearest_contour( + event.x, event.y, cs.label_indices)[:5] + + # Get index of nearest level in subset of levels used for labeling + lmin = cs.label_indices.index(conmin) + + paths = cs.collections[conmin].get_paths() lc = paths[segmin].vertices # Figure out label rotation. This is very cludgy. @@ -287,15 +294,15 @@ if rotation < -90: rotation = 180 + rotation - self.cs.add_label(xmin,ymin,rotation,conmin) + cs.add_label(xmin,ymin,rotation,cs.label_levels[lmin], + cs.label_cvalues[lmin]) if self.inline: # Get label width for breaking contours - lw = self.cs.get_label_width(self.cs.label_levels[conmin], - self.cs.fmt, - self.cs.fslist[conmin]) + lw = cs.get_label_width(cs.label_levels[lmin], + cs.fmt, cs.fslist[lmin]) # Break contour - new=self.cs.break_linecontour(lc,rotation,lw,imin) + new=cs.break_linecontour(lc,rotation,lw,imin) if len(new[0]): paths[segmin] = path.Path(new[0]) if len(new[1]): @@ -304,7 +311,7 @@ self.fig.canvas.draw() else: # Remove event if not valid BlockingInput.pop(self) - + def button3(self,event): """ This will be called if button 3 is clicked. This will remove Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2008-07-21 09:50:36 UTC (rev 5798) +++ trunk/matplotlib/lib/matplotlib/contour.py 2008-07-21 12:26:35 UTC (rev 5799) @@ -17,6 +17,9 @@ import matplotlib.text as text import matplotlib.cbook as cbook +# Import needed for adding manual selection capability to clabel +from matplotlib.blocking_input import BlockingContourLabeler + # We can't use a single line collection for contour because a line # collection can have only a single line style, and we want to be able to have # dashed negative contours, for example, and solid positive contours. @@ -68,16 +71,49 @@ *fmt*: a format string for the label. Default is '%1.3f' + Alternatively, this can be a dictionary matching contour + levels with arbitrary strings to use for each contour level + (i.e., fmt[level]=string) + *manual*: + if *True*, contour labels will be placed manually using + mouse clicks. Click the first button near a contour to + add a label, click the second button (or potentially both + mouse buttons at once) to finish adding labels. The third + button can be used to remove the last label added, but + only if labels are not inline. """ + + """" + NOTES on how this all works: + + clabel basically takes the input arguments and uses them to + add a list of "label specific" attributes to the ContourSet + object. These attributes currently include: label_indices, + label_levels, label_cvalues, fp (font properties), fslist + (fontsize list), label_mappable, cl (list of text objects of + labels), cl_xy (coordinates of labels), cl_cvalues (color + values of the actual labels). + + Note that these property names do not conform to the standards + set for coding matplotlib and I (DMK) eventually plan on + changing them so that they are clearer and conform to + standards. + + Once these attributes are set, clabel passes control to the + labels method (case of automatic label placement) or + BlockingContourLabeler (case of manual label placement. + """ + fontsize = kwargs.get('fontsize', None) inline = kwargs.get('inline', 1) self.fmt = kwargs.get('fmt', '%1.3f') _colors = kwargs.get('colors', None) + # Detect if manual selection is desired and remove from argument list + self.manual_select=kwargs.get('manual',False) - if len(args) == 0: levels = self.levels indices = range(len(self.levels)) @@ -126,10 +162,16 @@ #self.cl_cvalues = [] # same self.cl_xy = [] - self.labels(inline) + if self.manual_select: + print 'Select label locations manually using first mouse button.' + print 'End manual selection with second mouse button.' + if not inline: + print 'Remove last label by clicking third mouse button.' - for label in self.cl: - self.ax.add_artist(label) + blocking_contour_labeler = BlockingContourLabeler(self) + blocking_contour_labeler(inline) + else: + self.labels(inline) self.label_list = cbook.silent_list('text.Text', self.cl) return self.label_list @@ -141,10 +183,10 @@ if lcsize > 10 * labelwidth: return 1 - xmax = np.amax(np.array(linecontour)[:,0]) - xmin = np.amin(np.array(linecontour)[:,0]) - ymax = np.amax(np.array(linecontour)[:,1]) - ymin = np.amin(np.array(linecontour)[:,1]) + xmax = np.amax(linecontour[:,0]) + xmin = np.amin(linecontour[:,0]) + ymax = np.amax(linecontour[:,1]) + ymin = np.amin(linecontour[:,1]) lw = labelwidth if (xmax - xmin) > 1.2* lw or (ymax - ymin) > 1.2 * lw: @@ -180,12 +222,10 @@ if self.too_close(x,y, lw): continue else: - self.cl_xy.append((x,y)) return x,y, ind ind = adist[0] x, y = XX[ind][hysize], YY[ind][hysize] - self.cl_xy.append((x,y)) return x,y, ind def get_label_width(self, lev, fmt, fsize): @@ -193,7 +233,7 @@ if cbook.is_string_like(lev): lw = (len(lev)) * fsize else: - lw = (len(fmt%lev)) * fsize + lw = (len(self.get_text(lev,fmt))) * fsize return lw @@ -210,9 +250,11 @@ if cbook.is_string_like(lev): return lev else: - return fmt%lev + if isinstance(fmt,dict): + return fmt[lev] + else: + return fmt%lev - def break_linecontour(self, linecontour, rot, labelwidth, ind): "break a contour in two contours at the location of the label" lcsize = len(linecontour) @@ -226,8 +268,8 @@ slc = trans.transform(linecontour) x,y = slc[ind] - xx= np.asarray(slc)[:,0].copy() - yy=np.asarray(slc)[:,1].copy() + xx=slc[:,0].copy() + yy=slc[:,1].copy() #indices which are under the label inds, = np.nonzero(((xx < x+xlabel) & (xx > x-xlabel)) & @@ -308,8 +350,8 @@ else: ysize = labelwidth - XX = np.resize(np.asarray(linecontour)[:,0],(xsize, ysize)) - YY = np.resize(np.asarray(linecontour)[:,1],(xsize, ysize)) + XX = np.resize(linecontour[:,0],(xsize, ysize)) + YY = np.resize(linecontour[:,1],(xsize, ysize)) #I might have fouled up the following: yfirst = YY[:,0].reshape(xsize, 1) ylast = YY[:,-1].reshape(xsize, 1) @@ -335,19 +377,38 @@ return x,y, rotation, dind + def add_label(self,x,y,rotation,lev,cvalue): + dx,dy = self.ax.transData.inverted().transform_point((x,y)) + t = text.Text(dx, dy, rotation = rotation, + horizontalalignment='center', + verticalalignment='center') + + color = self.label_mappable.to_rgba(cvalue,alpha=self.alpha) + + _text = self.get_text(lev,self.fmt) + self.set_label_props(t, _text, color) + self.cl.append(t) + self.cl_cvalues.append(cvalue) + self.cl_xy.append((x,y)) + + # Add label to plot here - useful for manual mode label selection + self.ax.add_artist(t) + + def pop_label(self,index=-1): + '''Defaults to removing last label, but any index can be supplied''' + self.cl_cvalues.pop(index) + t = self.cl.pop(index) + t.remove() + def labels(self, inline): - levels = self.label_levels - fslist = self.fslist - trans = self.ax.transData - _colors = self.label_mappable.to_rgba(self.label_cvalues, - alpha=self.alpha) - fmt = self.fmt - for icon, lev, color, cvalue, fsize in zip(self.label_indices, - self.label_levels, - _colors, - self.label_cvalues, fslist): + trans = self.ax.transData # A bit of shorthand + + for icon, lev, fsize, cvalue in zip( + self.label_indices, self.label_levels, self.fslist, + self.label_cvalues ): + con = self.collections[icon] - lw = self.get_label_width(lev, fmt, fsize) + lw = self.get_label_width(lev, self.fmt, fsize) additions = [] paths = con.get_paths() for segNum, linepath in enumerate(paths): @@ -356,28 +417,27 @@ # avoid division by zero if np.all(linecontour[0] == linecontour[-1]): linecontour = np.concatenate((linecontour, - linecontour[1][np.newaxis,:])) + linecontour[1][np.newaxis,:])) #linecontour.append(linecontour[1]) # transfer all data points to screen coordinates slc = trans.transform(linecontour) if self.print_label(slc,lw): x,y, rotation, ind = self.locate_label(slc, lw) - # transfer the location of the label back to - # data coordinates - dx,dy = trans.inverted().transform_point((x,y)) - t = text.Text(dx, dy, rotation = rotation, - horizontalalignment='center', - verticalalignment='center') - _text = self.get_text(lev,fmt) - self.set_label_props(t, _text, color) - self.cl.append(t) - self.cl_cvalues.append(cvalue) + + # Actually add the label + self.add_label(x,y,rotation,lev,cvalue) + + # Use break_linecontour to split contours for inlining if inline: - new = self.break_linecontour(linecontour, rotation, lw, ind) + new = self.break_linecontour(linecontour, rotation, + lw, ind) if len(new[0]): paths[segNum] = path.Path(new[0]) if len(new[1]): additions.append(path.Path(new[1])) + + # After looping over all segments on a contour, append + # new paths to existing paths.extend(additions) @@ -802,19 +862,8 @@ Use keyword args to control colors, linewidth, origin, cmap ... see below for more details. - *X*, *Y*, and *Z* may be arrays all with the same 2-D shape, or - *X* and *Y* can be 1-D while *Z* is 2-D. In the latter - case, the following must be true: + *X*, *Y*, and *Z* must be arrays with the same dimensions. - :: - - Z.shape == len(Y), len(X) - - Note that the first index of *Z*, the row number, corresponds - to the vertical coordinate on the page, while the second - index, the column number, corresponds to the horizontal - coordinate on the page. - *Z* may be a masked array, but filled contouring may not handle internal masked regions correctly. @@ -908,3 +957,70 @@ .. plot:: contour_demo.py """ + + def find_nearest_contour( self, x, y, indices=None, pixel=True ): + """ + Finds contour that is closest to a point. Defaults to + measuring distance in pixels (screen space - useful for manual + contour labeling), but this can be controlled via a keyword + argument. + + Returns a tuple containing the contour, segment, index of + segment, x & y of segment point and distance to minimum point. + + Call signature:: + + conmin,segmin,imin,xmin,ymin,dmin = find_nearest_contour( + self, x, y, indices=None, pixel=True ) + + Optional keyword arguments:: + + *indices*: + Indexes of contour levels to consider when looking for + nearest point. Defaults to using all levels. + + *pixel*: + If *True*, measure distance in pixel space, if not, measure + distance in axes space. Defaults to *True*. + + """ + + # This function uses a method that is probably quite + # inefficient based on converting each contour segment to + # pixel coordinates and then comparing the given point to + # those coordinates for each contour. This will probably be + # quite slow for complex contours, but for normal use it works + # sufficiently well that the time is not noticeable. + # Nonetheless, improvements could probably be made. + + if indices==None: + indices = range(len(self.levels)) + + dmin = 1e10 + conmin = None + segmin = None + xmin = None + ymin = None + + for icon in indices: + con = self.collections[icon] + paths = con.get_paths() + for segNum, linepath in enumerate(paths): + lc = linepath.vertices + + # transfer all data points to screen coordinates if desired + if pixel: + lc = self.ax.transData.transform(lc) + + ds = (lc[:,0]-x)**2 + (lc[:,1]-y)**2 + d = min( ds ) + if d < dmin: + dmin = d + conmin = icon + segmin = segNum + imin = mpl.mlab.find( ds == d )[0] + xmin = lc[imin,0] + ymin = lc[imin,1] + + return (conmin,segmin,imin,xmin,ymin,dmin) + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2008-07-21 19:08:32
|
Revision: 5802 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5802&view=rev Author: efiring Date: 2008-07-21 19:08:29 +0000 (Mon, 21 Jul 2008) Log Message: ----------- In image.py, ensure input can be ndarray or MaskedArray (Klaus Zimmerman). Masked 2-D arrays are handled automatically by the color mapping framework, so they need to be passed through; but there is no point in converting 3-D arrays into masked arrays because there is no support for dealing with a mask in that case. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/image.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-21 12:58:53 UTC (rev 5801) +++ trunk/matplotlib/CHANGELOG 2008-07-21 19:08:29 UTC (rev 5802) @@ -1,3 +1,7 @@ +2008-07-21 Changed the "asarray" strategy in image.py so that + colormapping of masked input should work for all + image types (thanks Klaus Zimmerman) - EF + 2008-07-20 Rewrote cbook.delete_masked_points and corresponding unit test to support rgb color array inputs, datetime inputs, etc. - EF Modified: trunk/matplotlib/lib/matplotlib/image.py =================================================================== --- trunk/matplotlib/lib/matplotlib/image.py 2008-07-21 12:58:53 UTC (rev 5801) +++ trunk/matplotlib/lib/matplotlib/image.py 2008-07-21 19:08:29 UTC (rev 5802) @@ -272,10 +272,11 @@ ACCEPTS: numpy/PIL Image A""" # check if data is PIL Image without importing Image if hasattr(A,'getpixel'): - X = pil_to_array(A) + self._A = pil_to_array(A) + elif ma.isMA(A): + self._A = A else: - X = ma.asarray(A) # assume array - self._A = X + self._A = np.asarray(A) # assume array self._imcache =None self._rgbacache = None @@ -408,7 +409,8 @@ def set_data(self, x, y, A): x = np.asarray(x,np.float32) y = np.asarray(y,np.float32) - A = np.asarray(A) + if not ma.isMA(A): + A = np.asarray(A) if len(x.shape) != 1 or len(y.shape) != 1\ or A.shape[0:2] != (y.shape[0], x.shape[0]): raise TypeError("Axes don't match array shape") @@ -535,7 +537,8 @@ def set_data(self, x, y, A): - A = ma.asarray(A) + if not ma.isMA(A): + A = np.asarray(A) if x is None: x = np.arange(0, A.shape[1]+1, dtype=np.float64) else: This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ef...@us...> - 2008-07-21 19:39:15
|
Revision: 5803 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5803&view=rev Author: efiring Date: 2008-07-21 19:39:12 +0000 (Mon, 21 Jul 2008) Log Message: ----------- Add get_offsets, set_offsets to Collection (Ryan Kraus) Modified Paths: -------------- trunk/matplotlib/API_CHANGES trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/collections.py Modified: trunk/matplotlib/API_CHANGES =================================================================== --- trunk/matplotlib/API_CHANGES 2008-07-21 19:08:29 UTC (rev 5802) +++ trunk/matplotlib/API_CHANGES 2008-07-21 19:39:12 UTC (rev 5803) @@ -1,6 +1,9 @@ Changes for 0.98.x ================== +* Methods get_offsets and set_offsets added to Collections base + class. + * Figure.figurePatch renamed Figure.patch, Axes.axesPatch renamed Axes.patch, Axes.axesFrame renamed Axes.frame, Axes.get_frame, which returns Axes.patch, is deprecated. Examples and users guide updated Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-21 19:08:29 UTC (rev 5802) +++ trunk/matplotlib/CHANGELOG 2008-07-21 19:39:12 UTC (rev 5803) @@ -1,3 +1,6 @@ +2008-07-21 Committed patch by Ryan May to add get_offsets and + set_offsets to Collections base class - EF + 2008-07-21 Changed the "asarray" strategy in image.py so that colormapping of masked input should work for all image types (thanks Klaus Zimmerman) - EF Modified: trunk/matplotlib/lib/matplotlib/collections.py =================================================================== --- trunk/matplotlib/lib/matplotlib/collections.py 2008-07-21 19:08:29 UTC (rev 5802) +++ trunk/matplotlib/lib/matplotlib/collections.py 2008-07-21 19:39:12 UTC (rev 5803) @@ -218,6 +218,32 @@ def set_pickradius(self,pickradius): self.pickradius = 5 def get_pickradius(self): return self.pickradius + def set_offsets(self, offsets): + """ + Set the offsets for the collection. *offsets* can be a scalar + or a sequence. + + ACCEPTS: float or sequence of floats + """ + offsets = np.asarray(offsets, np.float_) + if len(offsets.shape) == 1: + offsets = offsets[np.newaxis,:] # Make it Nx2. + #This decision is based on how they are initialized above + if self._uniform_offsets is None: + self._offsets = offsets + else: + self._uniform_offsets = offsets + + def get_offsets(self): + """ + Return the offsets for the collection. + """ + #This decision is based on how they are initialized above in __init__() + if self._uniform_offsets is None: + return self._offsets + else: + return self._uniform_offsets + def set_linewidths(self, lw): """ Set the linewidth(s) for the collection. *lw* can be a scalar This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <md...@us...> - 2008-07-21 22:42:54
|
Revision: 5804 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5804&view=rev Author: mdboom Date: 2008-07-21 22:42:52 +0000 (Mon, 21 Jul 2008) Log Message: ----------- Re-introduce offset_copy Modified Paths: -------------- trunk/matplotlib/API_CHANGES trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/transforms.py Modified: trunk/matplotlib/API_CHANGES =================================================================== --- trunk/matplotlib/API_CHANGES 2008-07-21 19:39:12 UTC (rev 5803) +++ trunk/matplotlib/API_CHANGES 2008-07-21 22:42:52 UTC (rev 5804) @@ -130,8 +130,6 @@ Transform.inverse_xy_tup(points) Transform.inverted().transform(points) - offset_copy(trans, x, y) trans + Affine2D().translate(x, y) - axes.py Axes.get_position() Axes.get_position() [Axes.get_position() used to return a list of points, not it Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-21 19:39:12 UTC (rev 5803) +++ trunk/matplotlib/CHANGELOG 2008-07-21 22:42:52 UTC (rev 5804) @@ -1,3 +1,6 @@ +2008-07-21 Re-introduced offset_copy that works in the context of the + new transforms. - MGD + 2008-07-21 Committed patch by Ryan May to add get_offsets and set_offsets to Collections base class - EF Modified: trunk/matplotlib/lib/matplotlib/transforms.py =================================================================== --- trunk/matplotlib/lib/matplotlib/transforms.py 2008-07-21 19:39:12 UTC (rev 5803) +++ trunk/matplotlib/lib/matplotlib/transforms.py 2008-07-21 22:42:52 UTC (rev 5804) @@ -2144,6 +2144,27 @@ ((a < b) and (a < val and b > val)) or (b < val and a > val)) +def offset_copy(trans, fig, x=0.0, y=0.0, units='inches'): + ''' + Return a new transform with an added offset. + args: + trans is any transform + kwargs: + fig is the current figure; it can be None if units are 'dots' + x, y give the offset + units is 'inches', 'points' or 'dots' + ''' + if units == 'dots': + return trans + Affine2D().translate(x, y) + if fig is None: + raise ValueError('For units of inches or points a fig kwarg is needed') + if units == 'points': + x /= 72.0 + y /= 72.0 + elif not units == 'inches': + raise ValueError('units must be dots, points, or inches') + return trans + ScaledTranslation(x, y, fig.dpi_scale_trans) + if __name__ == '__main__': import copy from random import random This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <js...@us...> - 2008-07-22 01:52:15
|
Revision: 5805 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5805&view=rev Author: jswhit Date: 2008-07-22 01:52:12 +0000 (Tue, 22 Jul 2008) Log Message: ----------- added scikits.delaunay as matplotlib.delaunay, added griddata function to mlab. Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/examples/tests/backend_driver.py trunk/matplotlib/setup.py trunk/matplotlib/setupext.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/griddata_demo.py trunk/matplotlib/lib/matplotlib/delaunay/ trunk/matplotlib/lib/matplotlib/delaunay/VoronoiDiagramGenerator.cpp trunk/matplotlib/lib/matplotlib/delaunay/VoronoiDiagramGenerator.h trunk/matplotlib/lib/matplotlib/delaunay/__init__.py trunk/matplotlib/lib/matplotlib/delaunay/_delaunay.cpp trunk/matplotlib/lib/matplotlib/delaunay/delaunay_utils.cpp trunk/matplotlib/lib/matplotlib/delaunay/delaunay_utils.h trunk/matplotlib/lib/matplotlib/delaunay/interpolate.py trunk/matplotlib/lib/matplotlib/delaunay/natneighbors.cpp trunk/matplotlib/lib/matplotlib/delaunay/natneighbors.h trunk/matplotlib/lib/matplotlib/delaunay/testfuncs.py trunk/matplotlib/lib/matplotlib/delaunay/triangulate.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-21 22:42:52 UTC (rev 5804) +++ trunk/matplotlib/CHANGELOG 2008-07-22 01:52:12 UTC (rev 5805) @@ -1,3 +1,8 @@ +2008-07-21 Added scikits.delaunay as matplotlib.delaunay. Added griddata + function in matplotlib.mlab, with example (griddata_demo.py) in + pylab_examples. griddata function will use mpl_toolkits._natgrid + if installed (haven't yet created the toolkit). - JSW + 2008-07-21 Re-introduced offset_copy that works in the context of the new transforms. - MGD Added: trunk/matplotlib/examples/pylab_examples/griddata_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/griddata_demo.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/griddata_demo.py 2008-07-22 01:52:12 UTC (rev 5805) @@ -0,0 +1,40 @@ +from numpy.random import uniform, seed +from matplotlib.mlab import griddata +import matplotlib.pyplot as plt +import numpy as np +# make up data. +#npts = int(raw_input('enter # of random points to plot:')) +seed(-1) +npts = 200 +x = uniform(-2,2,npts) +y = uniform(-2,2,npts) +z = x*np.exp(-x**2-y**2) +# define grid. +xi = np.linspace(-2.1,2.1,100) +yi = np.linspace(-2.1,2.1,100) +# grid the data. +zi = griddata(x,y,z,xi,yi) +# contour the gridded data, plotting dots at the nonuniform data points. +CS = plt.contour(xi,yi,zi,15,linewidths=0.5,colors='k') +CS = plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet) +plt.colorbar() # draw colorbar +# plot data points. +plt.scatter(x,y,marker='o',c='b',s=5) +plt.xlim(-2,2) +plt.ylim(-2,2) +plt.title('griddata test (%d points)' % npts) +plt.show() + +# test case that scikits.delaunay fails on, but natgrid passes.. +#data = np.array([[-1, -1], [-1, 0], [-1, 1], +# [ 0, -1], [ 0, 0], [ 0, 1], +# [ 1, -1 - np.finfo(np.float_).eps], [ 1, 0], [ 1, 1], +# ]) +#x = data[:,0] +#y = data[:,1] +#z = x*np.exp(-x**2-y**2) +## define grid. +#xi = np.linspace(-1.1,1.1,100) +#yi = np.linspace(-1.1,1.1,100) +## grid the data. +#zi = griddata(x,y,z,xi,yi) Modified: trunk/matplotlib/examples/tests/backend_driver.py =================================================================== --- trunk/matplotlib/examples/tests/backend_driver.py 2008-07-21 22:42:52 UTC (rev 5804) +++ trunk/matplotlib/examples/tests/backend_driver.py 2008-07-22 01:52:12 UTC (rev 5805) @@ -41,6 +41,7 @@ 'contour_demo.py', 'contour_label_demo.py', 'contourf_demo.py', + 'griddata_demo.py', 'csd_demo.py', 'custom_ticker1.py', 'customize_rc.py', Added: trunk/matplotlib/lib/matplotlib/delaunay/VoronoiDiagramGenerator.cpp =================================================================== --- trunk/matplotlib/lib/matplotlib/delaunay/VoronoiDiagramGenerator.cpp (rev 0) +++ trunk/matplotlib/lib/matplotlib/delaunay/VoronoiDiagramGenerator.cpp 2008-07-22 01:52:12 UTC (rev 5805) @@ -0,0 +1,1152 @@ +/* + * The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T + * Bell Laboratories. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * This code was originally written by Stephan Fortune in C code. Shane O'Sullivan, + * have since modified it, encapsulating it in a C++ class and, fixing memory leaks and + * adding accessors to the Voronoi Edges. + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + */ + +/* + * Subsequently, Robert Kern modified it to yield Python objects. + * Copyright 2005 Robert Kern <rob...@gm...> + * See LICENSE.txt in the scipy source directory. + */ + +#include "VoronoiDiagramGenerator.h" + +VoronoiDiagramGenerator::VoronoiDiagramGenerator() +{ + siteidx = 0; + sites = 0; + + allMemoryList = new FreeNodeArrayList; + allMemoryList->memory = 0; + allMemoryList->next = 0; + currentMemoryBlock = allMemoryList; + allEdges = 0; + allEdgeList = 0; + iteratorEdges = 0; + iterEdgeList = 0; + minDistanceBetweenSites = 0; +} + +VoronoiDiagramGenerator::~VoronoiDiagramGenerator() +{ + cleanupEdgeList(); + cleanup(); + cleanupEdges(); + + if(allMemoryList != 0) + delete allMemoryList; +} + + + +bool VoronoiDiagramGenerator::generateVoronoi(double *xValues, double *yValues, int numPoints, double minX, double maxX, double minY, double maxY, double minDist) +{ + cleanupEdgeList(); + cleanup(); + cleanupEdges(); + int i; + + minDistanceBetweenSites = minDist; + + nsites=numPoints; + plot = 0; + triangulate = 0; + debug = 1; + sorted = 0; + freeinit(&sfl, sizeof (Site)); + + sites = (struct Site *) myalloc(nsites*sizeof( *sites)); + + if(sites == 0) + return false; + + xmin = xValues[0]; + ymin = yValues[0]; + xmax = xValues[0]; + ymax = yValues[0]; + + for(i = 0; i< nsites; i++) + { + sites[i].coord.x = xValues[i]; + sites[i].coord.y = yValues[i]; + sites[i].sitenbr = i; + sites[i].refcnt = 0; + + if(xValues[i] < xmin) + xmin = xValues[i]; + else if(xValues[i] > xmax) + xmax = xValues[i]; + + if(yValues[i] < ymin) + ymin = yValues[i]; + else if(yValues[i] > ymax) + ymax = yValues[i]; + + //printf("\n%f %f\n",xValues[i],yValues[i]); + } + + qsort(sites, nsites, sizeof (*sites), scomp); + + siteidx = 0; + geominit(); + double temp = 0; + if(minX > maxX) + { + temp = minX; + minX = maxX; + maxX = temp; + } + if(minY > maxY) + { + temp = minY; + minY = maxY; + maxY = temp; + } + borderMinX = minX; + borderMinY = minY; + borderMaxX = maxX; + borderMaxY = maxY; + + siteidx = 0; + + voronoi(triangulate); + + return true; +} + +bool VoronoiDiagramGenerator::ELinitialize() +{ + int i; + freeinit(&hfl, sizeof **ELhash); + ELhashsize = 2 * sqrt_nsites; + ELhash = (struct Halfedge **) myalloc ( sizeof *ELhash * ELhashsize); + + if(ELhash == 0) + return false; + + for(i=0; i<ELhashsize; i +=1) ELhash[i] = (struct Halfedge *)NULL; + ELleftend = HEcreate( (struct Edge *)NULL, 0); + ELrightend = HEcreate( (struct Edge *)NULL, 0); + ELleftend -> ELleft = (struct Halfedge *)NULL; + ELleftend -> ELright = ELrightend; + ELrightend -> ELleft = ELleftend; + ELrightend -> ELright = (struct Halfedge *)NULL; + ELhash[0] = ELleftend; + ELhash[ELhashsize-1] = ELrightend; + + return true; +} + + +struct Halfedge* VoronoiDiagramGenerator::HEcreate(struct Edge *e,int pm) +{ + struct Halfedge *answer; + answer = (struct Halfedge *) getfree(&hfl); + answer -> ELedge = e; + answer -> ELpm = pm; + answer -> PQnext = (struct Halfedge *) NULL; + answer -> vertex = (struct Site *) NULL; + answer -> ELrefcnt = 0; + return(answer); +} + + +void VoronoiDiagramGenerator::ELinsert(struct Halfedge *lb, struct Halfedge *newHe) +{ + newHe -> ELleft = lb; + newHe -> ELright = lb -> ELright; + (lb -> ELright) -> ELleft = newHe; + lb -> ELright = newHe; +} + +/* Get entry from hash table, pruning any deleted nodes */ +struct Halfedge * VoronoiDiagramGenerator::ELgethash(int b) +{ + struct Halfedge *he; + + if(b<0 || b>=ELhashsize) + return((struct Halfedge *) NULL); + he = ELhash[b]; + if (he == (struct Halfedge *) NULL || he->ELedge != (struct Edge *) DELETED ) + return (he); + + /* Hash table points to deleted half edge. Patch as necessary. */ + ELhash[b] = (struct Halfedge *) NULL; + if ((he -> ELrefcnt -= 1) == 0) + makefree((Freenode*)he, &hfl); + return ((struct Halfedge *) NULL); +} + +struct Halfedge * VoronoiDiagramGenerator::ELleftbnd(struct Point *p) +{ + int i, bucket; + struct Halfedge *he; + + /* Use hash table to get close to desired halfedge */ + bucket = (int)((p->x - xmin)/deltax * ELhashsize); //use the hash function to find the place in the hash map that this HalfEdge should be + + if(bucket<0) bucket =0; //make sure that the bucket position in within the range of the hash array + if(bucket>=ELhashsize) bucket = ELhashsize - 1; + + he = ELgethash(bucket); + if(he == (struct Halfedge *) NULL) //if the HE isn't found, search backwards and forwards in the hash map for the first non-null entry + { + for(i=1; 1 ; i += 1) + { + if ((he=ELgethash(bucket-i)) != (struct Halfedge *) NULL) + break; + if ((he=ELgethash(bucket+i)) != (struct Halfedge *) NULL) + break; + }; + totalsearch += i; + }; + ntry += 1; + /* Now search linear list of halfedges for the correct one */ + if (he==ELleftend || (he != ELrightend && right_of(he,p))) + { + do + { + he = he -> ELright; + } while (he!=ELrightend && right_of(he,p)); //keep going right on the list until either the end is reached, or you find the 1st edge which the point + he = he -> ELleft; //isn't to the right of + } + else //if the point is to the left of the HalfEdge, then search left for the HE just to the left of the point + do + { + he = he -> ELleft; + } while (he!=ELleftend && !right_of(he,p)); + + /* Update hash table and reference counts */ + if(bucket > 0 && bucket <ELhashsize-1) + { + if(ELhash[bucket] != (struct Halfedge *) NULL) + { + ELhash[bucket] -> ELrefcnt -= 1; + } + ELhash[bucket] = he; + ELhash[bucket] -> ELrefcnt += 1; + }; + return (he); +} + + +/* This delete routine can't reclaim node, since pointers from hash +table may be present. */ +void VoronoiDiagramGenerator::ELdelete(struct Halfedge *he) +{ + (he -> ELleft) -> ELright = he -> ELright; + (he -> ELright) -> ELleft = he -> ELleft; + he -> ELedge = (struct Edge *)DELETED; +} + + +struct Halfedge * VoronoiDiagramGenerator::ELright(struct Halfedge *he) +{ + return (he -> ELright); +} + +struct Halfedge * VoronoiDiagramGenerator::ELleft(struct Halfedge *he) +{ + return (he -> ELleft); +} + + +struct Site * VoronoiDiagramGenerator::leftreg(struct Halfedge *he) +{ + if(he -> ELedge == (struct Edge *)NULL) + return(bottomsite); + return( he -> ELpm == le ? + he -> ELedge -> reg[le] : he -> ELedge -> reg[re]); +} + +struct Site * VoronoiDiagramGenerator::rightreg(struct Halfedge *he) +{ + if(he -> ELedge == (struct Edge *)NULL) //if this halfedge has no edge, return the bottom site (whatever that is) + return(bottomsite); + + //if the ELpm field is zero, return the site 0 that this edge bisects, otherwise return site number 1 + return( he -> ELpm == le ? he -> ELedge -> reg[re] : he -> ELedge -> reg[le]); +} + +void VoronoiDiagramGenerator::geominit() +{ + double sn; + + freeinit(&efl, sizeof(Edge)); + nvertices = 0; + nedges = 0; + sn = (double)nsites+4; + sqrt_nsites = (int)sqrt(sn); + deltay = ymax - ymin; + deltax = xmax - xmin; +} + + +struct Edge * VoronoiDiagramGenerator::bisect(struct Site *s1, struct Site *s2) +{ + double dx,dy,adx,ady; + struct Edge *newedge; + + newedge = (struct Edge *) getfree(&efl); + + newedge -> reg[0] = s1; //store the sites that this edge is bisecting + newedge -> reg[1] = s2; + ref(s1); + ref(s2); + newedge -> ep[0] = (struct Site *) NULL; //to begin with, there are no endpoints on the bisector - it goes to infinity + newedge -> ep[1] = (struct Site *) NULL; + + dx = s2->coord.x - s1->coord.x; //get the difference in x dist between the sites + dy = s2->coord.y - s1->coord.y; + adx = dx>0 ? dx : -dx; //make sure that the difference in positive + ady = dy>0 ? dy : -dy; + newedge -> c = (double)(s1->coord.x * dx + s1->coord.y * dy + (dx*dx + dy*dy)*0.5);//get the slope of the line + + if (adx>ady) + { + newedge -> a = 1.0; newedge -> b = dy/dx; newedge -> c /= dx;//set formula of line, with x fixed to 1 + } + else + { + newedge -> b = 1.0; newedge -> a = dx/dy; newedge -> c /= dy;//set formula of line, with y fixed to 1 + }; + + newedge -> edgenbr = nedges; + + //printf("\nbisect(%d) ((%f,%f) and (%f,%f)",nedges,s1->coord.x,s1->coord.y,s2->coord.x,s2->coord.y); + + nedges += 1; + return(newedge); +} + +//create a new site where the HalfEdges el1 and el2 intersect - note that the Point in the argument list is not used, don't know why it's there +struct Site * VoronoiDiagramGenerator::intersect(struct Halfedge *el1, struct Halfedge *el2, struct Point *p) +{ + struct Edge *e1,*e2, *e; + struct Halfedge *el; + double d, xint, yint; + int right_of_site; + struct Site *v; + + e1 = el1 -> ELedge; + e2 = el2 -> ELedge; + if(e1 == (struct Edge*)NULL || e2 == (struct Edge*)NULL) + return ((struct Site *) NULL); + + //if the two edges bisect the same parent, return null + if (e1->reg[1] == e2->reg[1]) + return ((struct Site *) NULL); + + d = e1->a * e2->b - e1->b * e2->a; + if (-1.0e-10<d && d<1.0e-10) + return ((struct Site *) NULL); + + xint = (e1->c*e2->b - e2->c*e1->b)/d; + yint = (e2->c*e1->a - e1->c*e2->a)/d; + + if( (e1->reg[1]->coord.y < e2->reg[1]->coord.y) || + (e1->reg[1]->coord.y == e2->reg[1]->coord.y && + e1->reg[1]->coord.x < e2->reg[1]->coord.x) ) + { + el = el1; + e = e1; + } + else + { + el = el2; + e = e2; + }; + + right_of_site = xint >= e -> reg[1] -> coord.x; + if ((right_of_site && el -> ELpm == le) || (!right_of_site && el -> ELpm == re)) + return ((struct Site *) NULL); + + //create a new site at the point of intersection - this is a new vector event waiting to happen + v = (struct Site *) getfree(&sfl); + v -> refcnt = 0; + v -> coord.x = xint; + v -> coord.y = yint; + return(v); +} + +/* returns 1 if p is to right of halfedge e */ +int VoronoiDiagramGenerator::right_of(struct Halfedge *el,struct Point *p) +{ + struct Edge *e; + struct Site *topsite; + int right_of_site, above, fast; + double dxp, dyp, dxs, t1, t2, t3, yl; + + e = el -> ELedge; + topsite = e -> reg[1]; + right_of_site = p -> x > topsite -> coord.x; + if(right_of_site && el -> ELpm == le) return(1); + if(!right_of_site && el -> ELpm == re) return (0); + + if (e->a == 1.0) + { dyp = p->y - topsite->coord.y; + dxp = p->x - topsite->coord.x; + fast = 0; + if ((!right_of_site & (e->b<0.0)) | (right_of_site & (e->b>=0.0)) ) + { above = dyp>= e->b*dxp; + fast = above; + } + else + { above = p->x + p->y*e->b > e-> c; + if(e->b<0.0) above = !above; + if (!above) fast = 1; + }; + if (!fast) + { dxs = topsite->coord.x - (e->reg[0])->coord.x; + above = e->b * (dxp*dxp - dyp*dyp) < + dxs*dyp*(1.0+2.0*dxp/dxs + e->b*e->b); + if(e->b<0.0) above = !above; + }; + } + else /*e->b==1.0 */ + { yl = e->c - e->a*p->x; + t1 = p->y - yl; + t2 = p->x - topsite->coord.x; + t3 = yl - topsite->coord.y; + above = t1*t1 > t2*t2 + t3*t3; + }; + return (el->ELpm==le ? above : !above); +} + + +void VoronoiDiagramGenerator::endpoint(struct Edge *e,int lr,struct Site * s) +{ + e -> ep[lr] = s; + ref(s); + if(e -> ep[re-lr]== (struct Site *) NULL) + return; + + clip_line(e); + + deref(e->reg[le]); + deref(e->reg[re]); + makefree((Freenode*)e, &efl); +} + + +double VoronoiDiagramGenerator::dist(struct Site *s,struct Site *t) +{ + double dx,dy; + dx = s->coord.x - t->coord.x; + dy = s->coord.y - t->coord.y; + return (double)(sqrt(dx*dx + dy*dy)); +} + + +void VoronoiDiagramGenerator::makevertex(struct Site *v) +{ + v -> sitenbr = nvertices; + nvertices += 1; + out_vertex(v); +} + + +void VoronoiDiagramGenerator::deref(struct Site *v) +{ + v -> refcnt -= 1; + if (v -> refcnt == 0 ) + makefree((Freenode*)v, &sfl); +} + +void VoronoiDiagramGenerator::ref(struct Site *v) +{ + v -> refcnt += 1; +} + +//push the HalfEdge into the ordered linked list of vertices +void VoronoiDiagramGenerator::PQinsert(struct Halfedge *he,struct Site * v, double offset) +{ + struct Halfedge *last, *next; + + he -> vertex = v; + ref(v); + he -> ystar = (double)(v -> coord.y + offset); + last = &PQhash[PQbucket(he)]; + while ((next = last -> PQnext) != (struct Halfedge *) NULL && + (he -> ystar > next -> ystar || + (he -> ystar == next -> ystar && v -> coord.x > next->vertex->coord.x))) + { + last = next; + }; + he -> PQnext = last -> PQnext; + last -> PQnext = he; + PQcount += 1; +} + +//remove the HalfEdge from the list of vertices +void VoronoiDiagramGenerator::PQdelete(struct Halfedge *he) +{ + struct Halfedge *last; + + if(he -> vertex != (struct Site *) NULL) + { + last = &PQhash[PQbucket(he)]; + while (last -> PQnext != he) + last = last -> PQnext; + + last -> PQnext = he -> PQnext; + PQcount -= 1; + deref(he -> vertex); + he -> vertex = (struct Site *) NULL; + }; +} + +int VoronoiDiagramGenerator::PQbucket(struct Halfedge *he) +{ + int bucket; + + bucket = (int)((he->ystar - ymin)/deltay * PQhashsize); + if (bucket<0) bucket = 0; + if (bucket>=PQhashsize) bucket = PQhashsize-1 ; + if (bucket < PQmin) PQmin = bucket; + return(bucket); +} + + + +int VoronoiDiagramGenerator::PQempty() +{ + return(PQcount==0); +} + + +struct Point VoronoiDiagramGenerator::PQ_min() +{ + struct Point answer; + + while(PQhash[PQmin].PQnext == (struct Halfedge *)NULL) {PQmin += 1;}; + answer.x = PQhash[PQmin].PQnext -> vertex -> coord.x; + answer.y = PQhash[PQmin].PQnext -> ystar; + return (answer); +} + +struct Halfedge * VoronoiDiagramGenerator::PQextractmin() +{ + struct Halfedge *curr; + + curr = PQhash[PQmin].PQnext; + PQhash[PQmin].PQnext = curr -> PQnext; + PQcount -= 1; + return(curr); +} + + +bool VoronoiDiagramGenerator::PQinitialize() +{ + int i; + + PQcount = 0; + PQmin = 0; + PQhashsize = 4 * sqrt_nsites; + PQhash = (struct Halfedge *) myalloc(PQhashsize * sizeof *PQhash); + + if(PQhash == 0) + return false; + + for(i=0; i<PQhashsize; i+=1) PQhash[i].PQnext = (struct Halfedge *)NULL; + + return true; +} + + +void VoronoiDiagramGenerator::freeinit(struct Freelist *fl,int size) +{ + fl -> head = (struct Freenode *) NULL; + fl -> nodesize = size; +} + +char * VoronoiDiagramGenerator::getfree(struct Freelist *fl) +{ + int i; + struct Freenode *t; + + if(fl->head == (struct Freenode *) NULL) + { + t = (struct Freenode *) myalloc(sqrt_nsites * fl->nodesize); + + if(t == 0) + return 0; + + currentMemoryBlock->next = new FreeNodeArrayList; + currentMemoryBlock = currentMemoryBlock->next; + currentMemoryBlock->memory = t; + currentMemoryBlock->next = 0; + + for(i=0; i<sqrt_nsites; i+=1) + makefree((struct Freenode *)((char *)t+i*fl->nodesize), fl); + }; + t = fl -> head; + fl -> head = (fl -> head) -> nextfree; + return((char *)t); +} + + + +void VoronoiDiagramGenerator::makefree(struct Freenode *curr,struct Freelist *fl) +{ + curr -> nextfree = fl -> head; + fl -> head = curr; +} + +void VoronoiDiagramGenerator::cleanup() +{ + if(sites != 0) + { + free(sites); + sites = 0; + } + + FreeNodeArrayList* current=0, *prev = 0; + + current = prev = allMemoryList; + + while(current->next != 0) + { + prev = current; + current = current->next; + free(prev->memory); + delete prev; + prev = 0; + } + + if(current != 0 && current->memory != 0) + { + free(current->memory); + delete current; + } + + allMemoryList = new FreeNodeArrayList; + allMemoryList->next = 0; + allMemoryList->memory = 0; + currentMemoryBlock = allMemoryList; +} + +void VoronoiDiagramGenerator::cleanupEdges() +{ + GraphEdge* geCurrent = 0, *gePrev = 0; + geCurrent = gePrev = allEdges; + + while(geCurrent != 0 && geCurrent->next != 0) + { + gePrev = geCurrent; + geCurrent = geCurrent->next; + delete gePrev; + } + + allEdges = 0; + +} + +void VoronoiDiagramGenerator::cleanupEdgeList() +{ + EdgeList* elCurrent = 0, *elPrev = 0; + elCurrent = elPrev = allEdgeList; + + while (elCurrent != 0 && elCurrent->next != 0) + { + elPrev = elCurrent; + elCurrent = elCurrent->next; + delete elPrev; + } + + allEdgeList = 0; +} + +void VoronoiDiagramGenerator::pushGraphEdge(double x1, double y1, double x2, double y2) +{ + GraphEdge* newEdge = new GraphEdge; + newEdge->next = allEdges; + allEdges = newEdge; + newEdge->x1 = x1; + newEdge->y1 = y1; + newEdge->x2 = x2; + newEdge->y2 = y2; +} + +void VoronoiDiagramGenerator::pushEdgeList(Edge *e) +{ + EdgeList* newEdge = new EdgeList; + newEdge->next = allEdgeList; + allEdgeList = newEdge; + newEdge->a = e->a; + newEdge->b = e->b; + newEdge->c = e->c; + if (e->ep[0]) { + newEdge->ep0nbr = e->ep[0]->sitenbr; + newEdge->ep0x = e->ep[0]->coord.x; + newEdge->ep0y = e->ep[0]->coord.y; + } else { + newEdge->ep0nbr = -1; + } + if (e->ep[1]) { + newEdge->ep1nbr = e->ep[1]->sitenbr; + newEdge->ep1x = e->ep[1]->coord.x; + newEdge->ep1y = e->ep[1]->coord.y; + } else { + newEdge->ep1nbr = -1; + } + newEdge->reg0nbr = e->reg[0]->sitenbr; + newEdge->reg1nbr = e->reg[1]->sitenbr; + newEdge->edgenbr = e->edgenbr; +} + +char * VoronoiDiagramGenerator::myalloc(unsigned n) +{ + char *t=0; + t=(char*)malloc(n); + total_alloc += n; + return(t); +} + + +/* for those who don't have Cherry's plot */ +/* #include <plot.h> */ +void VoronoiDiagramGenerator::openpl(){} +void VoronoiDiagramGenerator::line(double x1, double y1, double x2, double y2) +{ + pushGraphEdge(x1,y1,x2,y2); + +} +void VoronoiDiagramGenerator::circle(double x, double y, double radius){} +void VoronoiDiagramGenerator::range(double minX, double minY, double maxX, double maxY){} + + + +void VoronoiDiagramGenerator::out_bisector(struct Edge *e) +{ + + +} + + +void VoronoiDiagramGenerator::out_ep(struct Edge *e) +{ + + +} + +void VoronoiDiagramGenerator::out_vertex(struct Site *v) +{ + +} + + +void VoronoiDiagramGenerator::out_site(struct Site *s) +{ + if(!triangulate & plot & !debug) + circle (s->coord.x, s->coord.y, cradius); + +} + + +void VoronoiDiagramGenerator::out_triple(struct Site *s1, struct Site *s2,struct Site * s3) +{ + +} + + + +void VoronoiDiagramGenerator::plotinit() +{ +// double dx,dy,d; +// +// dy = ymax - ymin; +// dx = xmax - xmin; +// d = (double)(( dx > dy ? dx : dy) * 1.1); +// pxmin = (double)(xmin - (d-dx)/2.0); +// pxmax = (double)(xmax + (d-dx)/2.0); +// pymin = (double)(ymin - (d-dy)/2.0); +// pymax = (double)(ymax + (d-dy)/2.0); +// cradius = (double)((pxmax - pxmin)/350.0); +// openpl(); +// range(pxmin, pymin, pxmax, pymax); +} + + +void VoronoiDiagramGenerator::clip_line(struct Edge *e) +{ +// struct Site *s1, *s2; +// double x1=0,x2=0,y1=0,y2=0; + + pushEdgeList(e); + +// x1 = e->reg[0]->coord.x; +// x2 = e->reg[1]->coord.x; +// y1 = e->reg[0]->coord.y; +// y2 = e->reg[1]->coord.y; +// +// //if the distance between the two points this line was created from is less than +// //the square root of 2, then ignore it +// if(sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))) < minDistanceBetweenSites) +// { +// return; +// } +// pxmin = borderMinX; +// pxmax = borderMaxX; +// pymin = borderMinY; +// pymax = borderMaxY; +// +// if(e -> a == 1.0 && e ->b >= 0.0) +// { +// s1 = e -> ep[1]; +// s2 = e -> ep[0]; +// } +// else +// { +// s1 = e -> ep[0]; +// s2 = e -> ep[1]; +// }; +// +// if(e -> a == 1.0) +// { +// y1 = pymin; +// if (s1!=(struct Site *)NULL && s1->coord.y > pymin) +// { +// y1 = s1->coord.y; +// } +// if(y1>pymax) +// { +// // printf("\nClipped (1) y1 = %f to %f",y1,pymax); +// y1 = pymax; +// //return; +// } +// x1 = e -> c - e -> b * y1; +// y2 = pymax; +// if (s2!=(struct Site *)NULL && s2->coord.y < pymax) +// y2 = s2->coord.y; +// +// if(y2<pymin) +// { +// //printf("\nClipped (2) y2 = %f to %f",y2,pymin); +// y2 = pymin; +// //return; +// } +// x2 = (e->c) - (e->b) * y2; +// if (((x1> pxmax) & (x2>pxmax)) | ((x1<pxmin)&(x2<pxmin))) +// { +// //printf("\nClipLine jumping out(3), x1 = %f, pxmin = %f, pxmax = %f",x1,pxmin,pxmax); +// return; +// } +// if(x1> pxmax) +// { x1 = pxmax; y1 = (e -> c - x1)/e -> b;}; +// if(x1<pxmin) +// { x1 = pxmin; y1 = (e -> c - x1)/e -> b;}; +// if(x2>pxmax) +// { x2 = pxmax; y2 = (e -> c - x2)/e -> b;}; +// if(x2<pxmin) +// { x2 = pxmin; y2 = (e -> c - x2)/e -> b;}; +// } +// else +// { +// x1 = pxmin; +// if (s1!=(struct Site *)NULL && s1->coord.x > pxmin) +// x1 = s1->coord.x; +// if(x1>pxmax) +// { +// //printf("\nClipped (3) x1 = %f to %f",x1,pxmin); +// //return; +// x1 = pxmax; +// } +// y1 = e -> c - e -> a * x1; +// x2 = pxmax; +// if (s2!=(struct Site *)NULL && s2->coord.x < pxmax) +// x2 = s2->coord.x; +// if(x2<pxmin) +// { +// //printf("\nClipped (4) x2 = %f to %f",x2,pxmin); +// //return; +// x2 = pxmin; +// } +// y2 = e -> c - e -> a * x2; +// if (((y1> pymax) & (y2>pymax)) | ((y1<pymin)&(y2<pymin))) +// { +// //printf("\nClipLine jumping out(6), y1 = %f, pymin = %f, pymax = %f",y2,pymin,pymax); +// return; +// } +// if(y1> pymax) +// { y1 = pymax; x1 = (e -> c - y1)/e -> a;}; +// if(y1<pymin) +// { y1 = pymin; x1 = (e -> c - y1)/e -> a;}; +// if(y2>pymax) +// { y2 = pymax; x2 = (e -> c - y2)/e -> a;}; +// if(y2<pymin) +// { y2 = pymin; x2 = (e -> c - y2)/e -> a;}; +// }; +// +// //printf("\nPushing line (%f,%f,%f,%f)",x1,y1,x2,y2); +// line(x1,y1,x2,y2); +} + + +/* implicit parameters: nsites, sqrt_nsites, xmin, xmax, ymin, ymax, +deltax, deltay (can all be estimates). +Performance suffers if they are wrong; better to make nsites, +deltax, and deltay too big than too small. (?) */ + +bool VoronoiDiagramGenerator::voronoi(int triangulate) +{ + struct Site *newsite, *bot, *top, *temp, *p; + struct Site *v; + struct Point newintstar; + int pm; + struct Halfedge *lbnd, *rbnd, *llbnd, *rrbnd, *bisector; + struct Edge *e; + + PQinitialize(); + bottomsite = nextone(); + out_site(bottomsite); + bool retval = ELinitialize(); + + if(!retval) + return false; + + newsite = nextone(); + while(1) + { + + if(!PQempty()) + newintstar = PQ_min(); + + //if the lowest site has a smaller y value than the lowest vector intersection, process the site + //otherwise process the vector intersection + + if (newsite != (struct Site *)NULL && (PQempty() || newsite -> coord.y < newintstar.y + || (newsite->coord.y == newintstar.y && newsite->coord.x < newintstar.x))) + {/* new site is smallest - this is a site event*/ + out_site(newsite); //output the site + lbnd = ELleftbnd(&(newsite->coord)); //get the first HalfEdge to the LEFT of the new site + rbnd = ELright(lbnd); //get the first HalfEdge to the RIGHT of the new site + bot = rightreg(lbnd); //if this halfedge has no edge, , bot = bottom site (whatever that is) + e = bisect(bot, newsite); //create a new edge that bisects + bisector = HEcreate(e, le); //create a new HalfEdge, setting its ELpm field to 0 + ELinsert(lbnd, bisector); //insert this new bisector edge between the left and right vectors in a linked list + + if ((p = intersect(lbnd, bisector)) != (struct Site *) NULL) //if the new bisector intersects with the left edge, remove the left edge's vertex, and put in the new one + { + PQdelete(lbnd); + PQinsert(lbnd, p, dist(p,newsite)); + }; + lbnd = bisector; + bisector = HEcreate(e, re); //create a new HalfEdge, setting its ELpm field to 1 + ELinsert(lbnd, bisector); //insert the new HE to the right of the original bisector earlier in the IF stmt + + if ((p = intersect(bisector, rbnd)) != (struct Site *) NULL) //if this new bisector intersects with the + { + PQinsert(bisector, p, dist(p,newsite)); //push the HE into the ordered linked list of vertices + }; + newsite = nextone(); + } + else if (!PQempty()) /* intersection is smallest - this is a vector event */ + { + lbnd = PQextractmin(); //pop the HalfEdge with the lowest vector off the ordered list of vectors + llbnd = ELleft(lbnd); //get the HalfEdge to the left of the above HE + rbnd = ELright(lbnd); //get the HalfEdge to the right of the above HE + rrbnd = ELright(rbnd); //get the HalfEdge to the right of the HE to the right of the lowest HE + bot = leftreg(lbnd); //get the Site to the left of the left HE which it bisects + top = rightreg(rbnd); //get the Site to the right of the right HE which it bisects + + out_triple(bot, top, rightreg(lbnd)); //output the triple of sites, stating that a circle goes through them + + v = lbnd->vertex; //get the vertex that caused this event + makevertex(v); //set the vertex number - couldn't do this earlier since we didn't know when it would be processed + endpoint(lbnd->ELedge,lbnd->ELpm,v); //set the endpoint of the left HalfEdge to be this vector + endpoint(rbnd->ELedge,rbnd->ELpm,v); //set the endpoint of the right HalfEdge to be this vector + ELdelete(lbnd); //mark the lowest HE for deletion - can't delete yet because there might be pointers to it in Hash Map + PQdelete(rbnd); //remove all vertex events to do with the right HE + ELdelete(rbnd); //mark the right HE for deletion - can't delete yet because there might be pointers to it in Hash Map + pm = le; //set the pm variable to zero + + if (bot->coord.y > top->coord.y) //if the site to the left of the event is higher than the Site + { //to the right of it, then swap them and set the 'pm' variable to 1 + temp = bot; + bot = top; + top = temp; + pm = re; + } + e = bisect(bot, top); //create an Edge (or line) that is between the two Sites. This creates + //the formula of the line, and assigns a line number to it + bisector = HEcreate(e, pm); //create a HE from the Edge 'e', and make it point to that edge with its ELedge field + ELinsert(llbnd, bisector); //insert the new bisector to the right of the left HE + endpoint(e, re-pm, v); //set one endpoint to the new edge to be the vector point 'v'. + //If the site to the left of this bisector is higher than the right + //Site, then this endpoint is put in position 0; otherwise in pos 1 + deref(v); //delete the vector 'v' + + //if left HE and the new bisector don't intersect, then delete the left HE, and reinsert it + if((p = intersect(llbnd, bisector)) != (struct Site *) NULL) + { + PQdelete(llbnd); + PQinsert(llbnd, p, dist(p,bot)); + }; + + //if right HE and the new bisector don't intersect, then reinsert it + if ((p = intersect(bisector, rrbnd)) != (struct Site *) NULL) + { + PQinsert(bisector, p, dist(p,bot)); + }; + } + else break; + }; + + + + + for(lbnd=ELright(ELleftend); lbnd != ELrightend; lbnd=ELright(lbnd)) + { + e = lbnd -> ELedge; + + clip_line(e); + + }; + + cleanup(); + + return true; + +} + + +int scomp(const void *p1,const void *p2) +{ + struct Point *s1 = (Point*)p1, *s2=(Point*)p2; + if(s1 -> y < s2 -> y) return(-1); + if(s1 -> y > s2 -> y) return(1); + if(s1 -> x < s2 -> x) return(-1); + if(s1 -> x > s2 -> x) return(1); + return(0); +} + +/* return a single in-storage site */ +struct Site * VoronoiDiagramGenerator::nextone() +{ + struct Site *s; + if(siteidx < nsites) + { + s = &sites[siteidx]; + siteidx += 1; + return(s); + } + else + return( (struct Site *)NULL); +} + +bool VoronoiDiagramGenerator::getNextDelaunay(int& ep0, double& ep0x, double& ep0y, + int& ep1, double& ep1x, double& ep1y, + int& reg0, int& reg1) +{ + if (iterEdgeList == 0) + return false; + + ep0 = iterEdgeList->ep0nbr; + ep0x = iterEdgeList->ep0x; + ep0y = iterEdgeList->ep0y; + ep1 = iterEdgeList->ep1nbr; + ep1x = iterEdgeList->ep1x; + ep1y = iterEdgeList->ep1y; + reg0 = iterEdgeList->reg0nbr; + reg1 = iterEdgeList->reg1nbr; + + iterEdgeList = iterEdgeList->next; + + return true; +} + +//PyObject* VoronoiDiagramGenerator::_getMesh() +//{ +// PyObject *vlist, *dlist, *tlist; +// PyObject *temp, *faces, *face; +// int tri0, tri1, reg0, reg1; +// double tri0x, tri0y, tri1x, tri1y; +// int length, numtri, i; +// +// length = nedges; +// numtri = nvertices; +// +// dlist = PyList_New(length); +// if (!dlist) goto fail; +// vlist = PyList_New(numtri); +// if (!vlist) goto fail; +// tlist = PyList_New(numtri); +// if (!tlist) goto fail; +// +// for (i=0; i<numtri; i++) { +// faces = PyList_New(0); +// if (!faces) goto fail; +// PyList_SET_ITEM(tlist, i, faces); +// } +// +// resetEdgeListIter(); +// i = -1; +// while (getNextDelaunay(tri0, tri0x, tri0y, tri1, tri1x, tri1y, reg0, reg1)) { +// i++; +// face = Py_BuildValue("(ii)", reg0, reg1); +// if (!face) goto fail; +// PyList_SET_ITEM(dlist, i, face); +// if (tri0 > -1) { +// temp = PyList_GET_ITEM(vlist, tri0); +// if (!temp) { +// temp = Py_BuildValue("(dd)", tri0x, tri0y); +// PyList_SET_ITEM(vlist, tri0, temp); +// } +// faces = PyList_GET_ITEM(tlist, tri0); +// if (PyList_Append(faces, face) < 0) goto fail; +// } +// if (tri1 > -1) { +// temp = PyList_GET_ITEM(vlist, tri1); +// if (!temp) { +// temp = Py_BuildValue("(dd)", tri1x, tri1y); +// PyList_SET_ITEM(vlist, tri1, temp); +// } +// faces = PyList_GET_ITEM(tlist, tri1); +// if (PyList_Append(faces, face) < 0) goto fail; +// } +// } +// +// temp = PyTuple_Pack(3, vlist, dlist, tlist); +// if (!temp) goto fail; +// +// Py_DECREF(vlist); +// Py_DECREF(dlist); +// Py_DECREF(tlist); +// +// return temp; +// +//fail: +// Py_XDECREF(vlist); +// Py_XDECREF(dlist); +// Py_XDECREF(temp); +// Py_XDECREF(faces); +// Py_XDECREF(face); +// return NULL; +//} + + Added: trunk/matplotlib/lib/matplotlib/delaunay/VoronoiDiagramGenerator.h =================================================================== --- trunk/matplotlib/lib/matplotlib/delaunay/VoronoiDiagramGenerator.h (rev 0) +++ trunk/matplotlib/lib/matplotlib/delaunay/VoronoiDiagramGenerator.h 2008-07-22 01:52:12 UTC (rev 5805) @@ -0,0 +1,283 @@ +/* +* The author of this software is Steven Fortune. Copyright (c) 1994 by AT&T +* Bell Laboratories. +* Permission to use, copy, modify, and distribute this software for any +* purpose without fee is hereby granted, provided that this entire notice +* is included in all copies of any software which is or includes a copy +* or modification of this software and in all copies of the supporting +* documentation for such software. +* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY +* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY +* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +/* +* This code was originally written by Stephan Fortune in C code. I, Shane O'Sullivan, +* have since modified it, encapsulating it in a C++ class and, fixing memory leaks and +* adding accessors to the Voronoi Edges. +* Permission to use, copy, modify, and distribute this software for any +* purpose without fee is hereby granted, provided that this entire notice +* is included in all copies of any software which is or includes a copy +* or modification of this software and in all copies of the supporting +* documentation for such software. +* THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED +* WARRANTY. IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY +* REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY +* OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. +*/ + +#ifndef VORONOI_DIAGRAM_GENERATOR +#define VORONOI_DIAGRAM_GENERATOR + +#include "Python.h" +#include "numpy/arrayobject.h" + +#include <math.h> +#include <stdlib.h> +#include <string.h> +#include <iostream> + + +#ifndef NULL +#define NULL 0 +#endif +#define DELETED -2 + +#define le 0 +#define re 1 + +#ifndef MAX +#define MAX(x, y) (x > y ? x: y) +#endif + +struct Freenode +{ + struct Freenode *nextfree; +}; + +struct FreeNodeArrayList +{ + struct Freenode* memory; + struct FreeNodeArrayList* next; + +}; + +struct Freelist +{ + struct Freenode *head; + int nodesize; +}; + +struct Point +{ + double x,y; +}; + +// structure used both for sites and for vertices +struct Site +{ + struct Point coord; + int sitenbr; + int refcnt; +}; + + + +struct Edge +{ + double a,b,c; + struct Site *ep[2]; + struct Site *reg[2]; + int edgenbr; +}; + +struct EdgeList +{ + double a,b,c; + int ep0nbr; + double ep0x, ep0y; + int ep1nbr; + double ep1x, ep1y; + int reg0nbr; + int reg1nbr; + int edgenbr; + struct EdgeList *next; +}; + +struct GraphEdge +{ + double x1,y1,x2,y2; + struct GraphEdge* next; +}; + + + + +struct Halfedge +{ + struct Halfedge *ELleft, *ELright; + struct Edge *ELedge; + int ELrefcnt; + char ELpm; + struct Site *vertex; + double ystar; + struct Halfedge *PQnext; +}; + + + + +class VoronoiDiagramGenerator +{ +public: + VoronoiDiagramGenerator(); + ~VoronoiDiagramGenerator(); + + bool generateVoronoi(double *xValues, double *yValues, int numPoints, double minX, double maxX, double minY, double maxY, double minDist=0); + + void resetIterator() + { + iteratorEdges = allEdges; + } + + bool getNext(double& x1, double& y1, double& x2, double& y2) + { + if(iteratorEdges == 0) + return false; + + x1 = iteratorEdges->x1; + x2 = iteratorEdges->x2; + y1 = iteratorEdges->y1; + y2 = iteratorEdges->y2; + + iteratorEdges = iteratorEdges->next; + + return true; + } + + void resetEdgeListIter() + { + iterEdgeList = allEdgeList; + } + + bool getNextDelaunay(int& ep0, double& ep0x, double& ep0y, + int& ep1, double& ep1x, double& ep1y, + int& reg0, int& reg1); + + void getNumbers(int& edges, int& vertices) { + edges = nedges; + vertices = nvertices; + } + +private: + void cleanup(); + void cleanupEdgeList(); + void cleanupEdges(); + char *getfree(struct Freelist *fl); + struct Halfedge *PQfind(); + int PQempty(); + + + struct Halfedge **ELhash; + struct Halfedge *HEcreate(), *ELleft(), *ELright(), *ELleftbnd(); + struct Halfedge *HEcreate(struct Edge *e,int pm); + + + struct Point PQ_min(); + struct Halfedge *PQextractmin(); + void freeinit(struct Freelist *fl,int size); + void makefree(struct Freenode *curr,struct Freelist *fl); + void geominit(); + void plotinit(); + bool voronoi(int triangulate); + void ref(struct Site *v); + void deref(struct Site *v); + void endpoint(struct Edge *e,int lr,struct Site * s); + + void ELdelete(struct Halfedge *he); + struct Halfedge *ELleftbnd(struct Point *p); + struct Halfedge *ELright(struct Halfedge *he); + void makevertex(struct Site *v); + void out_triple(struct Site *s1, struct Site *s2,struct Site * s3); + + void PQinsert(struct Halfedge *he,struct Site * v, double offset); + void PQdelete(struct Halfedge *he); + bool ELinitialize(); + void ELinsert(struct Halfedge *lb, struct Halfedge *newHe); + struct Halfedge *ELgethash(int b); + struct Halfedge *ELleft(struct Halfedge *he); + struct Site *leftreg(struct Halfedge *he); + void out_site(struct Site *s); + bool PQinitialize(); + int PQbucket(struct Halfedge *he); + void clip_line(struct Edge *e); + char *myalloc(unsigned n); + int right_of(struct Halfedge *el,struct Point *p); + + struct Site *rightreg(struct Halfedge *he); + struct Edge *bisect(struct Site *s1, struct Site *s2); + double dist(struct Site *s,struct Site *t); + struct Site *intersect(struct Halfedge *el1, struct Halfedge *el2, struct Point *p=0); + + void out_bisector(struct Edge *e); + void out_ep(struct Edge *e); + void out_vertex(struct Site *v); + struct Site *nextone(); + + void pushGraphEdge(double x1, double y1, double x2, double y2); + void pushEdgeList(Edge *e); + + void openpl(); + void line(double x1, double y1, double x2, double y2); + void circle(double x, double y, double radius); + void range(double minX, double minY, double maxX, double maxY); + + + struct Freelist hfl; + struct Halfedge *ELleftend, *ELrightend; + int ELhashsize; + + int triangulate, sorted, plot, debug; + double xmin, xmax, ymin, ymax, deltax, deltay; + + struct Site *sites; + int nsites; + int siteidx; + int sqrt_nsites; + int nvertices; + struct Freelist sfl; + struct Site *bottomsite; + + int nedges; + struct Freelist efl; + int PQhashsize; + struct Halfedge *PQhash; + int PQcount; + int PQmin; + + int ntry, totalsearch; + double pxmin, pxmax, pymin, pymax, cradius; + int total_alloc; + + double borderMinX, borderMaxX, borderMinY, borderMaxY; + + FreeNodeArrayList* allMemoryList; + FreeNodeArrayList* currentMemoryBlock; + + GraphEdge* allEdges; + GraphEdge* iteratorEdges; + + EdgeList* allEdgeList; + EdgeList* iterEdgeList; + + double minDistanceBetweenSites; + +}; + +int scomp(const void *p1, const void *p2); + + +#endif + + Added: trunk/matplotlib/lib/matplotlib/delaunay/__init__.py =================================================================== --- trunk/matplotlib/lib/matplotlib/delaunay/__init__.py (rev 0) +++ trunk/matplotlib/lib/matplotlib/delaunay/__init__.py 2008-07-22 01:52:12 UTC (rev 5805) @@ -0,0 +1,10 @@ +"""Delaunay triangulation and interpolation tools. + +:Author: Robert Kern <rob...@gm...> +:Copyright: Copyright 2005 Robert Kern. +:License: BSD-style license. See LICENSE.txt in the scipy source directory. +""" + +from matplotlib._delaunay import delaunay +from triangulate import * +from interpolate import * Added: trunk/matplotlib/lib/matplotlib/delaunay/_delaunay.cpp =================================================================== --- trunk/matplotlib/lib/matplotlib/delaunay/_delaunay.cpp (rev 0) +++ trunk/matplotlib/lib/matplotlib/delaunay/_delaunay.cpp 2008-07-22 01:52:12 UTC (rev 5805) @@ -0,0 +1,741 @@ +#include "Python.h" +#include <stdlib.h> +#include <map> +#include <iostream> + +#include "VoronoiDiagramGenerator.h" +#include "delaunay_utils.h" +#include "natneighbors.h" +#include "numpy/noprefix.h" + +using namespace std; + +extern "C" { + +static void reorder_edges(int npoints, int ntriangles, + double *x, double *y, + int *edge_db, int *tri_edges, int *tri_nbrs) +{ + int neighbors[3], nodes[3]; + int i, tmp; + int case1, case2; + + for (i=0; i<ntriangles; i++) { + nodes[0] = INDEX2(edge_db, INDEX3(tri_edges,i,0), 0); + nodes[1] = INDEX2(edge_db, INDEX3(tri_edges,i,0), 1); + tmp = INDEX2(edge_db, INDEX3(tri_edges,i,1), 0); + if (tmp == nodes[0]) { + case1 = 1; + nodes[2] = INDEX2(edge_db, INDEX3(tri_edges,i,1), 1); + } else if (tmp == nodes[1]) { + case1 = 0; + nodes[2] = INDEX2(edge_db, INDEX3(tri_edges,i,1), 1); + } else if (INDEX2(edge_db, INDEX3(tri_edges,i,1), 1) == nodes[0]) { + case1 = 1; + nodes[2] = tmp; + } else { + case1 = 0; + nodes[2] = tmp; + } + + if (ONRIGHT(x[nodes[0]], y[nodes[0]], + x[nodes[1]], y[nodes[1]], + x[nodes[2]], y[nodes[2]])) { + // flip to make counter-clockwise + tmp = nodes[2]; + nodes[2] = nodes[1]; + nodes[1] = tmp; + case2 = 1; + } else case2 = 0; + + // I worked it out on paper. You're just gonna have to trust me on this. + if (!case1 && !case2) { + neighbors[0] = INDEX3(tri_nbrs, i, 1); + neighbors[1] = INDEX3(tri_nbrs, i, 2); + neighbors[2] = INDEX3(tri_nbrs, i, 0); + } else if (case1 && !case2) { + neighbors[0] = INDEX3(tri_nbrs, i, 2); + neighbors[1] = INDEX3(tri_nbrs, i, 1); + neighbors[2] = INDEX3(tri_nbrs, i, 0); + } else if (!case1 && case2) { + neighbors[0] = INDEX3(tri_nbrs, i, 1); + neighbors[1] = INDEX3(tri_nbrs, i, 0); + neighbors[2] = INDEX3(tri_nbrs, i, 2); + } else { + neighbors[0] = INDEX3(tri_nbrs, i, 2); + neighbors[1] = INDEX3(tri_nbrs, i, 0); + neighbors[2] = INDEX3(tri_nbrs, i, 1); + } + + // Not trusting me? Okay, let's go through it: + // We have three edges to deal with and three nodes. Without loss + // of generality, let's label the nodes A, B, and C with (A, B) + // forming the first edge in the order they arrive on input. + // Then there are eight possibilities as to how the other edge-tuples + // may be labeled, but only two variations that are going to affect the + // output: + // + // AB AB + // BC (CB) AC (CA) + // CA (AC) BC (CB) + // + // The distinction is whether A is in the second edge or B is. + // This is the test "case1" above. + // + // The second test we need to perform is for counter-clockwiseness. + // Again, there are only two variations that will affect the outcome: + // either ABC is counter-clockwise, or it isn't. In the former case, + // we're done setting the node order, we just need to associate the + // appropriate neighbor triangles with their opposite nodes, something + // which can be done by inspection. In the latter case, to order the + // nodes counter-clockwise, we only have to switch B and C to get + // nodes ACB. Then we simply set the neighbor list by inspection again. + // + // CCW CW + // AB + // BC 120 102 -+ + // CA | + // +- neighbor order + // AB | + // AC 210 201 -+ + // BC + // ABC ACB -+- node order + + + INDEX3(tri_edges,i,0) = nodes[0]; + INDEX3(tri_edges,i,1) = nodes[1]; + INDEX3(tri_edges,i,2) = nodes[2]; + INDEX3(tri_nbrs,i,0) = neighbors[0]; + INDEX3(tri_nbrs,i,1) = neighbors[1]; + INDEX3(tri_nbrs,i,2) = neighbors[2]; + } +} + +static PyObject* getMesh(int npoints, double *x, double *y) +{ + PyObject *vertices, *edge_db, *tri_edges, *tri_nbrs; + PyObject *temp; + int tri0, tri1, reg0, reg1; + double tri0x, tri0y, tri1x, tri1y; + int length, numtri, i, j; + intp dim[MAX_DIMS]; + int *edge_db_ptr, *tri_edges_ptr, *tri_nbrs_ptr; + double *vertices_ptr; + VoronoiDiagramGenerator vdg; + + vdg.generateVoronoi(x, y, npoints, -100, 100, -100, 100, 0); + vdg.getNumbers(length, numtri); + + // Count the actual number of edges + i = 0; + vdg.resetEdgeListIter(); + while (vdg.getNextDelaunay(tri0, tri0x, tri0y, tri1, tri1x, tri1y, reg0, reg1)) + i++; + length = i; + + dim[0] = length; + dim[1] = 2; + edge_db = PyArray_SimpleNew(2, dim, PyArray_INT); + if (!edge_db) goto fail; + edge_db_ptr = (int*)PyArray_DATA(edge_db); + + dim[0] = numtri; + vertices = PyArray_SimpleNew(2, dim, PyArray_DOUBLE); + if (!vertices) goto fail; + vertices_ptr = (double*)PyArray_DATA(vertices); + + dim[1] = 3; + tri_edges = PyArray_SimpleNew(2, dim, PyArray_INT); + if (!tri_edges) goto fail; + tri_edges_ptr = (int*)PyArray_DATA(tri_edges); + + tri_nbrs = PyArray_SimpleNew(2, dim, PyArray_INT); + if (!tri_nbrs) goto fail; + tri_nbrs_ptr = (int*)PyArray_DATA(tri_nbrs); + + for (i=0; i<(3*numtri); i++) { + tri_edges_ptr[i] = tri_nbrs_ptr[i] = -1; + } + + vdg.resetEdgeListIter(); + i = -1; + while (vdg.getNextDelaunay(tri0, tri0x, tri0y, tri1, tri1x, tri1y, reg0, reg1)) { + i++; + INDEX2(edge_db_ptr,i,0) = reg0; + INDEX2(edge_db_ptr,i,1) = reg1; + if (tri0 > -1) { + INDEX2(vertices_ptr,tri0,0) = tri0x; + INDEX2(vertices_ptr,tri0,1) = tri0y; + for (j=0; j<3; j++) { + if (INDEX3(tri_edges_ptr,tri0,j) == i) break; + if (INDEX3(tri_edges_ptr,tri0,j) == -1) { + INDEX3(tri_edges_ptr,tri0,j) = i; + INDEX3(tri_nbrs_ptr,tri0,j) = tri1; + break; + } + } + } + if (tri1 > -1) { + INDEX2(vertices_ptr,tri1,0) = tri1x; + INDEX2(vertices_ptr,tri1,1) = tri1y; + for (j=0; j<3; j++) { + if (INDEX3(tri_edges_ptr,tri1,j) == i) break; + if (INDEX3(tri_edges_ptr,tri1,j) == -1) { + INDEX3(tri_edges_ptr,tri1,j) = i; + INDEX3(tri_nbrs_ptr,tri1,j) = tri0; + break; + } + } + } + } + + // tri_edges contains lists of edges; convert to lists of nodes in + // counterclockwise order and reorder tri_nbrs to match. Each node + // corresponds to the edge opposite it in the triangle. + reorder_edges(npoints, numtri, x, y, edge_db_ptr, tri_edges_ptr, + tri_nbrs_ptr); + + temp = Py_BuildValue("(OOOO)", vertices, edge_db, tri_edges, tri_nbrs); + if (!temp) goto fail; + + Py_DECREF(vertices); + Py_DECREF(edge_db); + Py_DECREF(tri_edges); + Py_DECREF(tri_nbrs); + + return temp; + +fail: + Py_XDECREF(vertices); + Py_XDECREF(edge_db); + Py_XDECREF(tri_edges); + Py_XDECREF(tri_nbrs); + return NULL; +} + +static PyObject *linear_planes(int ntriangles, double *x, double *y, double *z, + int *nodes) +{ + intp dims[2]; + PyObject *planes; + int i; + double *planes_ptr; + double x02, y02, z02, x12, y12, z12, xy0212; + + dims[0] = ntriangles; + dims[1] = 3; + planes = PyArray_SimpleNew(2, dims, PyArray_DOUBLE); + if (!planes) return NULL; + planes_ptr = (double *)PyArray_DATA(planes); + + for (i=0; i<ntriangles; i++) { + x02 = x[INDEX3(nodes,i,0)] - x[INDEX3(nodes,i,2)]; + y02 = y[INDEX3(nodes,i,0)] - y[INDEX3(nodes,i,2)]; + z02 = z[INDEX3(nodes,i,0)] - z[INDEX3(nodes,i,2)]; + x12 = x[INDEX3(nodes,i,1)] - x[INDEX3(nodes,i,2)]; + y12 = y[INDEX3(nodes,i,1)] - y[INDEX3(nodes,i,2)]; + z12 = z[INDEX3(nodes,i,1)] - z[INDEX3(nodes,i,2)]; + + if (y12 != 0.0) { + xy0212 = y02/y12; + INDEX3(planes_ptr,i,0) = (z02 - z12 * xy0212) / (x02 - x12 * xy0212); + INDEX3(planes_ptr,i,1) = (z12 - INDEX3(planes_ptr,i,0)*x12) / y12; + INDEX3(planes_ptr,i,2) = (z[INDEX3(nodes,i,2)] - + INDEX3(planes_ptr,i,0)*x[INDEX3(nodes,i,2)] - + INDEX3(planes_ptr,i,1)*y[INDEX3(nodes,i,2)]); + } else { + xy0212 = x02/x12; + INDEX3(planes_ptr,i,1) = (z02 - z12 * xy0212) / (y02 - y12 * xy0212); + INDEX3(planes_ptr,i,0) = (z12 - INDEX3(planes_ptr,i,1)*y12) / x12; + INDEX3(planes_ptr,i,2) = (z[INDEX3(nodes,i,2)] - + INDEX3(planes_ptr,i,0)*x[INDEX3(nodes,i,2)] - + INDEX3(planes_ptr,i,1)*y[INDEX3(nodes,i,2)]); + } + } + + return (PyObject*)planes; +} + +static double linear_interpolate_single(double targetx, double targety, + double *x, double *y, int *nodes, int *neighbors, + PyObject *planes, double defvalue, int start_triangle, int *end_triangle) +{ + double *planes_ptr; + planes_ptr = (double*)PyArray_DATA(planes); + if (start_triangle == -1) start_triangle = 0; + *end_triangle = walking_triangles(start_triangle, targetx, targety, + x, y, nodes, neighbors); + if (*end_triangle == -1) return defvalue; + return (targetx*INDEX3(planes_ptr,*end_triangle,0) + + targety*INDEX3(planes_ptr,*end_triangle,1) + + INDEX3(planes_ptr,*end_triangle,2)); +} + +static PyObject *linear_interpolate_grid(double x0, double x1, int xsteps, + double y0, double y1, int ysteps, + PyObject *planes, double defvalue, + int npoints, double *x, double *y, int *nodes, int *neighbors) +{ + int ix, iy; + double dx, dy, targetx, targety; + int rowtri, coltri, tri; + PyObject *z; + double *z_ptr; + intp dims[2]; + + dims[0] = ysteps; + dims[1] = xsteps; + z = PyArray_SimpleNew(2, dims, PyArray_DOUBLE); + if (!z) return NULL; + z_ptr = (double*)PyArray_DATA(z); + + dx = (x1 - x0) / (xsteps-1); + dy = (y1 - y0) / (ysteps-1); + + rowtri = 0; + for (iy=0; iy<ysteps; iy++) { + targety = y0 + dy*iy; + rowtri = walking_triangles(rowtri, x0, targety, x, y, nodes, neighbors); + tri = rowtri; + for (ix=0; ix<xsteps; ix++) { + targetx = x0 + dx*ix; + INDEXN(z_ptr, xsteps, iy, ix) = linear_interpolate_single( + targetx, targety, + x, y, nodes, neighbors, planes, defvalue, tri, &coltri); + if (coltri != -1) tri = coltri; + } + } + + return z; +} + +static PyObject *compute_planes_method(PyObject *self, PyObject *args) +{ + PyObject *pyx, *pyy, *pyz, *pynodes; + PyObject *x, *y, *z, *nodes; + int npoints, ntriangles; + + PyObject *planes; + + if (!PyArg_ParseTuple(args, "OOOO", &pyx, &pyy, &pyz, &pynodes)) { + return NULL; + } + x = PyArray_FROMANY(pyx, PyArray_DOUBLE, 1, 1, NPY_IN_ARRAY); + if (!x) { + PyErr_SetString(PyExc_ValueError, "x must be a 1-D array of floats"); + goto fail; + } + y = PyArray_FROMANY(pyy, PyArray_DOUBLE, 1, 1, NPY_IN_ARRAY); + if (!y) { + PyErr_SetString(PyExc_ValueError, "y must be a 1-D array of floats"); + goto fail; + } + z = PyArray_FROMANY(pyz, PyArray_DOUBLE, 1, 1, NPY_IN_ARRAY); + if (!z) { + PyErr_SetString(PyExc_ValueError, "z must be a 1-D array of floats"); + goto fail; + } + npoints = PyArray_DIM(x, 0); + if ((PyArray_DIM(y, 0) != npoints) || (PyArray_DIM(z, 0) != npoints)) { + PyErr_SetString(PyExc_ValueError, "x,y,z arrays must be of equal length"); + goto fail; + } + nodes = PyArray_FROMANY(pynodes, PyA... [truncated message content] |
From: <ry...@us...> - 2008-07-23 03:08:44
|
Revision: 5819 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5819&view=rev Author: ryanmay Date: 2008-07-23 03:08:41 +0000 (Wed, 23 Jul 2008) Log Message: ----------- Added Barbs polygon collection (similar to Quiver) for plotting wind barbs. Added corresponding helpers to Axes and pyplot as well. (examples/pylab_examples/barb_demo.py shows it off.) Modified Paths: -------------- trunk/matplotlib/CHANGELOG trunk/matplotlib/boilerplate.py trunk/matplotlib/examples/tests/backend_driver.py trunk/matplotlib/lib/matplotlib/axes.py trunk/matplotlib/lib/matplotlib/pyplot.py trunk/matplotlib/lib/matplotlib/quiver.py Added Paths: ----------- trunk/matplotlib/examples/pylab_examples/barb_demo.py Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-23 02:44:49 UTC (rev 5818) +++ trunk/matplotlib/CHANGELOG 2008-07-23 03:08:41 UTC (rev 5819) @@ -1,3 +1,7 @@ +2008-07-22 Added Barbs polygon collection (similar to Quiver) for plotting + wind barbs. Added corresponding helpers to Axes and pyplot as + well. (examples/pylab_examples/barb_demo.py shows it off.) - RMM + 2008-07-21 Added scikits.delaunay as matplotlib.delaunay. Added griddata function in matplotlib.mlab, with example (griddata_demo.py) in pylab_examples. griddata function will use mpl_toolkits._natgrid Modified: trunk/matplotlib/boilerplate.py =================================================================== --- trunk/matplotlib/boilerplate.py 2008-07-23 02:44:49 UTC (rev 5818) +++ trunk/matplotlib/boilerplate.py 2008-07-23 03:08:41 UTC (rev 5819) @@ -86,6 +86,7 @@ 'step', 'vlines', 'xcorr', + 'barbs', ) _misccommands = ( Added: trunk/matplotlib/examples/pylab_examples/barb_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/barb_demo.py (rev 0) +++ trunk/matplotlib/examples/pylab_examples/barb_demo.py 2008-07-23 03:08:41 UTC (rev 5819) @@ -0,0 +1,33 @@ +''' +Demonstration of wind barb plots +''' +import matplotlib.pyplot as plt +import numpy as np + +x = np.linspace(-5, 5, 5) +X,Y = np.meshgrid(x, x) +U, V = 12*X, 12*Y + +data = [(-1.5,.5,-6,-6), + (1,-1,-46,46), + (-3,-1,11,-11), + (1,1.5,80,80)] + +#Default parameters for arbitrary set of vectors +ax = plt.subplot(2,2,1) +ax.barbs(*zip(*data)) + +#Default parameters, uniform grid +ax = plt.subplot(2,2,2) +ax.barbs(X, Y, U, V) + +#Change parameters for arbitrary set of vectors +ax = plt.subplot(2,2,3) +ax.barbs(flagcolor='r', barbcolor=['b','g'], barb_increments=dict(half=10, + full=20, flag=100), *zip(*data)) + +#Showing colormapping with uniform grid. +ax = plt.subplot(2,2,4) +ax.barbs(X, Y, U, V, np.sqrt(U*U + V*V), fill_empty=True, rounding=False) + +plt.show() Modified: trunk/matplotlib/examples/tests/backend_driver.py =================================================================== --- trunk/matplotlib/examples/tests/backend_driver.py 2008-07-23 02:44:49 UTC (rev 5818) +++ trunk/matplotlib/examples/tests/backend_driver.py 2008-07-23 03:08:41 UTC (rev 5819) @@ -33,6 +33,7 @@ 'axhspan_demo.py', 'bar_stacked.py', 'barchart_demo.py', + 'barb_demo.py', 'boxplot_demo.py', 'broken_barh.py', 'barh_demo.py', Modified: trunk/matplotlib/lib/matplotlib/axes.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axes.py 2008-07-23 02:44:49 UTC (rev 5818) +++ trunk/matplotlib/lib/matplotlib/axes.py 2008-07-23 03:08:41 UTC (rev 5819) @@ -5224,6 +5224,15 @@ return q quiver.__doc__ = mquiver.Quiver.quiver_doc + def barbs(self, *args, **kw): + if not self._hold: self.cla() + b = mquiver.Barbs(self, *args, **kw) + self.add_collection(b) + self.update_datalim(b.get_offsets()) + self.autoscale_view() + return b + barbs.__doc__ = mquiver.Barbs.barbs_doc + def fill(self, *args, **kwargs): """ call signature:: Modified: trunk/matplotlib/lib/matplotlib/pyplot.py =================================================================== --- trunk/matplotlib/lib/matplotlib/pyplot.py 2008-07-23 02:44:49 UTC (rev 5818) +++ trunk/matplotlib/lib/matplotlib/pyplot.py 2008-07-23 03:08:41 UTC (rev 5819) @@ -2342,6 +2342,28 @@ # This function was autogenerated by boilerplate.py. Do not edit as # changes will be lost +def barbs(*args, **kwargs): + # allow callers to override the hold state by passing hold=True|False + b = ishold() + h = kwargs.pop('hold', None) + if h is not None: + hold(h) + try: + ret = gca().barbs(*args, **kwargs) + draw_if_interactive() + except: + hold(b) + raise + + hold(b) + return ret +if Axes.barbs.__doc__ is not None: + barbs.__doc__ = dedent(Axes.barbs.__doc__) + """ + +Additional kwargs: hold = [True|False] overrides default hold state""" + +# This function was autogenerated by boilerplate.py. Do not edit as +# changes will be lost def cla(*args, **kwargs): ret = gca().cla(*args, **kwargs) Modified: trunk/matplotlib/lib/matplotlib/quiver.py =================================================================== --- trunk/matplotlib/lib/matplotlib/quiver.py 2008-07-23 02:44:49 UTC (rev 5818) +++ trunk/matplotlib/lib/matplotlib/quiver.py 2008-07-23 03:08:41 UTC (rev 5819) @@ -1,9 +1,14 @@ """ -Support for plotting fields of arrows. +Support for plotting vector fields. -Presently this contains a single class, Quiver, but it -might make sense to consolidate other arrow plotting here. +Presently this contains Quiver and Barb. Quiver plots an arrow in the +direction of the vector, with the size of the arrow related to the +magnitude of the vector. +Barbs are like quiver in that they point along a vector, but +the magnitude of the vector is given schematically by the presence of barbs +or flags on the barb. + This will also become a home for things such as standard deviation ellipses, which can and will be derived very easily from the Quiver code. @@ -17,6 +22,8 @@ import matplotlib.text as mtext import matplotlib.artist as martist import matplotlib.font_manager as font_manager +from matplotlib.cbook import delete_masked_points +from matplotlib.patches import CirclePolygon import math @@ -498,3 +505,388 @@ return X, Y quiver_doc = _quiver_doc + +_barbs_doc = """ +Plot a 2-D field of barbs. + +call signatures:: + + barb(U, V, **kw) + barb(U, V, C, **kw) + barb(X, Y, U, V, **kw) + barb(X, Y, U, V, C, **kw) + +Arguments: + + *X*, *Y*: + The x and y coordinates of the barb locations + (default is head of barb; see *pivot* kwarg) + + *U*, *V*: + give the *x* and *y* components of the barb shaft + + *C*: + an optional array used to map colors to the barbs + +All arguments may be 1-D or 2-D arrays or sequences. If *X* and *Y* +are absent, they will be generated as a uniform grid. If *U* and *V* +are 2-D arrays but *X* and *Y* are 1-D, and if len(*X*) and len(*Y*) +match the column and row dimensions of *U*, then *X* and *Y* will be +expanded with :func:`numpy.meshgrid`. + +*U*, *V*, *C* may be masked arrays, but masked *X*, *Y* are not +supported at present. + +Keyword arguments: + + *length*: + Length of the barb in points; the other parts of the barb + are scaled against this. + Default is 9 + + *pivot*: [ 'tip' | 'middle' ] + The part of the arrow that is at the grid point; the arrow + rotates about this point, hence the name *pivot*. + Default is 'tip' + + *barbcolor*: [ color | color sequence ] + Specifies the color all parts of the barb except any flags. + This parameter is analagous to the *edgecolor* parameter + for polygons, which can be used instead. However this parameter + will override facecolor. + + *flagcolor*: [ color | color sequence ] + Specifies the color of any flags on the barb. + This parameter is analagous to the *facecolor* parameter + for polygons, which can be used instead. However this parameter + will override facecolor. If this is not set (and *C* has not either) + then *flagcolor* will be set to match *barbcolor* so that the barb + has a uniform color. If *C* has been set, *flagcolor* has no effect. + + *sizes*: + A dictionary of coefficients specifying the ratio of a given feature + to the length of the barb. Only those values one wishes to override + need to be included. These features include: + 'spacing' - space between features (flags, full/half barbs) + 'height' - height (distance from shaft to top) of a flag or full barb + 'width' - width of a flag, twice the width of a full barb + 'emptybarb' - radius of the circle used for low magnitudes + + *fill_empty*: + A flag on whether the empty barbs (circles) that are drawn should be filled + with the flag color. If they are not filled, they will be drawn such that + no color is applied to the center. + Default is False + + *rounding*: + A flag to indicate whether the vector magnitude should be rounded when + allocating barb components. If True, the magnitude is rounded to the + nearest multiple of the half-barb increment. If False, the magnitude + is simply truncated to the next lowest multiple. + Default is True + + *barb_increments*: + A dictionary of increments specifying values to associate with different + parts of the barb. Only those values one wishes to override need to be + included. + 'half' - half barbs (Default is 5) + 'full' - full barbs (Default is 10) + 'flag' - flags (default is 50) + +Barbs are traditionally used in meteorology as a way to plot the speed +and direction of wind observations, but can technically be used to plot +any two dimensional vector quantity. As opposed to arrows, which give +vector magnitude by the length of the arrow, the barbs give more quantitative +information about the vector magnitude by putting slanted lines or a triangle +for various increments in magnitude, as show schematically below: + + /\ \ + / \ \ + / \ \ \ +/ \ \ \ +------------------------------ + +The largest increment is given by a triangle (or "flag"). After those come full +lines (barbs). The smallest increment is a half line. There is only, of +course, ever at most 1 half line. If the magnitude is small and only needs a +single half-line and no full lines or triangles, the half-line is offset from +the end of the barb so that it can be easily distinguished from barbs with a +single full line. The magnitude for the barb shown above would nominally be +65, using the standard increments of 50, 10, and 5. + +linewidths and edgecolors can be used to customize the barb. +Additional :class:`~matplotlib.collections.PolyCollection` +keyword arguments: + +%(PolyCollection)s +""" % martist.kwdocd + +class Barbs(collections.PolyCollection): + ''' + Specialized PolyCollection for barbs. + + The only API method is set_UVC(), which can be used + to change the size, orientation, and color of the + arrows. Locations are changed using the set_offsets() collection + method.Possibly this method will be useful in animations. + + There is one internal function _find_tails() which finds exactly + what should be put on the barb given the vector magnitude. From there + _make_barbs() is used to find the vertices of the polygon to represent the + barb based on this information. + ''' + #This may be an abuse of polygons here to render what is essentially maybe + #1 triangle and a series of lines. It works fine as far as I can tell + #however. + def __init__(self, ax, *args, **kw): + self._pivot = kw.pop('pivot', 'tip') + self._length = kw.pop('length', 7) + barbcolor = kw.pop('barbcolor', None) + flagcolor = kw.pop('flagcolor', None) + self.sizes = kw.pop('sizes', dict()) + self.fill_empty = kw.pop('fill_empty', False) + self.barb_increments = kw.pop('barb_increments', dict()) + self.rounding = kw.pop('rounding', True) + + #Flagcolor and and barbcolor provide convenience parameters for setting + #the facecolor and edgecolor, respectively, of the barb polygon. We + #also work here to make the flag the same color as the rest of the barb + #by default + if None in (barbcolor, flagcolor): + kw['edgecolors'] = 'face' + if flagcolor: + kw['facecolors'] = flagcolor + elif barbcolor: + kw['facecolors'] = barbcolor + else: + #Set to facecolor passed in or default to black + kw.setdefault('facecolors', 'k') + else: + kw['edgecolors'] = barbcolor + kw['facecolors'] = flagcolor + + #Parse out the data arrays from the various configurations supported + x, y, u, v, c = self._parse_args(*args) + self.x = x + self.y = y + xy = np.hstack((x[:,np.newaxis], y[:,np.newaxis])) + + #Make a collection + barb_size = self._length**2 / 4 #Empirically determined + collections.PolyCollection.__init__(self, [], (barb_size,), offsets=xy, + transOffset=ax.transData, **kw) + self.set_transform(transforms.IdentityTransform()) + + self.set_UVC(u, v, c) + + __init__.__doc__ = """ + The constructor takes one required argument, an Axes + instance, followed by the args and kwargs described + by the following pylab interface documentation: + %s""" % _barbs_doc + + def _find_tails(self, mag, rounding=True, half=5, full=10, flag=50): + '''Find how many of each of the tail pieces is necessary. Flag + specifies the increment for a flag, barb for a full barb, and half for + half a barb. Mag should be the magnitude of a vector (ie. >= 0). + + This returns a tuple of: + (number of flags, number of barbs, half_flag, empty_flag) + half_flag is a boolean whether half of a barb is needed, since there + should only ever be one half on a given barb. Empty flag is an array + of flags to easily tell if a barb is empty (too low to plot any + barbs/flags.''' + + #If rounding, round to the nearest multiple of half, the smallest + #increment + if rounding: + mag = half * (mag / half + 0.5).astype(np.int) + + num_flags = np.floor(mag / flag).astype(np.int) + mag = np.mod(mag, flag) + + num_barb = np.floor(mag / full).astype(np.int) + mag = np.mod(mag, full) + + half_flag = mag >= half + empty_flag = ~(half_flag | (num_flags > 0) | (num_barb > 0)) + + return num_flags, num_barb, half_flag, empty_flag + + def _make_barbs(self, u, v, nflags, nbarbs, half_barb, empty_flag, length, + pivot, sizes, fill_empty): + '''This function actually creates the wind barbs. u and v are + components of the vector in the x and y directions, respectively. + nflags, nbarbs, and half_barb, empty_flag are, respectively, the number + of flags, number of barbs, flag for half a barb, and flag for empty + barb, ostensibly obtained from _find_tails. length is the length of + the barb staff in points. pivot specifies the point on the barb around + which the entire barb should be rotated. Right now valid options are + 'head' and 'middle'. sizes is a dictionary of coefficients specifying + the ratio of a given feature to the length of the barb. These features + include: + + spacing - space between features (flags, full/half barbs) + height - height (distance from shaft of top) of a flag or full barb + width - width of a flag, twice the width of a full barb + emptybarb - radius of the circle used for low magnitudes + + This function returns list of arrays of vertices, defining a polygon for + each of the wind barbs. These polygons have been rotated to properly + align with the vector direction.''' + + #These control the spacing and size of barb elements relative to the + #length of the shaft + spacing = length * sizes.get('spacing', 0.125) + full_height = length * sizes.get('height', 0.4) + full_width = length * sizes.get('width', 0.25) + empty_rad = length * sizes.get('emptybarb', 0.15) + + #Controls y point where to pivot the barb. + pivot_points = dict(tip=0.0, middle=-length/2.) + + endx = 0.0 + endy = pivot_points[pivot.lower()] + + #Get the appropriate angle for the vector components. The offset is due + #to the way the barb is initially drawn, going down the y-axis. This + #makes sense in a meteorological mode of thinking since there 0 degrees + #corresponds to north (the y-axis traditionally) + angles = -(ma.arctan2(v, u) + np.pi/2) + + #Used for low magnitude. We just get the vertices, so if we make it + #out here, it can be reused. The center set here should put the + #center of the circle at the location(offset), rather than at the + #same point as the barb pivot; this seems more sensible. + circ = CirclePolygon((0,0), radius=empty_rad).get_verts() + if fill_empty: + empty_barb = circ + else: + #If we don't want the empty one filled, we make a degenerate polygon + #that wraps back over itself + empty_barb = np.concatenate((circ, circ[::-1])) + + barb_list = [] + for index, angle in np.ndenumerate(angles): + #If the vector magnitude is too weak to draw anything, plot an + #empty circle instead + if empty_flag[index]: + #We can skip the transform since the circle has no preferred + #orientation + barb_list.append(empty_barb) + continue + + poly_verts = [(endx, endy)] + offset = length + + #Add vertices for each flag + for i in range(nflags[index]): + #The spacing that works for the barbs is a little to much for + #the flags, but this only occurs when we have more than 1 flag. + if offset != length: offset += spacing / 2. + poly_verts.extend([[endx, endy + offset], + [endx + full_height, endy - full_width/2 + offset], + [endx, endy - full_width + offset]]) + + offset -= full_width + spacing + + #Add vertices for each barb. These really are lines, but works + #great adding 3 vertices that basically pull the polygon out and + #back down the line + for i in range(nbarbs[index]): + poly_verts.extend([(endx, endy + offset), + (endx + full_height, endy + offset + full_width/2), + (endx, endy + offset)]) + + offset -= spacing + + #Add the vertices for half a barb, if needed + if half_barb[index]: + #If the half barb is the first on the staff, traditionally it is + #offset from the end to make it easy to distinguish from a barb + #with a full one + if offset == length: + poly_verts.append((endx, endy + offset)) + offset -= 1.5 * spacing + poly_verts.extend([(endx, endy + offset), + (endx + full_height/2, endy + offset + full_width/4), + (endx, endy + offset)]) + + #Rotate the barb according the angle. Making the barb first and then + #rotating it made the math for drawing the barb really easy. Also, + #the transform framework makes doing the rotation simple. + poly_verts = transforms.Affine2D().rotate(-angle).transform( + poly_verts) + barb_list.append(poly_verts) + + return barb_list + + #Taken shamelessly from Quiver + def _parse_args(self, *args): + X, Y, U, V, C = [None]*5 + args = list(args) + if len(args) == 3 or len(args) == 5: + C = ma.asarray(args.pop(-1)).ravel() + V = ma.asarray(args.pop(-1)) + U = ma.asarray(args.pop(-1)) + nn = np.shape(U) + nc = nn[0] + nr = 1 + if len(nn) > 1: + nr = nn[1] + if len(args) == 2: # remaining after removing U,V,C + X, Y = [np.array(a).ravel() for a in args] + if len(X) == nc and len(Y) == nr: + X, Y = [a.ravel() for a in np.meshgrid(X, Y)] + else: + indexgrid = np.meshgrid(np.arange(nc), np.arange(nr)) + X, Y = [np.ravel(a) for a in indexgrid] + return X, Y, U, V, C + + def set_UVC(self, U, V, C=None): + self.u = ma.asarray(U).ravel() + self.v = ma.asarray(V).ravel() + if C is not None: + c = ma.asarray(C).ravel() + x,y,u,v,c = delete_masked_points(self.x.ravel(), self.y.ravel(), + self.u, self.v, c) + else: + x,y,u,v = delete_masked_points(self.x.ravel(), self.y.ravel(), + self.u, self.v) + + magnitude = np.sqrt(u*u + v*v) + flags, barbs, halves, empty = self._find_tails(magnitude, + self.rounding, **self.barb_increments) + + #Get the vertices for each of the barbs + + plot_barbs = self._make_barbs(u, v, flags, barbs, halves, empty, + self._length, self._pivot, self.sizes, self.fill_empty) + self.set_verts(plot_barbs) + + #Set the color array + if C is not None: + self.set_array(c) + + #Update the offsets in case the masked data changed + xy = np.hstack((x[:,np.newaxis], y[:,np.newaxis])) + self._offsets = xy + + def set_offsets(self, xy): + ''' + Set the offsets for the barb polygons. This saves the offets passed in + and actually sets version masked as appropriate for the existing U/V + data. *offsets* should be a sequence. + + ACCEPTS: sequence of pairs of floats + ''' + self.x = xy[:,0] + self.y = xy[:,1] + x,y,u,v = delete_masked_points(self.x.ravel(), self.y.ravel(), self.u, + self.v) + xy = np.hstack((x[:,np.newaxis], y[:,np.newaxis])) + collections.PolyCollection.set_offsets(self, xy) + set_offsets.__doc__ = collections.PolyCollection.set_offsets.__doc__ + + barbs_doc = _barbs_doc + This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <dmk...@us...> - 2008-07-24 08:50:47
|
Revision: 5830 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5830&view=rev Author: dmkaplan Date: 2008-07-24 08:50:43 +0000 (Thu, 24 Jul 2008) Log Message: ----------- Committing rewrite of clabel inlining and label rotation code. Also, renaming ContourLabeler attributes to something like .labelAttribute. A few of the old attribute names have been maintained (in addition to new versions) for backward compatibility, but these should be remove in +1 releases. Added appropriate comments to CHANGELOG and API_CHANGES. Modified Paths: -------------- trunk/matplotlib/API_CHANGES trunk/matplotlib/CHANGELOG trunk/matplotlib/lib/matplotlib/blocking_input.py trunk/matplotlib/lib/matplotlib/contour.py Modified: trunk/matplotlib/API_CHANGES =================================================================== --- trunk/matplotlib/API_CHANGES 2008-07-24 08:06:45 UTC (rev 5829) +++ trunk/matplotlib/API_CHANGES 2008-07-24 08:50:43 UTC (rev 5830) @@ -8,6 +8,12 @@ Axes.patch, Axes.axesFrame renamed Axes.frame, Axes.get_frame, which returns Axes.patch, is deprecated. Examples and users guide updated +* Changes in the ContourLabeler attributes (clabel function) so that they + all have a form like .labelAttribute. The three attributes that are most + likely to be used by end users, .cl, .cl_xy and .cl_cvalues have been + maintained for the moment (in addition to their renamed versions), but they + are depricated and will eventually be removed. + Changes for 0.98.1 ================== Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-24 08:06:45 UTC (rev 5829) +++ trunk/matplotlib/CHANGELOG 2008-07-24 08:50:43 UTC (rev 5830) @@ -1,3 +1,6 @@ +2008-07-24 Rewrite of a significant portion of the clabel code (class + ContourLabeler) to improve inlining. - DMK + 2008-07-22 Added Barbs polygon collection (similar to Quiver) for plotting wind barbs. Added corresponding helpers to Axes and pyplot as well. (examples/pylab_examples/barb_demo.py shows it off.) - RMM @@ -4,7 +7,7 @@ 2008-07-21 Added scikits.delaunay as matplotlib.delaunay. Added griddata function in matplotlib.mlab, with example (griddata_demo.py) in - pylab_examples. griddata function will use mpl_toolkits._natgrid + pylab_examples. griddata function will use mpl_toolkits._natgrid if installed (haven't yet created the toolkit). - JSW 2008-07-21 Re-introduced offset_copy that works in the context of the Modified: trunk/matplotlib/lib/matplotlib/blocking_input.py =================================================================== --- trunk/matplotlib/lib/matplotlib/blocking_input.py 2008-07-24 08:06:45 UTC (rev 5829) +++ trunk/matplotlib/lib/matplotlib/blocking_input.py 2008-07-24 08:50:43 UTC (rev 5830) @@ -19,8 +19,9 @@ import time import numpy as np + from matplotlib import path, verbose -from cbook import is_sequence_of_strings +from matplotlib.cbook import is_sequence_of_strings class BlockingInput(object): """ @@ -267,47 +268,39 @@ if event.inaxes == cs.ax: conmin,segmin,imin,xmin,ymin = cs.find_nearest_contour( - event.x, event.y, cs.label_indices)[:5] + event.x, event.y, cs.labelIndiceList)[:5] # Get index of nearest level in subset of levels used for labeling - lmin = cs.label_indices.index(conmin) + lmin = cs.labelIndiceList.index(conmin) + # Coordinates of contour paths = cs.collections[conmin].get_paths() lc = paths[segmin].vertices - # Figure out label rotation. This is very cludgy. - # Ideally, there would be one method in ContourLabeler - # that would figure out the best rotation for a label at a - # point, but the way automatic label rotation is done is - # quite mysterious to me and doesn't seem easy to - # generalize to non-automatic label placement. The method - # used below is not very robust! It basically looks one - # point before and one point after label location on - # contour and takes mean of angles of two vectors formed. - # This produces "acceptable" results, but not nearly as - # nice as automatic method. - ll = lc[max(0,imin-1):imin+2] # Get points around point - dd = np.diff(ll,axis=0) - rotation = np.mean( np.arctan2(dd[:,1], dd[:,0]) ) * 180 / np.pi - if rotation > 90: - rotation = rotation -180 - if rotation < -90: - rotation = 180 + rotation + # In pixel/screen space + slc = cs.ax.transData.transform(lc) - cs.add_label(xmin,ymin,rotation,cs.label_levels[lmin], - cs.label_cvalues[lmin]) + # Get label width for rotating labels and breaking contours + lw = cs.get_label_width(cs.labelLevelList[lmin], + cs.labelFmt, cs.labelFontSizeList[lmin]) + # Figure out label rotation. + rotation,nlc = cs.calc_label_rot_and_inline( + slc, imin, lw, lc if self.inline else [], + self.inline_spacing ) + + cs.add_label(xmin,ymin,rotation,cs.labelLevelList[lmin], + cs.labelCValueList[lmin]) + if self.inline: - # Get label width for breaking contours - lw = cs.get_label_width(cs.label_levels[lmin], - cs.fmt, cs.fslist[lmin]) - # Break contour - new=cs.break_linecontour(lc,rotation,lw,imin) - if len(new[0]): - paths[segmin] = path.Path(new[0]) - if len(new[1]): - paths.extend([path.Path(new[1])]) + # Remove old, not looping over paths so we can do this up front + paths.pop(segmin) + # Add paths if not empty or single point + for n in nlc: + if len(n)>1: + paths.append( path.Path(n) ) + self.fig.canvas.draw() else: # Remove event if not valid BlockingInput.pop(self) @@ -320,14 +313,21 @@ broken contour - once humpty-dumpty is broken, he can't be put back together. In inline mode, this does nothing. """ + # Remove this last event - not too important for clabel use + # since clabel normally doesn't have a maximum number of + # events, but best for cleanliness sake. + BlockingInput.pop(self) + if self.inline: pass else: self.cs.pop_label() self.cs.ax.figure.canvas.draw() - def __call__(self,inline,n=-1,timeout=-1): + def __call__(self,inline,inline_spacing=5,n=-1,timeout=-1): self.inline=inline + self.inline_spacing=inline_spacing + BlockingMouseInput.__call__(self,n=n,timeout=timeout, show_clicks=False) Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2008-07-24 08:06:45 UTC (rev 5829) +++ trunk/matplotlib/lib/matplotlib/contour.py 2008-07-24 08:50:43 UTC (rev 5830) @@ -69,6 +69,12 @@ controls whether the underlying contour is removed or not. Default is *True*. + *inline_spacing*: + space in pixels to leave on each side of label when + placing inline. Defaults to 5. This spacing will be + exact for labels at locations where the contour is + straight, less so for labels on curved contours. + *fmt*: a format string for the label. Default is '%1.3f' Alternatively, this can be a dictionary matching contour @@ -108,11 +114,12 @@ fontsize = kwargs.get('fontsize', None) inline = kwargs.get('inline', 1) - self.fmt = kwargs.get('fmt', '%1.3f') + inline_spacing = kwargs.get('inline_spacing', 5) + self.labelFmt = kwargs.get('fmt', '%1.3f') _colors = kwargs.get('colors', None) # Detect if manual selection is desired and remove from argument list - self.manual_select=kwargs.get('manual',False) + self.labelManual=kwargs.get('manual',False) if len(args) == 0: levels = self.levels @@ -131,52 +138,59 @@ raise ValueError(msg) else: raise TypeError("Illegal arguments to clabel, see help(clabel)") - self.label_levels = levels - self.label_indices = indices + self.labelLevelList = levels + self.labelIndiceList = indices - self.fp = font_manager.FontProperties() + self.labelFontProps = font_manager.FontProperties() if fontsize == None: - font_size = int(self.fp.get_size_in_points()) + font_size = int(self.labelFontProps.get_size_in_points()) else: if type(fontsize) not in [int, float, str]: raise TypeError("Font size must be an integer number.") # Can't it be floating point, as indicated in line above? else: if type(fontsize) == str: - font_size = int(self.fp.get_size_in_points()) + font_size = int(self.labelFontProps.get_size_in_points()) else: - self.fp.set_size(fontsize) + self.labelFontProps.set_size(fontsize) font_size = fontsize - self.fslist = [font_size] * len(levels) + self.labelFontSizeList = [font_size] * len(levels) if _colors == None: - self.label_mappable = self - self.label_cvalues = np.take(self.cvalues, self.label_indices) + self.labelMappable = self + self.labelCValueList = np.take(self.cvalues, self.labelIndiceList) else: - cmap = colors.ListedColormap(_colors, N=len(self.label_levels)) - self.label_cvalues = range(len(self.label_levels)) - self.label_mappable = cm.ScalarMappable(cmap = cmap, - norm = colors.NoNorm()) + cmap = colors.ListedColormap(_colors, N=len(self.labelLevelList)) + self.labelCValueList = range(len(self.labelLevelList)) + self.labelMappable = cm.ScalarMappable(cmap = cmap, + norm = colors.NoNorm()) - #self.cl = [] # Initialized in ContourSet.__init__ - #self.cl_cvalues = [] # same - self.cl_xy = [] + #self.labelTexts = [] # Initialized in ContourSet.__init__ + #self.labelCValues = [] # same + self.labelXYs = [] - if self.manual_select: + if self.labelManual: print 'Select label locations manually using first mouse button.' print 'End manual selection with second mouse button.' if not inline: print 'Remove last label by clicking third mouse button.' blocking_contour_labeler = BlockingContourLabeler(self) - blocking_contour_labeler(inline) + blocking_contour_labeler(inline,inline_spacing) else: - self.labels(inline) + self.labels(inline,inline_spacing) - self.label_list = cbook.silent_list('text.Text', self.cl) - return self.label_list + # Hold on to some old attribute names. These are depricated and will + # be moved in the near future (sometime after 2008-08-01), but keeping + # for now for backwards compatibility + self.cl = self.labelTexts + self.cl_xy = self.labelXYs + self.cl_cvalues = self.labelCValues + self.labelTextsList = cbook.silent_list('text.Text', self.labelTexts) + return self.labelTextsList + def print_label(self, linecontour,labelwidth): "if contours are too short, don't plot a label" lcsize = len(linecontour) @@ -196,9 +210,9 @@ def too_close(self, x,y, lw): "if there's a label already nearby, find a better place" - if self.cl_xy != []: + if self.labelXYs != []: dist = [np.sqrt((x-loc[0]) ** 2 + (y-loc[1]) ** 2) - for loc in self.cl_xy] + for loc in self.labelXYs] for d in dist: if d < 1.2*lw: return 1 @@ -237,12 +251,35 @@ return lw + def get_real_label_width( self, lev, fmt, fsize ): + """ + This computes actual onscreen label width. + This uses some black magic to determine onscreen extent of non-drawn + label. This magic may not be very robust. + """ + # Find middle of axes + xx = np.mean( np.asarray(self.ax.axis()).reshape(2,2), axis=1 ) + # Temporarily create text object + t = text.Text( xx[0], xx[1] ) + self.set_label_props( t, self.get_text(lev,fmt), 'k' ) + + # Some black magic to get onscreen extent + # NOTE: This will only work for already drawn figures, as the canvas + # does not have a renderer otherwise. This is the reason this function + # can't be integrated into the rest of the code. + bbox = t.get_window_extent(renderer=self.ax.figure.canvas.renderer) + + # difference in pixel extent of image + lw = np.diff(bbox.corners()[0::2,0])[0] + + return lw + def set_label_props(self, label, text, color): "set the label properties - color, fontsize, text" label.set_text(text) label.set_color(color) - label.set_fontproperties(self.fp) + label.set_fontproperties(self.labelFontProps) label.set_clip_box(self.ax.bbox) def get_text(self, lev, fmt): @@ -255,85 +292,6 @@ else: return fmt%lev - def break_linecontour(self, linecontour, rot, labelwidth, ind): - "break a contour in two contours at the location of the label" - lcsize = len(linecontour) - hlw = int(labelwidth/2) - - #length of label in screen coords - ylabel = abs(hlw * np.sin(rot*np.pi/180)) - xlabel = abs(hlw * np.cos(rot*np.pi/180)) - - trans = self.ax.transData - - slc = trans.transform(linecontour) - x,y = slc[ind] - xx=slc[:,0].copy() - yy=slc[:,1].copy() - - #indices which are under the label - inds, = np.nonzero(((xx < x+xlabel) & (xx > x-xlabel)) & - ((yy < y+ylabel) & (yy > y-ylabel))) - - if len(inds) >0: - #if the label happens to be over the beginning of the - #contour, the entire contour is removed, i.e. - #indices to be removed are - #inds= [0,1,2,3,305,306,307] - #should rewrite this in a better way - linds, = np.nonzero(inds[1:]- inds[:-1] != 1) - if inds[0] == 0 and len(linds) != 0: - ii = inds[linds[0]] - lc1 =linecontour[ii+1:inds[ii+1]] - lc2 = [] - - else: - lc1=linecontour[:inds[0]] - lc2= linecontour[inds[-1]+1:] - - else: - lc1=linecontour[:ind] - lc2 = linecontour[ind+1:] - - - if rot <0: - new_x1, new_y1 = x-xlabel, y+ylabel - new_x2, new_y2 = x+xlabel, y-ylabel - else: - new_x1, new_y1 = x-xlabel, y-ylabel - new_x2, new_y2 = x+xlabel, y+ylabel - - inverse = trans.inverted() - new_x1d, new_y1d = inverse.transform_point((new_x1, new_y1)) - new_x2d, new_y2d = inverse.transform_point((new_x2, new_y2)) - new_xy1 = np.array(((new_x1d, new_y1d),)) - new_xy2 = np.array(((new_x2d, new_y2d),)) - - - if rot > 0: - if (len(lc1) > 0 and (lc1[-1][0] <= new_x1d) - and (lc1[-1][1] <= new_y1d)): - lc1 = np.concatenate((lc1, new_xy1)) - #lc1.append((new_x1d, new_y1d)) - - if (len(lc2) > 0 and (lc2[0][0] >= new_x2d) - and (lc2[0][1] >= new_y2d)): - lc2 = np.concatenate((new_xy2, lc2)) - #lc2.insert(0, (new_x2d, new_y2d)) - else: - if (len(lc1) > 0 and ((lc1[-1][0] <= new_x1d) - and (lc1[-1][1] >= new_y1d))): - lc1 = np.concatenate((lc1, new_xy1)) - #lc1.append((new_x1d, new_y1d)) - - if (len(lc2) > 0 and ((lc2[0][0] >= new_x2d) - and (lc2[0][1] <= new_y2d))): - lc2 = np.concatenate((new_xy2, lc2)) - #lc2.insert(0, (new_x2d, new_y2d)) - - return [lc1,lc2] - - def locate_label(self, linecontour, labelwidth): """find a good place to plot a label (relatively flat part of the contour) and the angle of rotation for the @@ -362,12 +320,6 @@ dist = np.add.reduce(([(abs(s)[i]/L[i]) for i in range(xsize)]),-1) x,y,ind = self.get_label_coords(dist, XX, YY, ysize, labelwidth) #print 'ind, x, y', ind, x, y - angle = np.arctan2(ylast - yfirst, xlast - xfirst).ravel() - rotation = angle[ind]*180/np.pi - if rotation > 90: - rotation = rotation -180 - if rotation < -90: - rotation = 180 + rotation # There must be a more efficient way... lc = [tuple(l) for l in linecontour] @@ -375,72 +327,181 @@ #print 'dind', dind #dind = list(linecontour).index((x,y)) - return x,y, rotation, dind + return x, y, dind + def calc_label_rot_and_inline( self, slc, ind, lw, lc=[], spacing=5 ): + """ + This function calculates the appropriate label rotation given + the linecontour coordinates in screen units, the index of the + label location and the label width. + + It will also break contour and calculate inlining if *lc* is + not empty. *spacing* is the space around the label in pixels + to leave empty. + + Do both of these tasks at once to avoid calling cbook.path_length + multiple times, which is relatively costly. + + The method used here involves calculating the path length + along the contour in pixel coordinates and then looking + approximately label width / 2 away from central point to + determine rotation and then to break contour if desired. + """ + + # Half the label width + hlw = lw/2.0 + + # Check if closed and, if so, rotate contour so label is at edge + closed = cbook.is_closed_polygon(slc) + if closed: + slc = np.r_[ slc[ind:-1], slc[:ind+1] ] + + if len(lc): # Rotate lc also if not empty + lc = np.r_[ lc[ind:-1], lc[:ind+1] ] + + ind = 0 + + # Path length in pixel space + pl = cbook.path_length(slc) + pl = pl-pl[ind] + + # Use linear interpolation to get points around label + xi = np.array( [ -hlw, hlw ] ) + if closed: # Look at end also for closed contours + dp = np.array([pl[-1],0]) + else: + dp = np.zeros_like(xi) + + ll = cbook.less_simple_linear_interpolation( pl, slc, dp+xi, + extrap=True ) + + # get vector in pixel space coordinates from one point to other + dd = np.diff( ll, axis=0 ).ravel() + + # Get angle of vector - must be calculated in pixel space for + # text rotation to work correctly + if np.all(dd==0): # Must deal with case of zero length label + rotation = 0.0 + else: + rotation = np.arctan2(dd[1], dd[0]) * 180.0 / np.pi + + # Fix angle so text is never upside-down + if rotation > 90: + rotation = rotation - 180.0 + if rotation < -90: + rotation = 180.0 + rotation + + # Break contour if desired + nlc = [] + if len(lc): + # Expand range by spacing + xi = dp + xi + np.array([-spacing,spacing]) + + # Get indices near points of interest + I = cbook.less_simple_linear_interpolation( + pl, np.arange(len(pl)), xi, extrap=False ) + + # If those indices aren't beyond contour edge, find x,y + if (not np.isnan(I[0])) and int(I[0])<>I[0]: + xy1 = cbook.less_simple_linear_interpolation( + pl, lc, [ xi[0] ] ) + + if (not np.isnan(I[1])) and int(I[1])<>I[1]: + xy2 = cbook.less_simple_linear_interpolation( + pl, lc, [ xi[1] ] ) + + # Make integer + I = [ np.floor(I[0]), np.ceil(I[1]) ] + + # Actually break contours + if closed: + # This will remove contour if shorter than label + if np.all(~np.isnan(I)): + nlc.append( np.r_[ xy2, lc[I[1]:I[0]+1], xy1 ] ) + else: + # These will remove pieces of contour if they have length zero + if not np.isnan(I[0]): + nlc.append( np.r_[ lc[:I[0]+1], xy1 ] ) + if not np.isnan(I[1]): + nlc.append( np.r_[ xy2, lc[I[1]:] ] ) + + # The current implementation removes contours completely + # covered by labels. Uncomment line below to keep + # original contour if this is the preferred behavoir. + #if not len(nlc): nlc = [ lc ] + + return (rotation,nlc) + + def add_label(self,x,y,rotation,lev,cvalue): dx,dy = self.ax.transData.inverted().transform_point((x,y)) t = text.Text(dx, dy, rotation = rotation, horizontalalignment='center', verticalalignment='center') - color = self.label_mappable.to_rgba(cvalue,alpha=self.alpha) + color = self.labelMappable.to_rgba(cvalue,alpha=self.alpha) - _text = self.get_text(lev,self.fmt) + _text = self.get_text(lev,self.labelFmt) self.set_label_props(t, _text, color) - self.cl.append(t) - self.cl_cvalues.append(cvalue) - self.cl_xy.append((x,y)) + self.labelTexts.append(t) + self.labelCValues.append(cvalue) + self.labelXYs.append((x,y)) # Add label to plot here - useful for manual mode label selection self.ax.add_artist(t) def pop_label(self,index=-1): '''Defaults to removing last label, but any index can be supplied''' - self.cl_cvalues.pop(index) - t = self.cl.pop(index) + self.labelCValues.pop(index) + t = self.labelTexts.pop(index) t.remove() - def labels(self, inline): + def labels(self, inline, inline_spacing): trans = self.ax.transData # A bit of shorthand for icon, lev, fsize, cvalue in zip( - self.label_indices, self.label_levels, self.fslist, - self.label_cvalues ): + self.labelIndiceList, self.labelLevelList, self.labelFontSizeList, + self.labelCValueList ): con = self.collections[icon] - lw = self.get_label_width(lev, self.fmt, fsize) + lw = self.get_label_width(lev, self.labelFmt, fsize) additions = [] paths = con.get_paths() for segNum, linepath in enumerate(paths): - linecontour = linepath.vertices - # for closed contours add one more point to - # avoid division by zero - if np.all(linecontour[0] == linecontour[-1]): - linecontour = np.concatenate((linecontour, - linecontour[1][np.newaxis,:])) - #linecontour.append(linecontour[1]) - # transfer all data points to screen coordinates - slc = trans.transform(linecontour) + lc = linepath.vertices # Line contour + slc0 = trans.transform(lc) # Line contour in screen coords + + # For closed polygons, add extra point to avoid division by + # zero in print_label and locate_label. Other than these + # functions, this is not necessary and should probably be + # eventually removed. + if cbook.is_closed_polygon( lc ): + slc = np.r_[ slc0, slc0[1:2,:] ] + else: + slc = slc0 + if self.print_label(slc,lw): - x,y, rotation, ind = self.locate_label(slc, lw) + x,y,ind = self.locate_label(slc, lw) + rotation,new=self.calc_label_rot_and_inline( + slc0, ind, lw, lc if inline else [], + inline_spacing ) + # Actually add the label self.add_label(x,y,rotation,lev,cvalue) - # Use break_linecontour to split contours for inlining + # If inline, add new contours if inline: - new = self.break_linecontour(linecontour, rotation, - lw, ind) - if len(new[0]): - paths[segNum] = path.Path(new[0]) - if len(new[1]): - additions.append(path.Path(new[1])) + for n in new: + # Add path if not empty or single point + if len(n)>1: additions.append( path.Path(n) ) - # After looping over all segments on a contour, append - # new paths to existing - paths.extend(additions) + # After looping over all segments on a contour, remove old + # paths and add new ones if inlining + if inline: + del paths[:] + paths.extend(additions) - class ContourSet(cm.ScalarMappable, ContourLabeler): """ Create and store a set of contour lines or filled regions. @@ -512,8 +573,8 @@ else: self.collections = cbook.silent_list('collections.LineCollection') # label lists must be initialized here - self.cl = [] - self.cl_cvalues = [] + self.labelTexts = [] + self.labelCValues = [] kw = {'cmap': cmap} if norm is not None: @@ -574,9 +635,9 @@ for color, collection in zip(tcolors, self.collections): collection.set_alpha(self.alpha) collection.set_color(color) - for label, cv in zip(self.cl, self.cl_cvalues): + for label, cv in zip(self.labelTexts, self.labelCValues): label.set_alpha(self.alpha) - label.set_color(self.label_mappable.to_rgba(cv)) + label.set_color(self.labelMappable.to_rgba(cv)) # add label colors cm.ScalarMappable.changed(self) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-24 13:30:56
|
Revision: 5836 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5836&view=rev Author: jdh2358 Date: 2008-07-24 13:30:53 +0000 (Thu, 24 Jul 2008) Log Message: ----------- Merged revisions 5834 via svnmerge from https://matplotlib.svn.sourceforge.net/svnroot/matplotlib/branches/v0_91_maint ........ r5834 | jdh2358 | 2008-07-24 08:15:23 -0500 (Thu, 24 Jul 2008) | 1 line deprecated mlab2 functions from mlab ........ Modified Paths: -------------- trunk/matplotlib/lib/matplotlib/blocking_input.py trunk/matplotlib/lib/matplotlib/contour.py trunk/matplotlib/lib/matplotlib/mlab.py Property Changed: ---------------- trunk/matplotlib/ Property changes on: trunk/matplotlib ___________________________________________________________________ Modified: svnmerge-integrated - /branches/v0_91_maint:1-5833 + /branches/v0_91_maint:1-5835 Modified: trunk/matplotlib/lib/matplotlib/blocking_input.py =================================================================== --- trunk/matplotlib/lib/matplotlib/blocking_input.py 2008-07-24 13:21:17 UTC (rev 5835) +++ trunk/matplotlib/lib/matplotlib/blocking_input.py 2008-07-24 13:30:53 UTC (rev 5836) @@ -274,11 +274,22 @@ lw = cs.get_label_width(cs.labelLevelList[lmin], cs.labelFmt, cs.labelFontSizeList[lmin]) + """ + # requires python 2.5 # Figure out label rotation. rotation,nlc = cs.calc_label_rot_and_inline( slc, imin, lw, lc if self.inline else [], self.inline_spacing ) + """ + # Figure out label rotation. + if self.inline: lcarg = lc + else: lcarg = [] + rotation,nlc = cs.calc_label_rot_and_inline( + slc, imin, lw, lcarg, + self.inline_spacing ) + + cs.add_label(xmin,ymin,rotation,cs.labelLevelList[lmin], cs.labelCValueList[lmin]) Modified: trunk/matplotlib/lib/matplotlib/contour.py =================================================================== --- trunk/matplotlib/lib/matplotlib/contour.py 2008-07-24 13:21:17 UTC (rev 5835) +++ trunk/matplotlib/lib/matplotlib/contour.py 2008-07-24 13:30:53 UTC (rev 5836) @@ -329,15 +329,15 @@ return x, y, dind - def calc_label_rot_and_inline( self, slc, ind, lw, lc=[], spacing=5 ): + def calc_label_rot_and_inline( self, slc, ind, lw, lc=None, spacing=5 ): """ This function calculates the appropriate label rotation given the linecontour coordinates in screen units, the index of the label location and the label width. It will also break contour and calculate inlining if *lc* is - not empty. *spacing* is the space around the label in pixels - to leave empty. + not empty (lc defaults to the empty list if None). *spacing* + is the space around the label in pixels to leave empty. Do both of these tasks at once to avoid calling cbook.path_length multiple times, which is relatively costly. @@ -348,6 +348,7 @@ determine rotation and then to break contour if desired. """ + if lc is None: lc = [] # Half the label width hlw = lw/2.0 @@ -483,8 +484,10 @@ if self.print_label(slc,lw): x,y,ind = self.locate_label(slc, lw) + if inline: lcarg = lc + else: lcarg = [] rotation,new=self.calc_label_rot_and_inline( - slc0, ind, lw, lc if inline else [], + slc0, ind, lw, lcarg, inline_spacing ) # Actually add the label Modified: trunk/matplotlib/lib/matplotlib/mlab.py =================================================================== --- trunk/matplotlib/lib/matplotlib/mlab.py 2008-07-24 13:21:17 UTC (rev 5835) +++ trunk/matplotlib/lib/matplotlib/mlab.py 2008-07-24 13:30:53 UTC (rev 5836) @@ -1771,181 +1771,51 @@ ### end fperez numutils code -### begin mlab2 functions -# From MLab2: http://pdilib.sourceforge.net/MLab2.py -readme = \ - """ -MLab2.py, release 1 -Created on February 2003 by Thomas Wendler as part of the Emotionis Project. -This script is supposed to implement Matlab functions that were left out in -numerix.mlab.py (part of Numeric Python). -For further information on the Emotionis Project or on this script, please -contact their authors: -Rodrigo Benenson, rodrigob at elo dot utfsm dot cl -Thomas Wendler, thomasw at elo dot utfsm dot cl -Look at: http://pdilib.sf.net for new releases. -""" -## mlab2 functions numpified and checked 2007/08/04 -_eps_approx = 1e-13 - -#from numpy import fix -def fix(x): - """ - Rounds towards zero. - x_rounded = fix(x) rounds the elements of x to the nearest integers - towards zero. - For negative numbers is equivalent to ceil and for positive to floor. - """ - warnings.warn("Use numpy.fix()", DeprecationWarning) - return np.fix(x) - def rem(x,y): """ - Remainder after division. - rem(x,y) is equivalent to x - y.*fix(x./y) in case y is not zero. - By convention (but contrary to numpy), rem(x,0) returns None. - This also differs from numpy.remainder, which uses floor instead of - fix. + Deprecated - see numpy.remainder """ - x,y = np.asarray(x), np.asarray(y) - if np.any(y == 0): - return None - return x - y * np.fix(x/y) + raise NotImplementedError('Deprecated - see numpy.remainder') - def norm(x,y=2): """ - This function is deprecated - use numpy.linalg.norm instead. - - Norm of a matrix or a vector. Functions similar to the Matlab (TM) - function of the same name. - - Call signature:: - - norm(x,y=2) - - This function behaves differently for vectors and matrices. For vectors, - it returns the y'th norm of x (i.e. (sum(abs(x)**y))**(1.0/y). - - For matrices, if y=2, then it returns the largest singular value - of X, namely max(linalg.svd(x)). If y=1, returns the largest - column sum of x (i.e., max(sum(abs(x),axis=0)) ). If y=inf, - returns the largest row sum. If y='fro', returns the Frobenius - norm, sqrt(sum(diag(dot(x.transpose(),x)))). + Deprecated - see numpy.linalg.norm """ - warnings.warn( "Use numpy.linalg.norm instead", DeprecationWarning ) + raise NotImplementedError('Deprecated - see numpy.linalg.norm') - x = np.asarray(x) - if x.ndim == 2: - if y==2: - return np.max(np.linalg.svd(x)[1]) - elif y==1: - return np.max(np.sum(np.absolute((x)), axis=0)) - elif y=='inf': - return np.max(np.sum(np.absolute((np.transpose(x))), axis=0)) - elif y=='fro': - xx = np.dot(x.transpose(), x) - return np.sqrt(np.sum(np.diag(xx), axis=0)) - else: - raise ValueError('Second argument not permitted for matrices') - else: - xa = np.absolute(x) - if y == 'inf': - return np.max(xa) - elif y == '-inf': - return np.min(xa) - else: - return np.power(np.sum(np.power(xa,y)),1/float(y)) - - def orth(A): """ - Orthogonalization procedure similar to Matlab (TM) function of the same - name. - - Call signature:: - - Q = orth(A) - - Returns an orthonormal basis with the range of A. Q is an orthonormal - matrix (i.e., dot( Q.transpose(), Q ) is an identity matrix) and the - columns of Q span the same space as the columns of A. + Deprecated - needs clean room implementation """ + raise NotImplementedError('Deprecated - needs clean room implementation') - A = np.asarray(A) - U,S,V = np.linalg.svd(A) - - m,n = A.shape - if m > 1: - s = S - elif m == 1: - s = S[0] - else: - s = 0 - - tol = max(m,n) * np.max(s) * _eps_approx - r = np.sum(s > tol) - Q = np.take(U,range(r),1) - - return Q - def rank(x): """ - Returns the rank of a matrix. - The rank is understood here as the an estimation of the number of - linearly independent rows or columns (depending on the size of the - matrix). - Note that numerix.mlab.rank() is not equivalent to Matlab's rank. - This function is! + Deprecated - see numpy.rank """ - x = np.asarray(x) - s = np.linalg.svd(x, compute_uv=False) - maxabs = np.max(np.absolute(s)) - maxdim = max(x.shape) - tol = maxabs * maxdim * _eps_approx - return np.sum(s > tol) + raise NotImplementedError('Deprecated - see numpy.rank') def sqrtm(x): """ - Returns the square root of a square matrix. - This means that s=sqrtm(x) implies dot(s,s) = x. - Note that s and x are matrices. + Deprecated - needs clean room implementation """ - return mfuncC(np.sqrt, x) + raise NotImplementedError('Deprecated - needs clean room implementation') def mfuncC(f, x): """ - mfuncC(f, x) : matrix function with possibly complex eigenvalues. - Note: Numeric defines (v,u) = eig(x) => x*u.T = u.T * Diag(v) - This function is needed by sqrtm and allows further functions. + Deprecated """ + raise NotImplementedError('Deprecated - needs clean room implementation') - x = np.asarray(x) - (v,uT) = np.linalg.eig(x) - V = np.diag(f(v+0j)) - # todo: warning: this is not exactly what matlab does - # MATLAB "B/A is roughly the same as B*inv(A)" - y = np.dot(uT, np.dot(V, np.linalg.inv(uT))) - return approx_real(y) - def approx_real(x): - """ - approx_real(x) : returns x.real if |x.imag| < |x.real| * _eps_approx. - This function is needed by sqrtm and allows further functions. + Deprecated - needs clean room implementation """ - ai = np.absolute(x.imag) - ar = np.absolute(x.real) - if np.max(ai) <= np.max(ar) * _eps_approx: - return x.real - else: - return x + raise NotImplementedError('Deprecated - needs clean room implementation') -### end mlab2 functions - #helpers for loading, saving, manipulating and viewing numpy record arrays def safe_isnan(x): This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pki...@us...> - 2008-07-24 14:54:24
|
Revision: 5839 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5839&view=rev Author: pkienzle Date: 2008-07-24 14:54:22 +0000 (Thu, 24 Jul 2008) Log Message: ----------- Fix wx start/stop event loop Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/ginput_demo.py trunk/matplotlib/lib/matplotlib/backends/backend_wx.py trunk/matplotlib/lib/matplotlib/backends/backend_wxagg.py Modified: trunk/matplotlib/examples/pylab_examples/ginput_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/ginput_demo.py 2008-07-24 14:22:20 UTC (rev 5838) +++ trunk/matplotlib/examples/pylab_examples/ginput_demo.py 2008-07-24 14:54:22 UTC (rev 5839) @@ -2,5 +2,6 @@ t = arange(10) plot(t, sin(t)) print "Please click" -x = ginput(3, verbose=True) +x = ginput(3) +print "clicked",x show() Modified: trunk/matplotlib/lib/matplotlib/backends/backend_wx.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_wx.py 2008-07-24 14:22:20 UTC (rev 5838) +++ trunk/matplotlib/lib/matplotlib/backends/backend_wx.py 2008-07-24 14:54:22 UTC (rev 5839) @@ -732,6 +732,8 @@ wx.EVT_IDLE(self, self._onIdle) + self._event_loop = wx.EventLoop() + self.macros = {} # dict from wx id to seq of macros self.Printer_Init() @@ -907,6 +909,44 @@ def flush_events(self): wx.Yield() + def start_event_loop(self, timeout=0): + """ + Start an event loop. This is used to start a blocking event + loop so that interactive functions, such as ginput and + waitforbuttonpress, can wait for events. This should not be + confused with the main GUI event loop, which is always running + and has nothing to do with this. + + Call signature:: + + start_event_loop(self,timeout=0) + + This call blocks until a callback function triggers + stop_event_loop() or *timeout* is reached. If *timeout* is + <=0, never timeout. + """ + id = wx.NewId() + timer = wx.Timer(self, id=id) + if timeout > 0: + timer.Start(timeout*1000, oneShot=True) + bind(self, wx.EVT_TIMER, self.stop_event_loop, id=id) + self._event_loop.Run() + timer.Stop() + + def stop_event_loop(self, event=None): + """ + Stop an event loop. This is used to stop a blocking event + loop so that interactive functions, such as ginput and + waitforbuttonpress, can wait for events. + + Call signature:: + + stop_event_loop_default(self) + """ + if self._event_loop.IsRunning(): + self._event_loop.Exit() + + def _get_imagesave_wildcards(self): 'return the wildcard string for the filesave dialog' default_filetype = self.get_default_filetype() @@ -1185,46 +1225,6 @@ if figManager is not None: figManager.canvas.draw() - def start_event_loop(self, timeout=0): - """ - Start an event loop. This is used to start a blocking event - loop so that interactive functions, such as ginput and - waitforbuttonpress, can wait for events. This should not be - confused with the main GUI event loop, which is always running - and has nothing to do with this. - - Call signature:: - - start_event_loop(self,timeout=0) - - This call blocks until a callback function triggers - stop_event_loop() or *timeout* is reached. If *timeout* is - <=0, never timeout. - """ - root = self.GetTopLevelParent() - bind(root, wx.EVT_CLOSE, self.stop_event_loop) - - id = wx.NewId() - timer = wx.Timer(self, id=id) - if timeout > 0: - timer.Start(timeout*1000, oneShot=True) - bind(self, wx.EVT_TIMER, self.stop_event_loop, id=id) - self._event_loop.Run() - timer.Stop() - - def stop_event_loop(self, event=None): - """ - Stop an event loop. This is used to stop a blocking event - loop so that interactive functions, such as ginput and - waitforbuttonpress, can wait for events. - - Call signature:: - - stop_event_loop_default(self) - """ - if self._event_loop.IsRunning(): - self._event_loop.Exit() - # Event binding code changed after version 2.5 if wx.VERSION_STRING >= '2.5': def bind(actor,event,action,**kw): @@ -1359,6 +1359,7 @@ def _onClose(self, evt): DEBUG_MSG("onClose()", 1, self) + self.canvas.stop_event_loop() Gcf.destroy(self.num) #self.Destroy() Modified: trunk/matplotlib/lib/matplotlib/backends/backend_wxagg.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_wxagg.py 2008-07-24 14:22:20 UTC (rev 5838) +++ trunk/matplotlib/lib/matplotlib/backends/backend_wxagg.py 2008-07-24 14:54:22 UTC (rev 5839) @@ -41,7 +41,7 @@ toolbar = None return toolbar -class FigureCanvasWxAgg(FigureCanvasAgg, FigureCanvasWx): +class FigureCanvasWxAgg(FigureCanvasWx, FigureCanvasAgg): """ The FigureCanvas contains the figure and does event handling. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-24 18:35:27
|
Revision: 5842 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5842&view=rev Author: jdh2358 Date: 2008-07-24 18:35:24 +0000 (Thu, 24 Jul 2008) Log Message: ----------- updated api changes and changelog to reflect mlab2 deprecation Modified Paths: -------------- trunk/matplotlib/API_CHANGES trunk/matplotlib/CHANGELOG Modified: trunk/matplotlib/API_CHANGES =================================================================== --- trunk/matplotlib/API_CHANGES 2008-07-24 17:07:43 UTC (rev 5841) +++ trunk/matplotlib/API_CHANGES 2008-07-24 18:35:24 UTC (rev 5842) @@ -1,6 +1,11 @@ + Changes for 0.98.x ================== +* Deprecated (raise NotImplementedError) all the mlab2 functions from + matplotlib.mlab out of concern that some of them were not clean room + implementations. + * Methods get_offsets and set_offsets added to Collections base class. Modified: trunk/matplotlib/CHANGELOG =================================================================== --- trunk/matplotlib/CHANGELOG 2008-07-24 17:07:43 UTC (rev 5841) +++ trunk/matplotlib/CHANGELOG 2008-07-24 18:35:24 UTC (rev 5842) @@ -1,3 +1,7 @@ +2008-07-24 Deprecated (raise NotImplementedError) all the mlab2 + functions from matplotlib.mlab out of concern that some of + them were not clean room implementations. JDH + 2008-07-24 Rewrite of a significant portion of the clabel code (class ContourLabeler) to improve inlining. - DMK This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-24 21:56:08
|
Revision: 5850 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5850&view=rev Author: jdh2358 Date: 2008-07-24 21:56:06 +0000 (Thu, 24 Jul 2008) Log Message: ----------- minor cleanup of units example, added some to backend driver, more to be fixed Modified Paths: -------------- trunk/matplotlib/examples/tests/backend_driver.py trunk/matplotlib/examples/units/artist_tests.py trunk/matplotlib/examples/units/bar_demo2.py trunk/matplotlib/examples/units/bar_unit_demo.py trunk/matplotlib/examples/units/basic_units.py trunk/matplotlib/examples/units/evans_test.py trunk/matplotlib/examples/units/evans_test2.py trunk/matplotlib/examples/units/radian_demo.py trunk/matplotlib/examples/units/units_scatter.py trunk/matplotlib/unit/memleak_hawaii3.py Modified: trunk/matplotlib/examples/tests/backend_driver.py =================================================================== --- trunk/matplotlib/examples/tests/backend_driver.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/tests/backend_driver.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -121,9 +121,23 @@ 'color_cycle.py', ] +units_dir = os.path.join('..', 'units') +units_files = [ + 'annotate_with_units.py', + #'artist_tests.py', # broken, fixme + 'bar_demo2.py', + #'bar_unit_demo.py', # broken, fixme + #'ellipse_with_units.py', # broken, fixme + 'radian_demo.py', + 'units_sample.py', + #'units_scatter.py', # broken, fixme + ] + files = [os.path.join(pylab_dir, fname) for fname in pylab_files] +\ - [os.path.join(api_dir, fname) for fname in api_files] + [os.path.join(api_dir, fname) for fname in api_files] +\ + [os.path.join(units_dir, fname) for fname in units_files] + # tests known to fail on a given backend Modified: trunk/matplotlib/examples/units/artist_tests.py =================================================================== --- trunk/matplotlib/examples/units/artist_tests.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/units/artist_tests.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -14,9 +14,9 @@ import matplotlib.units as units from basic_units import cm, inch +import numpy as np +from pylab import figure, show -from pylab import figure, show, nx - fig = figure() ax = fig.add_subplot(111) ax.xaxis.set_units(cm) @@ -26,7 +26,7 @@ verts = [] for i in range(10): # a random line segment in inches - verts.append(zip(*inch*10*nx.mlab.rand(2, random.randint(2,15)))) + verts.append(zip(*inch*10*np.random.rand(2, random.randint(2,15)))) lc = collections.LineCollection(verts, axes=ax) ax.add_collection(lc) Modified: trunk/matplotlib/examples/units/bar_demo2.py =================================================================== --- trunk/matplotlib/examples/units/bar_demo2.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/units/bar_demo2.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -7,10 +7,11 @@ units) """ +import numpy as np from basic_units import cm, inch -from pylab import figure, show, nx +from pylab import figure, show -cms = cm *nx.arange(0, 10, 2) +cms = cm *np.arange(0, 10, 2) bottom=0*cm width=0.8*cm Modified: trunk/matplotlib/examples/units/bar_unit_demo.py =================================================================== --- trunk/matplotlib/examples/units/bar_unit_demo.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/units/bar_unit_demo.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -1,6 +1,7 @@ #!/usr/bin/env python +import numpy as np from basic_units import cm, inch -from pylab import figure, show,nx +from pylab import figure, show N = 5 menMeans = (150*cm, 160*cm, 146*cm, 172*cm, 155*cm) @@ -9,7 +10,7 @@ fig = figure() ax = fig.add_subplot(111) -ind = nx.arange(N) # the x locations for the groups +ind = np.arange(N) # the x locations for the groups width = 0.35 # the width of the bars p1 = ax.bar(ind, menMeans, width, color='r', bottom=0*cm, yerr=menStd) Modified: trunk/matplotlib/examples/units/basic_units.py =================================================================== --- trunk/matplotlib/examples/units/basic_units.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/units/basic_units.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -1,9 +1,8 @@ import math +import numpy as np - import matplotlib.units as units import matplotlib.ticker as ticker -import matplotlib.numerix as nx from matplotlib.axes import Axes from matplotlib.cbook import iterable @@ -122,7 +121,7 @@ self.proxy_target = self.value def get_compressed_copy(self, mask): - compressed_value = nx.ma.masked_array(self.value, mask=mask).compressed() + compressed_value = np.ma.masked_array(self.value, mask=mask).compressed() return TaggedValue(compressed_value, self.unit) def __getattribute__(self, name): @@ -135,9 +134,9 @@ def __array__(self, t = None, context = None): if t is not None: - return nx.asarray(self.value).astype(t) + return np.asarray(self.value).astype(t) else: - return nx.asarray(self.value, 'O') + return np.asarray(self.value, 'O') def __array_wrap__(self, array, context): return TaggedValue(array, self.unit) @@ -159,7 +158,7 @@ return IteratorProxy(iter(self.value), self.unit) def get_compressed_copy(self, mask): - new_value = nx.ma.masked_array(self.value, mask=mask).compressed() + new_value = np.ma.masked_array(self.value, mask=mask).compressed() return TaggedValue(new_value, self.unit) def convert_to(self, unit): @@ -211,7 +210,7 @@ return TaggedValue(array, self) def __array__(self, t=None, context=None): - ret = nx.array([1]) + ret = np.array([1]) if t is not None: return ret.astype(t) else: @@ -275,8 +274,8 @@ radians = BasicUnit('rad', 'radians') degrees = BasicUnit('deg', 'degrees') -radians.add_conversion_factor(degrees, 180.0/nx.pi) -degrees.add_conversion_factor(radians, nx.pi/180.0) +radians.add_conversion_factor(degrees, 180.0/np.pi) +degrees.add_conversion_factor(radians, np.pi/180.0) secs = BasicUnit('s', 'seconds') hertz = BasicUnit('Hz', 'Hertz') @@ -287,7 +286,7 @@ # radians formatting def rad_fn(x,pos=None): - n = int((x / nx.pi) * 2.0 + 0.25) + n = int((x / np.pi) * 2.0 + 0.25) if n == 0: return '0' elif n == 1: @@ -307,7 +306,7 @@ if unit==radians: return units.AxisInfo( - majloc=ticker.MultipleLocator(base=nx.pi/2), + majloc=ticker.MultipleLocator(base=np.pi/2), majfmt=ticker.FuncFormatter(rad_fn), label=unit.fullname, ) Modified: trunk/matplotlib/examples/units/evans_test.py =================================================================== --- trunk/matplotlib/examples/units/evans_test.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/units/evans_test.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -11,7 +11,7 @@ from matplotlib.cbook import iterable import matplotlib.units as units import matplotlib.ticker as ticker -from pylab import figure, show, nx +from pylab import figure, show class Foo: def __init__( self, val, unit=1.0 ): Modified: trunk/matplotlib/examples/units/evans_test2.py =================================================================== --- trunk/matplotlib/examples/units/evans_test2.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/units/evans_test2.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -3,13 +3,14 @@ This example shows how the unit class can determine the tick locating, formatting and axis labeling. """ +import numpy as np from basic_units import radians, degrees, cos -from pylab import figure, show, nx +from pylab import figure, show from matplotlib.cbook import iterable import math -x = nx.arange(0, 15, 0.01) * radians +x = np.arange(0, 15, 0.01) * radians fig = figure() Modified: trunk/matplotlib/examples/units/radian_demo.py =================================================================== --- trunk/matplotlib/examples/units/radian_demo.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/units/radian_demo.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -1,7 +1,8 @@ +import numpy as np from basic_units import radians, degrees, cos -from pylab import figure, show, nx +from pylab import figure, show -x = nx.arange(0, 15, 0.01) * radians +x = np.arange(0, 15, 0.01) * radians fig = figure() fig.subplots_adjust(hspace=0.3) Modified: trunk/matplotlib/examples/units/units_scatter.py =================================================================== --- trunk/matplotlib/examples/units/units_scatter.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/examples/units/units_scatter.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -8,14 +8,16 @@ The example below shows support for unit conversions over masked arrays. """ +import numpy as np from basic_units import secs, hertz, minutes -from matplotlib.pylab import figure, show, nx +from matplotlib.pylab import figure, show # create masked array -xsecs = secs*nx.ma.MaskedArray((1,2,3,4,5,6,7,8), nx.Float, mask=(1,0,1,0,0,0,1,0)) -#xsecs = secs*nx.arange(1,10.) +xsecs = secs*np.ma.MaskedArray((1,2,3,4,5,6,7,8), (1,0,1,0,0,0,1,0), np.float) +#xsecs = secs*np.arange(1,10.) + fig = figure() ax1 = fig.add_subplot(3,1,1) ax1.scatter(xsecs, xsecs) Modified: trunk/matplotlib/unit/memleak_hawaii3.py =================================================================== --- trunk/matplotlib/unit/memleak_hawaii3.py 2008-07-24 21:23:04 UTC (rev 5849) +++ trunk/matplotlib/unit/memleak_hawaii3.py 2008-07-24 21:56:06 UTC (rev 5850) @@ -2,7 +2,7 @@ import os, sys, time, gc import matplotlib -matplotlib.use('PDF') +matplotlib.use('Agg') from matplotlib.cbook import report_memory import numpy as np This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <pki...@us...> - 2008-07-24 22:30:02
|
Revision: 5853 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5853&view=rev Author: pkienzle Date: 2008-07-24 22:29:57 +0000 (Thu, 24 Jul 2008) Log Message: ----------- support mouse wheel in wx Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/image_slices_viewer.py trunk/matplotlib/lib/matplotlib/backend_bases.py trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py trunk/matplotlib/lib/matplotlib/backends/backend_wx.py Modified: trunk/matplotlib/examples/pylab_examples/image_slices_viewer.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/image_slices_viewer.py 2008-07-24 21:56:57 UTC (rev 5852) +++ trunk/matplotlib/examples/pylab_examples/image_slices_viewer.py 2008-07-24 22:29:57 UTC (rev 5853) @@ -17,7 +17,7 @@ self.update() def onscroll(self, event): - print event.button + print event.button, event.step if event.button=='up': self.ind = numpy.clip(self.ind+1, 0, self.slices-1) else: Modified: trunk/matplotlib/lib/matplotlib/backend_bases.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-07-24 21:56:57 UTC (rev 5852) +++ trunk/matplotlib/lib/matplotlib/backend_bases.py 2008-07-24 22:29:57 UTC (rev 5853) @@ -768,7 +768,10 @@ *key* the key pressed: None, chr(range(255), 'shift', 'win', or 'control' + *step* + number of scroll steps (positive for 'up', negative for 'down') + Example usage:: def on_press(event): @@ -783,16 +786,18 @@ inaxes = None # the Axes instance if mouse us over axes xdata = None # x coord of mouse in data coords ydata = None # y coord of mouse in data coords + step = None # scroll steps for scroll events def __init__(self, name, canvas, x, y, button=None, key=None, - guiEvent=None): + step=0, guiEvent=None): """ x, y in figure coords, 0,0 = bottom, left - button pressed None, 1, 2, 3 + button pressed None, 1, 2, 3, 'up', 'down' """ LocationEvent.__init__(self, name, canvas, x, y, guiEvent=guiEvent) self.button = button self.key = key + self.step = step class PickEvent(Event): """ @@ -1050,7 +1055,7 @@ event = PickEvent(s, self, mouseevent, artist, **kwargs) self.callbacks.process(s, event) - def scroll_event(self, x, y, button, guiEvent=None): + def scroll_event(self, x, y, step, guiEvent=None): """ Backend derived classes should call this function on any scroll wheel event. x,y are the canvas coords: 0,0 is lower, @@ -1059,9 +1064,13 @@ This method will be call all functions connected to the 'scroll_event' with a :class:`MouseEvent` instance. """ - self._button = button + if step >= 0: + self._button = 'up' + else: + self._button = 'down' s = 'scroll_event' - mouseevent = MouseEvent(s, self, x, y, button, self._key, guiEvent=guiEvent) + mouseevent = MouseEvent(s, self, x, y, self._button, self._key, + step=step, guiEvent=guiEvent) self.callbacks.process(s, mouseevent) Modified: trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py 2008-07-24 21:56:57 UTC (rev 5852) +++ trunk/matplotlib/lib/matplotlib/backends/backend_gtk.py 2008-07-24 22:29:57 UTC (rev 5853) @@ -181,10 +181,10 @@ # flipy so y=0 is bottom of canvas y = self.allocation.height - event.y if event.direction==gdk.SCROLL_UP: - direction = 'up' + step = 1 else: - direction = 'down' - FigureCanvasBase.scroll_event(self, x, y, direction) + step = -1 + FigureCanvasBase.scroll_event(self, x, y, step) return False # finish event propagation? def button_press_event(self, widget, event): Modified: trunk/matplotlib/lib/matplotlib/backends/backend_wx.py =================================================================== --- trunk/matplotlib/lib/matplotlib/backends/backend_wx.py 2008-07-24 21:56:57 UTC (rev 5852) +++ trunk/matplotlib/lib/matplotlib/backends/backend_wx.py 2008-07-24 22:29:57 UTC (rev 5853) @@ -1160,9 +1160,36 @@ FigureCanvasBase.button_release_event(self, x, y, 1, guiEvent=evt) def _onMouseWheel(self, evt): - # TODO: implement mouse wheel handler - pass + """Translate mouse wheel events into matplotlib events""" + # Determine mouse location + x = evt.GetX() + y = self.figure.bbox.height - evt.GetY() + + # Convert delta/rotation/rate into a floating point step size + delta = evt.GetWheelDelta() + rotation = evt.GetWheelRotation() + rate = evt.GetLinesPerAction() + #print "delta,rotation,rate",delta,rotation,rate + step = rate*float(rotation)/delta + + # Done handling event + evt.Skip() + + # Mac is giving two events for every wheel event + # Need to skip every second one + if wx.Platform == '__WXMAC__': + if not hasattr(self,'_skipwheelevent'): + self._skipwheelevent = True + elif self._skipwheelevent: + self._skipwheelevent = False + return # Return without processing event + else: + self._skipwheelevent = True + + # Convert to mpl event + FigureCanvasBase.scroll_event(self, x, y, step, guiEvent=evt) + def _onMotion(self, evt): """Start measuring on an axis.""" This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <ry...@us...> - 2008-07-25 01:43:45
|
Revision: 5861 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5861&view=rev Author: ryanmay Date: 2008-07-25 01:43:43 +0000 (Fri, 25 Jul 2008) Log Message: ----------- Add support for flipping which side of the barb the features are drawn. Useful to the meteorologists in the southern hemisphere plus anyone who might have an aesthetic preference. Modified Paths: -------------- trunk/matplotlib/examples/pylab_examples/barb_demo.py trunk/matplotlib/lib/matplotlib/quiver.py Modified: trunk/matplotlib/examples/pylab_examples/barb_demo.py =================================================================== --- trunk/matplotlib/examples/pylab_examples/barb_demo.py 2008-07-24 22:50:41 UTC (rev 5860) +++ trunk/matplotlib/examples/pylab_examples/barb_demo.py 2008-07-25 01:43:43 UTC (rev 5861) @@ -36,6 +36,7 @@ #Change colors as well as the increments for parts of the barbs ax = plt.subplot(2,2,4) ax.barbs(data['x'], data['y'], data['u'], data['v'], flagcolor='r', - barbcolor=['b','g'], barb_increments=dict(half=10, full=20, flag=100)) + barbcolor=['b','g'], barb_increments=dict(half=10, full=20, flag=100), + flip_barb=True) plt.show() Modified: trunk/matplotlib/lib/matplotlib/quiver.py =================================================================== --- trunk/matplotlib/lib/matplotlib/quiver.py 2008-07-24 22:50:41 UTC (rev 5860) +++ trunk/matplotlib/lib/matplotlib/quiver.py 2008-07-25 01:43:43 UTC (rev 5861) @@ -592,6 +592,16 @@ 'half' - half barbs (Default is 5) 'full' - full barbs (Default is 10) 'flag' - flags (default is 50) + + *flip_barb*: + Either a single boolean flag or an array of booleans. Single boolean + indicates whether the lines and flags should point opposite to normal + for all barbs. An array (which should be the same size as the other + data arrays) indicates whether to flip for each individual barb. + Normal behavior is for the barbs and lines to point right (comes from + wind barbs having these features point towards low pressure in the + Northern Hemisphere.) + Default is False Barbs are traditionally used in meteorology as a way to plot the speed and direction of wind observations, but can technically be used to plot @@ -647,7 +657,8 @@ self.fill_empty = kw.pop('fill_empty', False) self.barb_increments = kw.pop('barb_increments', dict()) self.rounding = kw.pop('rounding', True) - + self.flip = kw.pop('flip_barb', False) + #Flagcolor and and barbcolor provide convenience parameters for setting #the facecolor and edgecolor, respectively, of the barb polygon. We #also work here to make the flag the same color as the rest of the barb @@ -714,7 +725,7 @@ return num_flags, num_barb, half_flag, empty_flag def _make_barbs(self, u, v, nflags, nbarbs, half_barb, empty_flag, length, - pivot, sizes, fill_empty): + pivot, sizes, fill_empty, flip): '''This function actually creates the wind barbs. u and v are components of the vector in the x and y directions, respectively. nflags, nbarbs, and half_barb, empty_flag are, respectively, the number @@ -730,7 +741,13 @@ height - height (distance from shaft of top) of a flag or full barb width - width of a flag, twice the width of a full barb emptybarb - radius of the circle used for low magnitudes - + + fill_empty specifies whether the circle representing an empty barb + should be filled or not (this changes the drawing of the polygon). + flip is a flag indicating whether the features should be flipped to + the other side of the barb (useful for winds in the southern + hemisphere. + This function returns list of arrays of vertices, defining a polygon for each of the wind barbs. These polygons have been rotated to properly align with the vector direction.''' @@ -744,6 +761,9 @@ #Controls y point where to pivot the barb. pivot_points = dict(tip=0.0, middle=-length/2.) + + #Check for flip + if flip: full_height = -full_height endx = 0.0 endy = pivot_points[pivot.lower()] @@ -861,7 +881,7 @@ #Get the vertices for each of the barbs plot_barbs = self._make_barbs(u, v, flags, barbs, halves, empty, - self._length, self._pivot, self.sizes, self.fill_empty) + self._length, self._pivot, self.sizes, self.fill_empty, self.flip) self.set_verts(plot_barbs) #Set the color array This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: <jd...@us...> - 2008-07-25 13:17:14
|
Revision: 5873 http://matplotlib.svn.sourceforge.net/matplotlib/?rev=5873&view=rev Author: jdh2358 Date: 2008-07-25 13:17:10 +0000 (Fri, 25 Jul 2008) Log Message: ----------- committed tonys minor changes Modified Paths: -------------- trunk/matplotlib/examples/event_handling/lasso_demo.py trunk/matplotlib/lib/matplotlib/axis.py Modified: trunk/matplotlib/examples/event_handling/lasso_demo.py =================================================================== --- trunk/matplotlib/examples/event_handling/lasso_demo.py 2008-07-25 11:58:15 UTC (rev 5872) +++ trunk/matplotlib/examples/event_handling/lasso_demo.py 2008-07-25 13:17:10 UTC (rev 5873) @@ -69,7 +69,7 @@ # acquire a lock on the widget drawing self.canvas.widgetlock(self.lasso) -if 0: +if __name__ == '__main__': data = [Datum(*xy) for xy in rand(100, 2)] Modified: trunk/matplotlib/lib/matplotlib/axis.py =================================================================== --- trunk/matplotlib/lib/matplotlib/axis.py 2008-07-25 11:58:15 UTC (rev 5872) +++ trunk/matplotlib/lib/matplotlib/axis.py 2008-07-25 13:17:10 UTC (rev 5873) @@ -147,7 +147,7 @@ """ self._pad = val - def get_pad(self, val): + def get_pad(self): 'Get the value of the tick label pad in points' return self._pad This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |