|
From: <ian...@us...> - 2010-05-13 09:13:56
|
Revision: 8316
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8316&view=rev
Author: ianthomas23
Date: 2010-05-13 09:13:49 +0000 (Thu, 13 May 2010)
Log Message:
-----------
Added triangular grid plotting and contouring functions.
Modified Paths:
--------------
trunk/matplotlib/boilerplate.py
trunk/matplotlib/lib/matplotlib/axes.py
trunk/matplotlib/lib/matplotlib/pyplot.py
trunk/matplotlib/setup.py
trunk/matplotlib/setupext.py
Added Paths:
-----------
trunk/matplotlib/examples/misc/contour_manual.py
trunk/matplotlib/examples/pylab_examples/tricontour_demo.py
trunk/matplotlib/examples/pylab_examples/tricontour_vs_griddata.py
trunk/matplotlib/examples/pylab_examples/tripcolor_demo.py
trunk/matplotlib/examples/pylab_examples/triplot_demo.py
trunk/matplotlib/lib/matplotlib/tri/
trunk/matplotlib/lib/matplotlib/tri/__init__.py
trunk/matplotlib/lib/matplotlib/tri/_tri.cpp
trunk/matplotlib/lib/matplotlib/tri/_tri.h
trunk/matplotlib/lib/matplotlib/tri/triangulation.py
trunk/matplotlib/lib/matplotlib/tri/tricontour.py
trunk/matplotlib/lib/matplotlib/tri/tripcolor.py
trunk/matplotlib/lib/matplotlib/tri/triplot.py
Modified: trunk/matplotlib/boilerplate.py
===================================================================
--- trunk/matplotlib/boilerplate.py 2010-05-12 17:58:17 UTC (rev 8315)
+++ trunk/matplotlib/boilerplate.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -89,6 +89,10 @@
#'spy',
'stem',
'step',
+ 'tricontour',
+ 'tricontourf',
+ 'tripcolor',
+ 'triplot',
'vlines',
'xcorr',
'barbs',
@@ -117,6 +121,9 @@
#'spy' : 'sci(%(ret)s)', ### may return image or Line2D
'quiver' : 'sci(%(ret)s)',
'specgram' : 'sci(%(ret)s[-1])',
+ 'tricontour' : 'if %(ret)s._A is not None: sci(%(ret)s)',
+ 'tricontourf': 'if %(ret)s._A is not None: sci(%(ret)s)',
+ 'tripcolor' : 'sci(%(ret)s)',
}
Added: trunk/matplotlib/examples/misc/contour_manual.py
===================================================================
--- trunk/matplotlib/examples/misc/contour_manual.py (rev 0)
+++ trunk/matplotlib/examples/misc/contour_manual.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -0,0 +1,50 @@
+"""
+Example of displaying your own contour lines and polygons using ContourSet.
+"""
+import matplotlib.pyplot as plt
+from matplotlib.contour import ContourSet
+import matplotlib.cm as cm
+
+# Contour lines for each level are a list/tuple of polygons.
+lines0 = [ [[0,0],[0,4]] ]
+lines1 = [ [[2,0],[1,2],[1,3]] ]
+lines2 = [ [[3,0],[3,2]], [[3,3],[3,4]] ] # Note two lines.
+
+# Filled contours between two levels are also a list/tuple of polygons.
+# Points can be ordered clockwise or anticlockwise.
+filled01 = [ [[0,0],[0,4],[1,3],[1,2],[2,0]] ]
+filled12 = [ [[2,0],[3,0],[3,2],[1,3],[1,2]], # Note two polygons.
+ [[1,4],[3,4],[3,3]] ]
+
+
+plt.figure()
+
+# Filled contours using filled=True.
+cs = ContourSet(plt.gca(), [0,1,2], [filled01, filled12], filled=True, cmap=cm.bone)
+cbar = plt.colorbar(cs)
+
+# Contour lines (non-filled).
+lines = ContourSet(plt.gca(), [0,1,2], [lines0, lines1, lines2], cmap=cm.cool,
+ linewidths=3)
+cbar.add_lines(lines)
+
+plt.axis([-0.5, 3.5, -0.5, 4.5])
+plt.title('User-specified contours')
+
+
+
+# Multiple filled contour lines can be specified in a single list of polygon
+# vertices along with a list of vertex kinds (code types) as described in the
+# Path class. This is particularly useful for polygons with holes.
+# Here a code type of 1 is a MOVETO, and 2 is a LINETO.
+
+plt.figure()
+filled01 = [ [[0,0],[3,0],[3,3],[0,3],[1,1],[1,2],[2,2],[2,1]] ]
+kinds01 = [ [1,2,2,2,1,2,2,2] ]
+cs = ContourSet(plt.gca(), [0,1], [filled01], [kinds01], filled=True)
+cbar = plt.colorbar(cs)
+
+plt.axis([-0.5, 3.5, -0.5, 3.5])
+plt.title('User specified filled contours with holes')
+
+plt.show()
\ No newline at end of file
Property changes on: trunk/matplotlib/examples/misc/contour_manual.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: trunk/matplotlib/examples/pylab_examples/tricontour_demo.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/tricontour_demo.py (rev 0)
+++ trunk/matplotlib/examples/pylab_examples/tricontour_demo.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -0,0 +1,98 @@
+"""
+Contour plots of unstructured triangular grids.
+"""
+import matplotlib.pyplot as plt
+import matplotlib.tri as tri
+import numpy as np
+import math
+
+# Creating a Triangulation without specifying the triangles results in the
+# Delaunay triangulation of the points.
+
+# First create the x and y coordinates of the points.
+n_angles = 48
+n_radii = 8
+min_radius = 0.25
+radii = np.linspace(min_radius, 0.95, n_radii)
+
+angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False)
+angles = np.repeat(angles[...,np.newaxis], n_radii, axis=1)
+angles[:,1::2] += math.pi/n_angles
+
+x = (radii*np.cos(angles)).flatten()
+y = (radii*np.sin(angles)).flatten()
+z = (np.cos(radii)*np.cos(angles*3.0)).flatten()
+
+# Create the Triangulation; no triangles so Delaunay triangulation created.
+triang = tri.Triangulation(x, y)
+
+# Mask off unwanted triangles.
+xmid = x[triang.triangles].mean(axis=1)
+ymid = y[triang.triangles].mean(axis=1)
+mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0)
+triang.set_mask(mask)
+
+# pcolor plot.
+plt.figure()
+plt.gca().set_aspect('equal')
+plt.tricontourf(triang, z)
+plt.colorbar()
+plt.tricontour(triang, z, colors='k')
+plt.title('Contour plot of Delaunay triangulation')
+
+# You can specify your own triangulation rather than perform a Delaunay
+# triangulation of the points, where each triangle is given by the indices of
+# the three points that make up the triangle, ordered in either a clockwise or
+# anticlockwise manner.
+
+xy = np.asarray([
+ [-0.101,0.872],[-0.080,0.883],[-0.069,0.888],[-0.054,0.890],[-0.045,0.897],
+ [-0.057,0.895],[-0.073,0.900],[-0.087,0.898],[-0.090,0.904],[-0.069,0.907],
+ [-0.069,0.921],[-0.080,0.919],[-0.073,0.928],[-0.052,0.930],[-0.048,0.942],
+ [-0.062,0.949],[-0.054,0.958],[-0.069,0.954],[-0.087,0.952],[-0.087,0.959],
+ [-0.080,0.966],[-0.085,0.973],[-0.087,0.965],[-0.097,0.965],[-0.097,0.975],
+ [-0.092,0.984],[-0.101,0.980],[-0.108,0.980],[-0.104,0.987],[-0.102,0.993],
+ [-0.115,1.001],[-0.099,0.996],[-0.101,1.007],[-0.090,1.010],[-0.087,1.021],
+ [-0.069,1.021],[-0.052,1.022],[-0.052,1.017],[-0.069,1.010],[-0.064,1.005],
+ [-0.048,1.005],[-0.031,1.005],[-0.031,0.996],[-0.040,0.987],[-0.045,0.980],
+ [-0.052,0.975],[-0.040,0.973],[-0.026,0.968],[-0.020,0.954],[-0.006,0.947],
+ [ 0.003,0.935],[ 0.006,0.926],[ 0.005,0.921],[ 0.022,0.923],[ 0.033,0.912],
+ [ 0.029,0.905],[ 0.017,0.900],[ 0.012,0.895],[ 0.027,0.893],[ 0.019,0.886],
+ [ 0.001,0.883],[-0.012,0.884],[-0.029,0.883],[-0.038,0.879],[-0.057,0.881],
+ [-0.062,0.876],[-0.078,0.876],[-0.087,0.872],[-0.030,0.907],[-0.007,0.905],
+ [-0.057,0.916],[-0.025,0.933],[-0.077,0.990],[-0.059,0.993] ])
+x = xy[:,0]*180/3.14159
+y = xy[:,1]*180/3.14159
+x0 = -5
+y0 = 52
+z = np.exp(-0.01*( (x-x0)*(x-x0) + (y-y0)*(y-y0) ))
+
+triangles = np.asarray([
+ [67,66, 1],[65, 2,66],[ 1,66, 2],[64, 2,65],[63, 3,64],[60,59,57],
+ [ 2,64, 3],[ 3,63, 4],[ 0,67, 1],[62, 4,63],[57,59,56],[59,58,56],
+ [61,60,69],[57,69,60],[ 4,62,68],[ 6, 5, 9],[61,68,62],[69,68,61],
+ [ 9, 5,70],[ 6, 8, 7],[ 4,70, 5],[ 8, 6, 9],[56,69,57],[69,56,52],
+ [70,10, 9],[54,53,55],[56,55,53],[68,70, 4],[52,56,53],[11,10,12],
+ [69,71,68],[68,13,70],[10,70,13],[51,50,52],[13,68,71],[52,71,69],
+ [12,10,13],[71,52,50],[71,14,13],[50,49,71],[49,48,71],[14,16,15],
+ [14,71,48],[17,19,18],[17,20,19],[48,16,14],[48,47,16],[47,46,16],
+ [16,46,45],[23,22,24],[21,24,22],[17,16,45],[20,17,45],[21,25,24],
+ [27,26,28],[20,72,21],[25,21,72],[45,72,20],[25,28,26],[44,73,45],
+ [72,45,73],[28,25,29],[29,25,31],[43,73,44],[73,43,40],[72,73,39],
+ [72,31,25],[42,40,43],[31,30,29],[39,73,40],[42,41,40],[72,33,31],
+ [32,31,33],[39,38,72],[33,72,38],[33,38,34],[37,35,38],[34,38,35],
+ [35,37,36] ])
+
+# Rather than create a Triangulation object, can simply pass x, y and triangles
+# arrays to tripcolor directly. It would be better to use a Triangulation object
+# if the same triangulation was to be used more than once to save duplicated
+# calculations.
+plt.figure()
+plt.gca().set_aspect('equal')
+plt.tricontourf(x, y, triangles, z)
+plt.colorbar()
+plt.title('Contour plot of user-specified triangulation')
+plt.xlabel('Longitude (degrees)')
+plt.ylabel('Latitude (degrees)')
+
+plt.show()
Property changes on: trunk/matplotlib/examples/pylab_examples/tricontour_demo.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: trunk/matplotlib/examples/pylab_examples/tricontour_vs_griddata.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/tricontour_vs_griddata.py (rev 0)
+++ trunk/matplotlib/examples/pylab_examples/tricontour_vs_griddata.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -0,0 +1,47 @@
+"""
+Comparison of griddata and tricontour for an unstructured triangular grid.
+"""
+import matplotlib.pyplot as plt
+import matplotlib.tri as tri
+import numpy as np
+from numpy.random import uniform, seed
+from matplotlib.mlab import griddata
+import time
+
+seed(0)
+npts = 200
+ngridx = 100
+ngridy = 200
+x = uniform(-2,2,npts)
+y = uniform(-2,2,npts)
+z = x*np.exp(-x**2-y**2)
+
+# griddata and contour.
+start = time.clock()
+plt.subplot(211)
+xi = np.linspace(-2.1,2.1,ngridx)
+yi = np.linspace(-2.1,2.1,ngridy)
+zi = griddata(x,y,z,xi,yi,interp='linear')
+plt.contour(xi,yi,zi,15,linewidths=0.5,colors='k')
+plt.contourf(xi,yi,zi,15,cmap=plt.cm.jet)
+plt.colorbar() # draw colorbar
+plt.plot(x, y, 'ko', ms=3)
+plt.xlim(-2,2)
+plt.ylim(-2,2)
+plt.title('griddata and contour (%d points, %d grid points)' % (npts, ngridx*ngridy))
+print 'griddata and contour seconds:', time.clock() - start
+
+# tricontour.
+start = time.clock()
+plt.subplot(212)
+triang = tri.Triangulation(x, y)
+plt.tricontour(x, y, z, 15, linewidths=0.5, colors='k')
+plt.tricontourf(x, y, z, 15, cmap=plt.cm.jet)
+plt.colorbar()
+plt.plot(x, y, 'ko', ms=3)
+plt.xlim(-2,2)
+plt.ylim(-2,2)
+plt.title('tricontour (%d points)' % npts)
+print 'tricontour seconds:', time.clock() - start
+
+plt.show()
Property changes on: trunk/matplotlib/examples/pylab_examples/tricontour_vs_griddata.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: trunk/matplotlib/examples/pylab_examples/tripcolor_demo.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/tripcolor_demo.py (rev 0)
+++ trunk/matplotlib/examples/pylab_examples/tripcolor_demo.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -0,0 +1,98 @@
+"""
+Pseudocolor plots of unstructured triangular grids.
+"""
+import matplotlib.pyplot as plt
+import matplotlib.tri as tri
+import numpy as np
+import math
+
+# Creating a Triangulation without specifying the triangles results in the
+# Delaunay triangulation of the points.
+
+# First create the x and y coordinates of the points.
+n_angles = 36
+n_radii = 8
+min_radius = 0.25
+radii = np.linspace(min_radius, 0.95, n_radii)
+
+angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False)
+angles = np.repeat(angles[...,np.newaxis], n_radii, axis=1)
+angles[:,1::2] += math.pi/n_angles
+
+x = (radii*np.cos(angles)).flatten()
+y = (radii*np.sin(angles)).flatten()
+z = (np.cos(radii)*np.cos(angles*3.0)).flatten()
+
+# Create the Triangulation; no triangles so Delaunay triangulation created.
+triang = tri.Triangulation(x, y)
+
+# Mask off unwanted triangles.
+xmid = x[triang.triangles].mean(axis=1)
+ymid = y[triang.triangles].mean(axis=1)
+mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0)
+triang.set_mask(mask)
+
+# pcolor plot.
+plt.figure()
+plt.gca().set_aspect('equal')
+plt.tripcolor(triang, z, shading='faceted')
+plt.colorbar()
+plt.title('tripcolor of Delaunay triangulation')
+
+
+# You can specify your own triangulation rather than perform a Delaunay
+# triangulation of the points, where each triangle is given by the indices of
+# the three points that make up the triangle, ordered in either a clockwise or
+# anticlockwise manner.
+
+xy = np.asarray([
+ [-0.101,0.872],[-0.080,0.883],[-0.069,0.888],[-0.054,0.890],[-0.045,0.897],
+ [-0.057,0.895],[-0.073,0.900],[-0.087,0.898],[-0.090,0.904],[-0.069,0.907],
+ [-0.069,0.921],[-0.080,0.919],[-0.073,0.928],[-0.052,0.930],[-0.048,0.942],
+ [-0.062,0.949],[-0.054,0.958],[-0.069,0.954],[-0.087,0.952],[-0.087,0.959],
+ [-0.080,0.966],[-0.085,0.973],[-0.087,0.965],[-0.097,0.965],[-0.097,0.975],
+ [-0.092,0.984],[-0.101,0.980],[-0.108,0.980],[-0.104,0.987],[-0.102,0.993],
+ [-0.115,1.001],[-0.099,0.996],[-0.101,1.007],[-0.090,1.010],[-0.087,1.021],
+ [-0.069,1.021],[-0.052,1.022],[-0.052,1.017],[-0.069,1.010],[-0.064,1.005],
+ [-0.048,1.005],[-0.031,1.005],[-0.031,0.996],[-0.040,0.987],[-0.045,0.980],
+ [-0.052,0.975],[-0.040,0.973],[-0.026,0.968],[-0.020,0.954],[-0.006,0.947],
+ [ 0.003,0.935],[ 0.006,0.926],[ 0.005,0.921],[ 0.022,0.923],[ 0.033,0.912],
+ [ 0.029,0.905],[ 0.017,0.900],[ 0.012,0.895],[ 0.027,0.893],[ 0.019,0.886],
+ [ 0.001,0.883],[-0.012,0.884],[-0.029,0.883],[-0.038,0.879],[-0.057,0.881],
+ [-0.062,0.876],[-0.078,0.876],[-0.087,0.872],[-0.030,0.907],[-0.007,0.905],
+ [-0.057,0.916],[-0.025,0.933],[-0.077,0.990],[-0.059,0.993] ])
+x = xy[:,0]*180/3.14159
+y = xy[:,1]*180/3.14159
+x0 = -5
+y0 = 52
+z = np.exp(-0.01*( (x-x0)*(x-x0) + (y-y0)*(y-y0) ))
+
+triangles = np.asarray([
+ [67,66, 1],[65, 2,66],[ 1,66, 2],[64, 2,65],[63, 3,64],[60,59,57],
+ [ 2,64, 3],[ 3,63, 4],[ 0,67, 1],[62, 4,63],[57,59,56],[59,58,56],
+ [61,60,69],[57,69,60],[ 4,62,68],[ 6, 5, 9],[61,68,62],[69,68,61],
+ [ 9, 5,70],[ 6, 8, 7],[ 4,70, 5],[ 8, 6, 9],[56,69,57],[69,56,52],
+ [70,10, 9],[54,53,55],[56,55,53],[68,70, 4],[52,56,53],[11,10,12],
+ [69,71,68],[68,13,70],[10,70,13],[51,50,52],[13,68,71],[52,71,69],
+ [12,10,13],[71,52,50],[71,14,13],[50,49,71],[49,48,71],[14,16,15],
+ [14,71,48],[17,19,18],[17,20,19],[48,16,14],[48,47,16],[47,46,16],
+ [16,46,45],[23,22,24],[21,24,22],[17,16,45],[20,17,45],[21,25,24],
+ [27,26,28],[20,72,21],[25,21,72],[45,72,20],[25,28,26],[44,73,45],
+ [72,45,73],[28,25,29],[29,25,31],[43,73,44],[73,43,40],[72,73,39],
+ [72,31,25],[42,40,43],[31,30,29],[39,73,40],[42,41,40],[72,33,31],
+ [32,31,33],[39,38,72],[33,72,38],[33,38,34],[37,35,38],[34,38,35],
+ [35,37,36] ])
+
+# Rather than create a Triangulation object, can simply pass x, y and triangles
+# arrays to tripcolor directly. It would be better to use a Triangulation object
+# if the same triangulation was to be used more than once to save duplicated
+# calculations.
+plt.figure()
+plt.gca().set_aspect('equal')
+plt.tripcolor(x, y, triangles, z, shading='faceted')
+plt.colorbar()
+plt.title('tripcolor of user-specified triangulation')
+plt.xlabel('Longitude (degrees)')
+plt.ylabel('Latitude (degrees)')
+
+plt.show()
Property changes on: trunk/matplotlib/examples/pylab_examples/tripcolor_demo.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: trunk/matplotlib/examples/pylab_examples/triplot_demo.py
===================================================================
--- trunk/matplotlib/examples/pylab_examples/triplot_demo.py (rev 0)
+++ trunk/matplotlib/examples/pylab_examples/triplot_demo.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -0,0 +1,92 @@
+"""
+Creating and plotting unstructured triangular grids.
+"""
+import matplotlib.pyplot as plt
+import matplotlib.tri as tri
+import numpy as np
+import math
+
+# Creating a Triangulation without specifying the triangles results in the
+# Delaunay triangulation of the points.
+
+# First create the x and y coordinates of the points.
+n_angles = 36
+n_radii = 8
+min_radius = 0.25
+radii = np.linspace(min_radius, 0.95, n_radii)
+
+angles = np.linspace(0, 2*math.pi, n_angles, endpoint=False)
+angles = np.repeat(angles[...,np.newaxis], n_radii, axis=1)
+angles[:,1::2] += math.pi/n_angles
+
+x = (radii*np.cos(angles)).flatten()
+y = (radii*np.sin(angles)).flatten()
+
+# Create the Triangulation; no triangles so Delaunay triangulation created.
+triang = tri.Triangulation(x, y)
+
+# Mask off unwanted triangles.
+xmid = x[triang.triangles].mean(axis=1)
+ymid = y[triang.triangles].mean(axis=1)
+mask = np.where(xmid*xmid + ymid*ymid < min_radius*min_radius, 1, 0)
+triang.set_mask(mask)
+
+# Plot the triangulation.
+plt.figure()
+plt.gca().set_aspect('equal')
+plt.triplot(triang, 'bo-')
+plt.title('triplot of Delaunay triangulation')
+
+
+# You can specify your own triangulation rather than perform a Delaunay
+# triangulation of the points, where each triangle is given by the indices of
+# the three points that make up the triangle, ordered in either a clockwise or
+# anticlockwise manner.
+
+xy = np.asarray([
+ [-0.101,0.872],[-0.080,0.883],[-0.069,0.888],[-0.054,0.890],[-0.045,0.897],
+ [-0.057,0.895],[-0.073,0.900],[-0.087,0.898],[-0.090,0.904],[-0.069,0.907],
+ [-0.069,0.921],[-0.080,0.919],[-0.073,0.928],[-0.052,0.930],[-0.048,0.942],
+ [-0.062,0.949],[-0.054,0.958],[-0.069,0.954],[-0.087,0.952],[-0.087,0.959],
+ [-0.080,0.966],[-0.085,0.973],[-0.087,0.965],[-0.097,0.965],[-0.097,0.975],
+ [-0.092,0.984],[-0.101,0.980],[-0.108,0.980],[-0.104,0.987],[-0.102,0.993],
+ [-0.115,1.001],[-0.099,0.996],[-0.101,1.007],[-0.090,1.010],[-0.087,1.021],
+ [-0.069,1.021],[-0.052,1.022],[-0.052,1.017],[-0.069,1.010],[-0.064,1.005],
+ [-0.048,1.005],[-0.031,1.005],[-0.031,0.996],[-0.040,0.987],[-0.045,0.980],
+ [-0.052,0.975],[-0.040,0.973],[-0.026,0.968],[-0.020,0.954],[-0.006,0.947],
+ [ 0.003,0.935],[ 0.006,0.926],[ 0.005,0.921],[ 0.022,0.923],[ 0.033,0.912],
+ [ 0.029,0.905],[ 0.017,0.900],[ 0.012,0.895],[ 0.027,0.893],[ 0.019,0.886],
+ [ 0.001,0.883],[-0.012,0.884],[-0.029,0.883],[-0.038,0.879],[-0.057,0.881],
+ [-0.062,0.876],[-0.078,0.876],[-0.087,0.872],[-0.030,0.907],[-0.007,0.905],
+ [-0.057,0.916],[-0.025,0.933],[-0.077,0.990],[-0.059,0.993] ])
+x = xy[:,0]*180/3.14159
+y = xy[:,1]*180/3.14159
+
+triangles = np.asarray([
+ [67,66, 1],[65, 2,66],[ 1,66, 2],[64, 2,65],[63, 3,64],[60,59,57],
+ [ 2,64, 3],[ 3,63, 4],[ 0,67, 1],[62, 4,63],[57,59,56],[59,58,56],
+ [61,60,69],[57,69,60],[ 4,62,68],[ 6, 5, 9],[61,68,62],[69,68,61],
+ [ 9, 5,70],[ 6, 8, 7],[ 4,70, 5],[ 8, 6, 9],[56,69,57],[69,56,52],
+ [70,10, 9],[54,53,55],[56,55,53],[68,70, 4],[52,56,53],[11,10,12],
+ [69,71,68],[68,13,70],[10,70,13],[51,50,52],[13,68,71],[52,71,69],
+ [12,10,13],[71,52,50],[71,14,13],[50,49,71],[49,48,71],[14,16,15],
+ [14,71,48],[17,19,18],[17,20,19],[48,16,14],[48,47,16],[47,46,16],
+ [16,46,45],[23,22,24],[21,24,22],[17,16,45],[20,17,45],[21,25,24],
+ [27,26,28],[20,72,21],[25,21,72],[45,72,20],[25,28,26],[44,73,45],
+ [72,45,73],[28,25,29],[29,25,31],[43,73,44],[73,43,40],[72,73,39],
+ [72,31,25],[42,40,43],[31,30,29],[39,73,40],[42,41,40],[72,33,31],
+ [32,31,33],[39,38,72],[33,72,38],[33,38,34],[37,35,38],[34,38,35],
+ [35,37,36] ])
+
+# Rather than create a Triangulation object, can simply pass x, y and triangles
+# arrays to triplot directly. It would be better to use a Triangulation object
+# if the same triangulation was to be used more than once to save duplicated
+# calculations.
+plt.figure()
+plt.gca().set_aspect('equal')
+plt.triplot(x, y, triangles, 'go-')
+plt.title('triplot of user-specified triangulation')
+plt.xlabel('Longitude (degrees)')
+plt.ylabel('Latitude (degrees)')
+
+plt.show()
Property changes on: trunk/matplotlib/examples/pylab_examples/triplot_demo.py
___________________________________________________________________
Added: svn:keywords
+ Id
Modified: trunk/matplotlib/lib/matplotlib/axes.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/axes.py 2010-05-12 17:58:17 UTC (rev 8315)
+++ trunk/matplotlib/lib/matplotlib/axes.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -32,6 +32,7 @@
import matplotlib.text as mtext
import matplotlib.ticker as mticker
import matplotlib.transforms as mtransforms
+import matplotlib.tri as mtri
iterable = cbook.iterable
@@ -8021,7 +8022,23 @@
self.xaxis.set_minor_locator(mticker.NullLocator())
self.yaxis.set_minor_locator(mticker.NullLocator())
+ def tricontour(self, *args, **kwargs):
+ return mtri.tricontour(self, *args, **kwargs)
+ tricontour.__doc__ = mtri.TriContourSet.tricontour_doc
+ def tricontourf(self, *args, **kwargs):
+ return mtri.tricontourf(self, *args, **kwargs)
+ tricontourf.__doc__ = mtri.TriContourSet.tricontour_doc
+
+ def tripcolor(self, *args, **kwargs):
+ return mtri.tripcolor(self, *args, **kwargs)
+ tripcolor.__doc__ = mtri.tripcolor.__doc__
+
+ def triplot(self, *args, **kwargs):
+ mtri.triplot(self, *args, **kwargs)
+ triplot.__doc__ = mtri.triplot.__doc__
+
+
class SubplotBase:
"""
Base class for subplots, which are :class:`Axes` instances with
Modified: trunk/matplotlib/lib/matplotlib/pyplot.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/pyplot.py 2010-05-12 17:58:17 UTC (rev 8315)
+++ trunk/matplotlib/lib/matplotlib/pyplot.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -2427,6 +2427,78 @@
# This function was autogenerated by boilerplate.py. Do not edit as
# changes will be lost
+@autogen_docstring(Axes.tricontour)
+def tricontour(*args, **kwargs):
+ ax = gca()
+ # allow callers to override the hold state by passing hold=True|False
+ washold = ax.ishold()
+ hold = kwargs.pop('hold', None)
+ if hold is not None:
+ ax.hold(hold)
+ try:
+ ret = ax.tricontour(*args, **kwargs)
+ draw_if_interactive()
+ finally:
+ ax.hold(washold)
+ if ret._A is not None: sci(ret)
+ return ret
+
+# This function was autogenerated by boilerplate.py. Do not edit as
+# changes will be lost
+@autogen_docstring(Axes.tricontourf)
+def tricontourf(*args, **kwargs):
+ ax = gca()
+ # allow callers to override the hold state by passing hold=True|False
+ washold = ax.ishold()
+ hold = kwargs.pop('hold', None)
+ if hold is not None:
+ ax.hold(hold)
+ try:
+ ret = ax.tricontourf(*args, **kwargs)
+ draw_if_interactive()
+ finally:
+ ax.hold(washold)
+ if ret._A is not None: sci(ret)
+ return ret
+
+# This function was autogenerated by boilerplate.py. Do not edit as
+# changes will be lost
+@autogen_docstring(Axes.tripcolor)
+def tripcolor(*args, **kwargs):
+ ax = gca()
+ # allow callers to override the hold state by passing hold=True|False
+ washold = ax.ishold()
+ hold = kwargs.pop('hold', None)
+ if hold is not None:
+ ax.hold(hold)
+ try:
+ ret = ax.tripcolor(*args, **kwargs)
+ draw_if_interactive()
+ finally:
+ ax.hold(washold)
+ sci(ret)
+ return ret
+
+# This function was autogenerated by boilerplate.py. Do not edit as
+# changes will be lost
+@autogen_docstring(Axes.triplot)
+def triplot(*args, **kwargs):
+ ax = gca()
+ # allow callers to override the hold state by passing hold=True|False
+ washold = ax.ishold()
+ hold = kwargs.pop('hold', None)
+ if hold is not None:
+ ax.hold(hold)
+ try:
+ ret = ax.triplot(*args, **kwargs)
+ draw_if_interactive()
+ finally:
+ ax.hold(washold)
+
+ return ret
+
+# This function was autogenerated by boilerplate.py. Do not edit as
+# changes will be lost
@autogen_docstring(Axes.vlines)
def vlines(x, ymin, ymax, colors='k', linestyles='solid', label='', hold=None, **kwargs):
ax = gca()
Added: trunk/matplotlib/lib/matplotlib/tri/__init__.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/tri/__init__.py (rev 0)
+++ trunk/matplotlib/lib/matplotlib/tri/__init__.py 2010-05-13 09:13:49 UTC (rev 8316)
@@ -0,0 +1,8 @@
+"""
+Unstructured triangular grid functions.
+"""
+
+from triangulation import *
+from tricontour import *
+from tripcolor import *
+from triplot import *
Property changes on: trunk/matplotlib/lib/matplotlib/tri/__init__.py
___________________________________________________________________
Added: svn:keywords
+ Id
Added: trunk/matplotlib/lib/matplotlib/tri/_tri.cpp
===================================================================
--- trunk/matplotlib/lib/matplotlib/tri/_tri.cpp (rev 0)
+++ trunk/matplotlib/lib/matplotlib/tri/_tri.cpp 2010-05-13 09:13:49 UTC (rev 8316)
@@ -0,0 +1,1112 @@
+#include "_tri.h"
+#include "src/mplutils.h"
+
+#include <algorithm>
+#include <iostream>
+#include <set>
+
+#define MOVETO 1
+#define LINETO 2
+
+
+
+TriEdge::TriEdge()
+ : tri(-1), edge(-1)
+{}
+
+TriEdge::TriEdge(int tri_, int edge_)
+ : tri(tri_), edge(edge_)
+{}
+
+bool TriEdge::operator<(const TriEdge& other) const
+{
+ if (tri != other.tri)
+ return tri < other.tri;
+ else
+ return edge < other.edge;
+}
+
+bool TriEdge::operator==(const TriEdge& other) const
+{
+ return tri == other.tri && edge == other.edge;
+}
+
+bool TriEdge::operator!=(const TriEdge& other) const
+{
+ return !operator==(other);
+}
+
+std::ostream& operator<<(std::ostream& os, const TriEdge& tri_edge)
+{
+ return os << tri_edge.tri << ' ' << tri_edge.edge;
+}
+
+
+
+XY::XY()
+{}
+
+XY::XY(const double& x_, const double& y_)
+ : x(x_), y(y_)
+{}
+
+double XY::angle() const
+{
+ return atan2(y, x);
+}
+
+double XY::cross_z(const XY& other) const
+{
+ return x*other.y - y*other.x;
+}
+
+bool XY::operator==(const XY& other) const
+{
+ return x == other.x && y == other.y;
+}
+
+bool XY::operator!=(const XY& other) const
+{
+ return x != other.x || y != other.y;
+}
+
+bool XY::operator<(const XY& other) const
+{
+ if (y == other.y)
+ return x < other.x;
+ else
+ return y < other.y;
+}
+
+XY XY::operator*(const double& multiplier) const
+{
+ return XY(x*multiplier, y*multiplier);
+}
+
+XY XY::operator+(const XY& other) const
+{
+ return XY(x + other.x, y + other.y);
+}
+
+XY XY::operator-(const XY& other) const
+{
+ return XY(x - other.x, y - other.y);
+}
+
+std::ostream& operator<<(std::ostream& os, const XY& xy)
+{
+ return os << '(' << xy.x << ' ' << xy.y << ')';
+}
+
+
+
+BoundingBox::BoundingBox()
+ : empty(true)
+{}
+
+void BoundingBox::add(const XY& point)
+{
+ if (empty) {
+ empty = false;
+ lower = upper = point;
+ } else {
+ if (point.x < lower.x) lower.x = point.x;
+ else if (point.x > upper.x) upper.x = point.x;
+
+ if (point.y < lower.y) lower.y = point.y;
+ else if (point.y > upper.y) upper.y = point.y;
+ }
+}
+
+bool BoundingBox::contains_x(const double& x) const
+{
+ return !empty && x >= lower.x && x <= upper.x;
+}
+
+std::ostream& operator<<(std::ostream& os, const BoundingBox& box)
+{
+ if (box.empty)
+ return os << "<empty>";
+ else
+ return os << box.lower << " -> " << box.upper;
+}
+
+
+
+ContourLine::ContourLine()
+ : std::vector<XY>()
+{}
+
+void ContourLine::insert_unique(iterator pos, const XY& point)
+{
+ if (empty() || pos == end() || point != *pos)
+ std::vector<XY>::insert(pos, point);
+}
+
+void ContourLine::push_back(const XY& point)
+{
+ if (empty() || point != back())
+ std::vector<XY>::push_back(point);
+}
+
+void ContourLine::write() const
+{
+ std::cout << "ContourLine of " << size() << " points:";
+ for (const_iterator it = begin(); it != end(); ++it)
+ std::cout << ' ' << *it;
+ std::cout << std::endl;
+}
+
+
+
+void write_contour(const Contour& contour)
+{
+ std::cout << "Contour of " << contour.size() << " lines." << std::endl;
+ for (Contour::const_iterator it = contour.begin(); it != contour.end(); ++it)
+ it->write();
+}
+
+
+
+
+Triangulation::Triangulation(PyArrayObject* x,
+ PyArrayObject* y,
+ PyArrayObject* triangles,
+ PyArrayObject* mask,
+ PyArrayObject* edges,
+ PyArrayObject* neighbors)
+ : _npoints(PyArray_DIM(x,0)),
+ _ntri(PyArray_DIM(triangles,0)),
+ _x(x),
+ _y(y),
+ _triangles(triangles),
+ _mask(mask),
+ _edges(edges),
+ _neighbors(neighbors)
+{
+ _VERBOSE("Triangulation::Triangulation");
+ correct_triangles();
+}
+
+Triangulation::~Triangulation()
+{
+ _VERBOSE("Triangulation::~Triangulation");
+ Py_XDECREF(_x);
+ Py_XDECREF(_y);
+ Py_XDECREF(_triangles);
+ Py_XDECREF(_mask);
+ Py_XDECREF(_edges);
+ Py_XDECREF(_neighbors);
+}
+
+void Triangulation::calculate_boundaries()
+{
+ _VERBOSE("Triangulation::calculate_boundaries");
+
+ get_neighbors(); // Ensure _neighbors has been created.
+
+ // Create set of all boundary TriEdges, which are those which do not
+ // have a neighbor triangle.
+ typedef std::set<TriEdge> BoundaryEdges;
+ BoundaryEdges boundary_edges;
+ for (int tri = 0; tri < _ntri; ++tri) {
+ if (!is_masked(tri)) {
+ for (int edge = 0; edge < 3; ++edge) {
+ if (get_neighbor(tri, edge) == -1) {
+ boundary_edges.insert(TriEdge(tri, edge));
+ }
+ }
+ }
+ }
+
+ // Take any boundary edge and follow the boundary until return to start
+ // point, removing edges from boundary_edges as they are used. At the same
+ // time, initialise the _tri_edge_to_boundary_map.
+ while (!boundary_edges.empty()) {
+ // Start of new boundary.
+ BoundaryEdges::iterator it = boundary_edges.begin();
+ int tri = it->tri;
+ int edge = it->edge;
+ _boundaries.push_back(Boundary());
+ Boundary& boundary = _boundaries.back();
+
+ while (true) {
+ boundary.push_back(TriEdge(tri, edge));
+ boundary_edges.erase(it);
+ _tri_edge_to_boundary_map[TriEdge(tri, edge)] =
+ BoundaryEdge(_boundaries.size()-1, boundary.size()-1);
+
+ // Move to next edge of current triangle.
+ edge = (edge+1) % 3;
+
+ // Find start point index of boundary edge.
+ int point = get_triangle_point(tri, edge);
+
+ // Find next TriEdge by traversing neighbors until find one
+ // without a neighbor.
+ while (get_neighbor(tri, edge) != -1) {
+ tri = get_neighbor(tri, edge);
+ edge = get_edge_in_triangle(tri, point);
+ }
+
+ if (TriEdge(tri,edge) == boundary.front())
+ break; // Reached beginning of this boundary, so finished it.
+ else
+ it = boundary_edges.find(TriEdge(tri, edge));
+ }
+ }
+}
+
+void Triangulation::calculate_edges()
+{
+ _VERBOSE("Triangulation::calculate_edges");
+ Py_XDECREF(_edges);
+
+ // Create set of all edges, storing them with start point index less than
+ // end point index.
+ typedef std::set<Edge> EdgeSet;
+ EdgeSet edge_set;
+ for (int tri = 0; tri < _ntri; ++tri) {
+ if (!is_masked(tri)) {
+ for (int edge = 0; edge < 3; edge++) {
+ int start = get_triangle_point(tri, edge);
+ int end = get_triangle_point(tri, (edge+1)%3);
+ edge_set.insert(start > end ? Edge(start,end) : Edge(end,start));
+ }
+ }
+ }
+
+ // Convert to python _edges array.
+ npy_intp dims[2] = {edge_set.size(), 2};
+ _edges = (PyArrayObject*)PyArray_SimpleNew(2, dims, PyArray_INT);
+ int* edges_ptr = (int*)PyArray_DATA(_edges);
+ for (EdgeSet::const_iterator it = edge_set.begin(); it != edge_set.end(); ++it) {
+ *edges_ptr++ = it->start;
+ *edges_ptr++ = it->end;
+ }
+}
+
+void Triangulation::calculate_neighbors()
+{
+ _VERBOSE("Triangulation::calculate_neighbors");
+ Py_XDECREF(_neighbors);
+
+ // Create _neighbors array with shape (ntri,3) and initialise all to -1.
+ npy_intp dims[2] = {_ntri,3};
+ _neighbors = (PyArrayObject*)PyArray_SimpleNew(2, dims, PyArray_INT);
+ int* neighbors_ptr = (int*)PyArray_DATA(_neighbors);
+ std::fill(neighbors_ptr, neighbors_ptr + 3*_ntri, -1);
+
+ // For each triangle edge (start to end point), find corresponding neighbor
+ // edge from end to start point. Do this by traversing all edges and
+ // storing them in a map from edge to TriEdge. If corresponding neighbor
+ // edge is already in the map, don't need to store new edge as neighbor
+ // already found.
+ typedef std::map<Edge, TriEdge> EdgeToTriEdgeMap;
+ EdgeToTriEdgeMap edge_to_tri_edge_map;
+ for (int tri = 0; tri < _ntri; ++tri) {
+ if (!is_masked(tri)) {
+ for (int edge = 0; edge < 3; ++edge) {
+ int start = get_triangle_point(tri, edge);
+ int end = get_triangle_point(tri, (edge+1)%3);
+ EdgeToTriEdgeMap::iterator it =
+ edge_to_tri_edge_map.find(Edge(end,start));
+ if (it == edge_to_tri_edge_map.end()) {
+ // No neighbor edge exists in the edge_to_tri_edge_map, so
+ // add this edge to it.
+ edge_to_tri_edge_map[Edge(start,end)] = TriEdge(tri,edge);
+ } else {
+ // Neighbor edge found, set the two elements of _neighbors
+ // and remove edge from edge_to_tri_edge_map.
+ neighbors_ptr[3*tri + edge] = it->second.tri;
+ neighbors_ptr[3*it->second.tri + it->second.edge] = tri;
+ edge_to_tri_edge_map.erase(it);
+ }
+ }
+ }
+ }
+
+ // Note that remaining edges in the edge_to_tri_edge_map correspond to
+ // boundary edges, but the boundaries are calculated separately elsewhere.
+}
+
+void Triangulation::correct_triangles()
+{
+ int* triangles_ptr = (int*)PyArray_DATA(_triangles);
+ int* neighbors_ptr = _neighbors != 0 ? (int*)PyArray_DATA(_neighbors) : 0;
+ for (int tri = 0; tri < _ntri; ++tri) {
+ XY point0 = get_point_coords(*triangles_ptr++);
+ XY point1 = get_point_coords(*triangles_ptr++);
+ XY point2 = get_point_coords(*triangles_ptr++);
+ if ( (point1 - point0).cross_z(point2 - point0) < 0.0) {
+ // Triangle points are clockwise, so change them to anticlockwise.
+ std::swap(*(triangles_ptr-2), *(triangles_ptr-1));
+ if (neighbors_ptr)
+ std::swap(*(neighbors_ptr+3*tri+1), *(neighbors_ptr+3*tri+2));
+ }
+ }
+}
+
+const Triangulation::Boundaries& Triangulation::get_boundaries() const
+{
+ _VERBOSE("Triangulation::get_boundaries");
+ if (_boundaries.empty())
+ const_cast<Triangulation*>(this)->calculate_boundaries();
+ return _boundaries;
+}
+
+void Triangulation::get_boundary_edge(const TriEdge& triEdge,
+ int& boundary,
+ int& edge) const
+{
+ get_boundaries(); // Ensure _tri_edge_to_boundary_map has been created.
+ TriEdgeToBoundaryMap::const_iterator it =
+ _tri_edge_to_boundary_map.find(triEdge);
+ assert(it != _tri_edge_to_boundary_map.end() &&
+ "TriEdge is not on a boundary");
+ boundary = it->second.boundary;
+ edge = it->second.edge;
+}
+
+int Triangulation::get_edge_in_triangle(int tri, int point) const
+{
+ assert(tri >= 0 && tri < _ntri && "Triangle index out of bounds");
+ assert(point >= 0 && point < _npoints && "Point index out of bounds.");
+ const int* triangles_ptr = get_triangles_ptr() + 3*tri;
+ for (int edge = 0; edge < 3; ++edge) {
+ if (*triangles_ptr++ == point) return edge;
+ }
+ return -1; // point is not in triangle.
+}
+
+Py::Object Triangulation::get_edges()
+{
+ _VERBOSE("Triangulation::get_edges");
+ if (_edges == 0)
+ calculate_edges();
+ return Py::asObject(Py::new_reference_to((PyObject*)_edges));
+}
+
+int Triangulation::get_neighbor(int tri, int edge) const
+{
+ assert(tri >= 0 && tri < _ntri && "Triangle index out of bounds");
+ assert(edge >= 0 && edge < 3 && "Edge index out of bounds");
+ return get_neighbors_ptr()[3*tri + edge];
+}
+
+TriEdge Triangulation::get_neighbor_edge(int tri, int edge) const
+{
+ int neighbor_tri = get_neighbor(tri, edge);
+ if (neighbor_tri == -1)
+ return TriEdge(-1,-1);
+ else
+ return TriEdge(neighbor_tri,
+ get_edge_in_triangle(neighbor_tri,
+ get_triangle_point(tri,
+ (edge+1)%3)));
+}
+
+Py::Object Triangulation::get_neighbors()
+{
+ _VERBOSE("Triangulation::get_neighbors");
+ if (_neighbors == 0) calculate_neighbors();
+ return Py::asObject(Py::new_reference_to((PyObject*)_neighbors));
+}
+
+const int* Triangulation::get_neighbors_ptr() const
+{
+ if (_neighbors == 0)
+ const_cast<Triangulation*>(this)->calculate_neighbors();
+ return (const int*)PyArray_DATA(_neighbors);
+}
+
+int Triangulation::get_npoints() const
+{
+ return _npoints;
+}
+
+int Triangulation::get_ntri() const
+{
+ return _ntri;
+}
+
+XY Triangulation::get_point_coords(int point) const
+{
+ assert(point >= 0 && point < _npoints && "Point index out of bounds.");
+ return XY( ((const double*)PyArray_DATA(_x))[point],
+ ((const double*)PyArray_DATA(_y))[point] );
+}
+
+int Triangulation::get_triangle_point(int tri, int edge) const
+{
+ assert(tri >= 0 && tri < _ntri && "Triangle index out of bounds");
+ assert(edge >= 0 && edge < 3 && "Edge index out of bounds");
+ return get_triangles_ptr()[3*tri + edge];
+}
+
+int Triangulation::get_triangle_point(const TriEdge& tri_edge) const
+{
+ return get_triangle_point(tri_edge.tri, tri_edge.edge);
+}
+
+const int* Triangulation::get_triangles_ptr() const
+{
+ return (const int*)PyArray_DATA(_triangles);
+}
+
+void Triangulation::init_type()
+{
+ _VERBOSE("Triangulation::init_type");
+
+ behaviors().name("Triangulation");
+ behaviors().doc("Triangulation");
+
+ add_noargs_method("get_edges", &Triangulation::get_edges,
+ "get_edges()");
+ add_noargs_method("get_neighbors", &Triangulation::get_neighbors,
+ "get_neighbors()");
+ add_varargs_method("set_mask", &Triangulation::set_mask,
+ "set_mask(mask)");
+}
+
+bool Triangulation::is_masked(int tri) const
+{
+ assert(tri >= 0 && tri < _ntri && "Triangle index out of bounds.");
+ return _mask && *((bool*)PyArray_DATA(_mask) + tri);
+}
+
+Py::Object Triangulation::set_mask(const Py::Tuple &args)
+{
+ _VERBOSE("Triangulation::set_mask");
+ args.verify_length(1);
+
+ Py_XDECREF(_mask);
+ _mask = 0;
+ if (args[0] != Py::None())
+ {
+ _mask = (PyArrayObject*)PyArray_ContiguousFromObject(
+ args[0].ptr(), PyArray_BOOL, 1, 1);
+ if (_mask == 0 || PyArray_DIM(_mask,0) != PyArray_DIM(_triangles,0)) {
+ Py_XDECREF(_mask);
+ throw Py::ValueError(
+ "mask must be a 1D array with the same length as the triangles array");
+ }
+ }
+
+ // Clear derived fields so they are recalculated when needed.
+ Py_XDECREF(_edges);
+ _edges = 0;
+ Py_XDECREF(_neighbors);
+ _neighbors = 0;
+ _boundaries.clear();
+
+ return Py::None();
+}
+
+void Triangulation::write_boundaries() const
+{
+ const Boundaries& bs = get_boundaries();
+ std::cout << "Number of boundaries: " << bs.size() << std::endl;
+ for (Boundaries::const_iterator it = bs.begin(); it != bs.end(); ++it) {
+ const Boundary& b = *it;
+ std::cout << " Boundary of " << b.size() << " points: ";
+ for (Boundary::const_iterator itb = b.begin(); itb != b.end(); ++itb) {
+ std::cout << *itb << ", ";
+ }
+ std::cout << std::endl;
+ }
+}
+
+
+
+
+TriContourGenerator::TriContourGenerator(Py::Object triangulation,
+ PyArrayObject* z)
+ : _triangulation(triangulation),
+ _z(z),
+ _interior_visited(2*get_triangulation().get_ntri()),
+ _boundaries_visited(0),
+ _boundaries_used(0)
+{
+ _VERBOSE("TriContourGenerator::TriContourGenerator");
+}
+
+TriContourGenerator::~TriContourGenerator()
+{
+ _VERBOSE("TriContourGenerator::~TriContourGenerator");
+ Py_XDECREF(_z);
+}
+
+void TriContourGenerator::clear_visited_flags(bool include_boundaries)
+{
+ // Clear _interiorVisited.
+ std::fill(_interior_visited.begin(), _interior_visited.end(), false);
+
+ if (include_boundaries) {
+ if (_boundaries_visited.empty()) {
+ const Boundaries& boundaries = get_boundaries();
+
+ // Initialise _boundaries_visited.
+ _boundaries_visited.reserve(boundaries.size());
+ for (Boundaries::const_iterator it = boundaries.begin();
+ it != boundaries.end(); ++it)
+ _boundaries_visited.push_back(BoundaryVisited(it->size()));
+
+ // Initialise _boundaries_used.
+ _boundaries_used = BoundariesUsed(boundaries.size());
+ }
+
+ // Clear _boundaries_visited.
+ for (BoundariesVisited::iterator it = _boundaries_visited.begin();
+ it != _boundaries_visited.end(); ++it)
+ fill(it->begin(), it->end(), false);
+
+ // Clear _boundaries_used.
+ fill(_boundaries_used.begin(), _boundaries_used.end(), false);
+ }
+}
+
+Py::Object TriContourGenerator::contour_to_segs(const Contour& contour)
+{
+ Py::List segs(contour.size());
+ for (Contour::size_type i = 0; i < contour.size(); ++i) {
+ const ContourLine& line = contour[i];
+ npy_intp dims[2] = {line.size(),2};
+ PyArrayObject* py_line = (PyArrayObject*)PyArray_SimpleNew(
+ 2, dims, PyArray_DOUBLE);
+ double* p = (double*)PyArray_DATA(py_line);
+ for (ContourLine::const_iterator it = line.begin(); it != line.end(); ++it) {
+ *p++ = it->x;
+ *p++ = it->y;
+ }
+ segs[i] = Py::asObject((PyObject*)py_line);
+ }
+ return segs;
+}
+
+Py::Object TriContourGenerator::contour_to_segs_and_kinds(const Contour& contour)
+{
+ Contour::const_iterator line;
+ ContourLine::const_iterator point;
+
+ // Find total number of points in all contour lines.
+ int n_points = 0;
+ for (line = contour.begin(); line != contour.end(); ++line)
+ n_points += line->size();
+
+ // Create segs array for point coordinates.
+ npy_intp segs_dims[2] = {n_points, 2};
+ PyArrayObject* segs = (PyArrayObject*)PyArray_SimpleNew(
+ 2, segs_dims, PyArray_DOUBLE);
+ double* segs_ptr = (double*)PyArray_DATA(segs);
+
+ // Create kinds array for code types.
+ npy_intp kinds_dims[1] = {n_points};
+ PyArrayObject* kinds = (PyArrayObject*)PyArray_SimpleNew(
+ 1, kinds_dims, PyArray_UBYTE);
+ unsigned char* kinds_ptr = (unsigned char*)PyArray_DATA(kinds);
+
+ for (line = contour.begin(); line != contour.end(); ++line) {
+ for (point = line->begin(); point != line->end(); point++) {
+ *segs_ptr++ = point->x;
+ *segs_ptr++ = point->y;
+ *kinds_ptr++ = (point == line->begin() ? MOVETO : LINETO);
+ }
+ }
+
+ Py::Tuple result(2);
+ result[0] = Py::asObject((PyObject*)segs);
+ result[1] = Py::asObject((PyObject*)kinds);
+ return result;
+}
+
+Py::Object TriContourGenerator::create_contour(const Py::Tuple &args)
+{
+ _VERBOSE("TriContourGenerator::create_contour");
+ args.verify_length(1);
+
+ double level = (Py::Float)args[0];
+
+ clear_visited_flags(false);
+ Contour contour;
+
+ find_boundary_lines(contour, level);
+ find_interior_lines(contour, level, false, false);
+
+ return contour_to_segs(contour);
+}
+
+Py::Object TriContourGenerator::create_filled_contour(const Py::Tuple &args)
+{
+ _VERBOSE("TriContourGenerator::create_filled_contour");
+ args.verify_length(2);
+
+ double lower_level = (Py::Float)args[0];
+ double upper_level = (Py::Float)args[1];
+
+ clear_visited_flags(true);
+ Contour contour;
+
+ find_boundary_lines_filled(contour, lower_level, upper_level);
+ find_interior_lines(contour, lower_level, false, true);
+ find_interior_lines(contour, upper_level, true, true);
+
+ return contour_to_segs_and_kinds(contour);
+}
+
+XY TriContourGenerator::edge_interp(int tri, int edge, const double& level)
+{
+ return interp(get_triangulation().get_triangle_point(tri, edge),
+ get_triangulation().get_triangle_point(tri, (edge+1)%3),
+ level);
+}
+
+void TriContourGenerator::find_boundary_lines(Contour& contour,
+ const double& level)
+{
+ // Traverse boundaries to find starting points for all contour lines that
+ // intersect the boundaries. For each starting point found, follow the
+ // line to its end before continuing.
+ const Triangulation& triang = get_triangulation();
+ const Boundaries& boundaries = get_boundaries();
+ for (Boundaries::const_iterator it = boundaries.begin();
+ it != boundaries.end(); ++it) {
+ const Boundary& boundary = *it;
+ bool startAbove, endAbove = false;
+ for (Boundary::const_iterator itb = boundary.begin();
+ itb != boundary.end(); ++itb) {
+ if (itb == boundary.begin())
+ startAbove = get_z(triang.get_triangle_point(*itb)) >= level;
+ else
+ startAbove = endAbove;
+ endAbove = get_z(triang.get_triangle_point(itb->tri,
+ (itb->edge+1)%3)) >= level;
+ if (startAbove && !endAbove) {
+ // This boundary edge is the start point for a contour line,
+ // so follow the line.
+ contour.push_back(ContourLine());
+ ContourLine& contour_line = contour.back();
+ TriEdge tri_edge = *itb;
+ follow_interior(contour_line, tri_edge, true, level, false);
+ }
+ }
+ }
+}
+
+void TriContourGenerator::find_boundary_lines_filled(Contour& contour,
+ const double& lower_level,
+ const double& upper_level)
+{
+ // Traverse boundaries to find starting points for all contour lines that
+ // intersect the boundaries. For each starting point found, follow the
+ // line to its end before continuing.
+ const Triangulation& triang = get_triangulation();
+ const Boundaries& boundaries = get_boundaries();
+ for (Boundaries::size_type i = 0; i < boundaries.size(); ++i) {
+ const Boundary& boundary = boundaries[i];
+ for (Boundary::size_type j = 0; j < boundary.size(); ++j) {
+ if (!_boundaries_visited[i][j]) {
+ // z values of start and end of this boundary edge.
+ double z_start = get_z(triang.get_triangle_point(boundary[j]));
+ double z_end = get_z(triang.get_triangle_point(
+ boundary[j].tri, (boundary[j].edge+1)%3));
+
+ // Does this boundary edge's z increase through upper level
+ // and/or decrease through lower level?
+ bool incr_upper = (z_start < upper_level && z_end >= upper_level);
+ bool decr_lower = (z_start >= lower_level && z_end < lower_level);
+
+ if (decr_lower || incr_upper) {
+ // Start point for contour line, so follow it.
+ contour.push_back(ContourLine());
+ ContourLine& contour_line = contour.back();
+ TriEdge start_tri_edge = boundary[j];
+ TriEdge tri_edge = start_tri_edge;
+
+ // Traverse interior and boundaries until return to start.
+ bool on_upper = incr_upper;
+ do {
+ follow_interior(contour_line, tri_edge, true,
+ on_upper ? upper_level : lower_level, on_upper);
+ on_upper = follow_boundary(contour_line, tri_edge,
+ lower_level, upper_level, on_upper);
+ } while (tri_edge != start_tri_edge);
+
+ // Filled contour lines must not have same first and last
+ // points.
+ if (contour_line.size() > 1 &&
+ contour_line.front() == contour_line.back())
+ contour_line.pop_back();
+ }
+ }
+ }
+ }
+
+ // Add full boundaries that lie between the lower and upper levels. These
+ // are boundaries that have not been touched by an internal contour line
+ // which are stored in _boundaries_used.
+ for (Boundaries::size_type i = 0; i < boundaries.size(); ++i) {
+ if (!_boundaries_used[i]) {
+ const Boundary& boundary = boundaries[i];
+ double z = get_z(triang.get_triangle_point(boundary[0]));
+ if (z >= lower_level && z < upper_level) {
+ contour.push_back(ContourLine());
+ ContourLine& contour_line = contour.back();
+ for (Boundary::size_type j = 0; j < boundary.size(); ++j)
+ contour_line.push_back(triang.get_point_coords(
+ triang.get_triangle_point(boundary[j])));
+ }
+ }
+ }
+}
+
+void TriContourGenerator::find_interior_lines(Contour& contour,
+ const double& level,
+ bool on_upper,
+ bool filled)
+{
+ const Triangulation& triang = get_triangulation();
+ int ntri = triang.get_ntri();
+ for (int tri = 0; tri < ntri; ++tri) {
+ int visited_index = (on_upper ? tri+ntri : tri);
+
+ if (_interior_visited[visited_index] || triang.is_masked(tri))
+ continue; // Triangle has already been visited or is masked.
+
+ _interior_visited[visited_index] = true;
+
+ // Determine edge via which to leave this triangle.
+ int edge = get_exit_edge(tri, level, on_upper);
+ assert(edge >= -1 && edge < 3 && "Invalid exit edge");
+ if (edge == -1)
+ continue; // Contour does not pass through this triangle.
+
+ // Found start of new contour line loop.
+ contour.push_back(ContourLine());
+ ContourLine& contour_line = contour.back();
+ TriEdge tri_edge = triang.get_neighbor_edge(tri, edge);
+ follow_interior(contour_line, tri_edge, false, level, on_upper);
+
+ if (!filled)
+ // Non-filled contour lines must be closed.
+ contour_line.push_back(contour_line.front());
+ else if (contour_line.size() > 1 &&
+ contour_line.front() == contour_line.back())
+ // Filled contour lines must not have same first and last points.
+ contour_line.pop_back();
+ }
+}
+
+bool TriContourGenerator::follow_boundary(ContourLine& contour_line,
+ TriEdge& tri_edge,
+ const double& lower_level,
+ const double& upper_level,
+ bool on_upper)
+{
+ const Triangulation& triang = get_triangulation();
+ const Boundaries& boundaries = get_boundaries();
+
+ // Have TriEdge to start at, need equivalent boundary edge.
+ int boundary, edge;
+ triang.get_boundary_edge(tri_edge, boundary, edge);
+ _boundaries_used[boundary] = true;
+
+ bool stop = false;
+ bool first_edge = true;
+ double z_start, z_end = 0;
+ while (!stop)
+ {
+ assert(!_boundaries_visited[boundary][edge] && "Boundary already visited");
+ _boundaries_visited[boundary][edge] = true;
+
+ // z values of start and end points of boundary edge.
+ if (first_edge)
+ z_start = get_z(triang.get_triangle_point(tri_edge));
+ else
+ z_start = z_end;
+ z_end = get_z(triang.get_triangle_point(tri_edge.tri,
+ (tri_edge.edge+1)%3));
+
+ if (z_end > z_start) { // z increasing.
+ if (!(!on_upper && first_edge) &&
+ z_end >= lower_level && z_start < lower_level) {
+ stop = true;
+ on_upper = false;
+ } else if (z_end >= upper_level && z_start < upper_level) {
+ stop = true;
+ on_upper = true;
+ }
+ } else { // z decreasing.
+ if (!(on_upper && first_edge) &&
+ z_start >= upper_level && z_end < upper_level) {
+ stop = true;
+ on_upper = true;
+ } else if (z_start >= lower_level && z_end < lower_level) {
+ stop = true;
+ on_upper = false;
+ }
+ }
+
+ first_edge = false;
+
+ if (!stop) {
+ // Move to next boundary edge, adding point to contour line.
+ edge = (edge+1) % (int)boundaries[boundary].size();
+ tri_edge = boundaries[boundary][edge];
+ contour_line.push_back(triang.get_point_coords(
+ triang.get_triangle_point(tri_edge)));
+ }
+ }
+
+ return on_upper;
+}
+
+void TriContourGenerator::follow_interior(ContourLine& contour_line,
+ TriEdge& tri_edge,
+ bool end_on_boundary,
+ const double& level,
+ bool on_upper)
+{
+ int& tri = tri_edge.tri;
+ int& edge = tri_edge.edge;
+
+ // Initial point.
+ contour_line.push_back(edge_interp(tri, edge, level));
+
+ while (true) {
+ int visited_index = tri;
+ if (on_upper)
+ visited_index += get_triangulation().get_ntri();
+
+ // Check for end not on boundary.
+ if (!end_on_boundary && _interior_visited[visited_index])
+ break; // Reached start point, so return.
+
+ // Determine edge by which to leave this triangle.
+ edge = get_exit_edge(tri, level, on_upper);
+ assert(edge >= 0 && edge < 3 && "Invalid exit edge");
+
+ _interior_visited[visited_index] = true;
+
+ // Append new point to point set.
+ assert(edge >= 0 && edge < 3 && "Invalid triangle edge");
+ contour_line.push_back(edge_interp(tri, edge, level));
+
+ // Move to next triangle.
+ TriEdge next_tri_edge = get_triangulation().get_neighbor_edge(tri,edge);
+
+ // Check if ending on a boundary.
+ if (end_on_boundary && next_tri_edge.tri == -1)
+ break;
+
+ tri_edge = next_tri_edge;
+ assert(tri_edge.tri != -1 && "Invalid triangle for internal loop");
+ }
+}
+
+const TriContourGenerator::Boundaries& TriContourGenerator::get_boundaries() const
+{
+ return get_triangulation().get_boundaries();
+}
+
+int TriContourGenerator::get_exit_edge(int tri,
+ const double& level,
+ bool on_upper) const
+{
+ assert(tri >= 0 && tri < get_triangulation().get_ntri() &&
+ "Triangle index out of bounds.");
+
+ unsigned int config =
+ (get_z(get_triangulation().get_triangle_point(tri, 0)) >= level) |
+ (get_z(get_triangulation().get_triangle_point(tri, 1)) >= level) << 1 |
+ (get_z(get_triangulation().get_triangle_point(tri, 2)) >= level) << 2;
+
+ if (on_upper) config = 7-config;
+
+ switch (config) {
+ case 0: return -1;
+ case 1: return 2;
+ case 2: return 0;
+ case 3: return 2;
+ case 4: return 1;
+ case 5: return 1;
+ case 6: return 0;
+ case 7: return -1;
+ default: assert(0 && "Invalid config value"); return -1;
+ }
+}
+
+const Triangulation& TriContourGenerator::get_triangulation() const
+{
+ return *(Triangulation*)_triangulation.ptr();
+}
+
+const double& TriContourGenerator::get_z(int point) const
+{
+ assert(point >= 0 && point < get_triangulation().get_npoints() &&
+ "Point index out of bounds.");
+ return ((const double*)PyArray_DATA(_z))[point];
+}
+
+void TriContourGenerator::init_type()
+{
+ _VERBOSE("TriContourGenerator::init_type");
+
+ behaviors().name("TriContourGenerator");
+ behaviors().doc("TriContourGenerator");
+
+ add_varargs_method("create_contour",
+ &TriContourGenerator::create_contour,
+ "create_contour(level)");
+ add_varargs_method("create_filled_contour",
+ &TriContourGenerator::create_filled_contour,
+ "create_filled_contour(lower_level, upper_level)");
+}
+
+XY TriContourGenerator::interp(int point1,
+ int point2,
+ const double& level) const
+{
+ assert(point1 >= 0 && point1 < get_triangulation().get_npoints() &&
+ "Point index 1 out of bounds.");
+ assert(point2 >= 0 && point2 < get_triangulation().get_npoints() &&
+ "Point index 2 out of bounds.");
+ assert(point1 != point2 && "Identical points");
+ double fraction = (get_z(point2) - level) / (get_z(point2) - get_z(point1));
+ return get_triangulation().get_point_coords(point1)*fraction +
+ get_triangulation().get_point_coords(point2)*(1.0 - fraction);
+}
+
+
+
+
+
+#if defined(_MSC_VER)
+DL_EXPORT(void)
+#elif defined(__cplusplus)
+extern "C" void
+#else
+void
+#endif
+init_tri()
+{
+ static TriModule* triModule = new TriModule;
+ import_array();
+}
+
+TriModule::TriModule()
+ : Py::ExtensionModule<TriModule>("tri")
+{
+ Triangulation::init_type();
+ TriContourGenerator::init_type();
+
+ add_varargs_method("Triangulation", &TriModule::new_triangulation,
+ "Create and return new C++ Triangulation object");
+ add_varargs_method("TriContourGenerator", &TriModule::new_tricontourgenerator,
+ "Create and return new C++ TriContourGenerator object");
+
+ initialize("Module for unstructured triangular grids");
+}
+
+Py::Object TriModule::new_triangulation(const Py::Tuple &args)
+{
+ _VERBOSE("TriModule::new_triangulation");
+ args.verify_length(6);
+
+ // x and y.
+ PyArrayObject* x = (PyArrayObject*)PyArray_ContiguousFromObject(
+ args[0].ptr(), PyArray_DOUBLE, 1, 1);
+ PyArrayObject* y = (PyArrayObject*)PyArray_ContiguousFromObject(
+ args[1].ptr(), PyArray_DOUBLE, 1, 1);
+ if (x == 0 || y == 0 || PyArray_DIM(x,0) != PyArray_DIM(y,0)) {
+ Py_XDECREF(x);
+ Py_XDECREF(y);
+ throw Py::ValueError("x and y must be 1D arrays of the same length");
+ }
+
+ // triangles.
+ PyArrayObject* triangles = (PyArrayObject*)PyArray_ContiguousFromObject(
+ args[2].ptr(), PyArray_INT, 2, 2);
+ if (triangles == 0 || PyArray_DIM(triangles,1) != 3) {
+ Py_XDECREF(x);
+ Py_XDECREF(y);
+ Py_XDECREF(triangles);
+ throw Py::ValueError("triangles must be a 2D array of shape (?,3)");
+ }
+
+ // Optiona...
[truncated message content] |