Permalink
Browse files

Merge remote-tracking branch 'origin/v1.1.x'

Conflicts:
	lib/matplotlib/delaunay/triangulate.py
  • Loading branch information...
2 parents 67df281 + 97f7130 commit df3167787fe5dbb901949d876becd55ef3532eb5 @mdboom committed Jun 11, 2012
@@ -63,6 +63,29 @@ class Triangulation(object):
hull -- list of point_id's giving the nodes which form the convex hull
of the point set. This list is sorted in counter-clockwise order.
+
+ Duplicate points.
+ If there are no duplicate points, Triangulation stores the specified
+ x and y arrays and there is no difference between the client's and
+ Triangulation's understanding of point indices used in edge_db,
+ triangle_nodes and hull.
+
+ If there are duplicate points, they are removed from the stored
+ self.x and self.y as the underlying delaunay code cannot deal with
+ duplicates. len(self.x) is therefore equal to len(x) minus the
+ number of duplicate points. Triangulation's edge_db, triangle_nodes
+ and hull refer to point indices in self.x and self.y, for internal
+ consistency within Triangulation and the corresponding Interpolator
+ classes. Client code must take care to deal with this in one of
+ two ways:
+
+ 1. Ignore the x,y it specified in Triangulation's constructor and
+ use triangulation.x and triangulation.y instead, as these are
+ consistent with edge_db, triangle_nodes and hull.
+
+ 2. If using the x,y the client specified then edge_db,
+ triangle_nodes and hull should be passed through the function
+ to_client_point_indices() first.
"""
def __init__(self, x, y):
self.x = np.asarray(x, dtype=np.float64)
@@ -72,38 +95,46 @@ def __init__(self, x, y):
raise ValueError("x,y must be equal-length 1-D arrays")
self.old_shape = self.x.shape
- j_unique = self._collapse_duplicate_points()
+ duplicates = self._get_duplicate_point_indices()
- if j_unique.shape != self.x.shape:
+ if len(duplicates) > 0:
warnings.warn(
"Input data contains duplicate x,y points; some values are ignored.",
DuplicatePointWarning,
)
- self.j_unique = j_unique
+
+ # self.j_unique is the array of non-duplicate indices, in
+ # increasing order.
+ self.j_unique = np.delete(np.arange(len(self.x)), duplicates)
self.x = self.x[self.j_unique]
self.y = self.y[self.j_unique]
else:
self.j_unique = None
+ # If there are duplicate points, need a map of point indices used
+ # by delaunay to those used by client. If there are no duplicate
+ # points then the map is not needed. Either way, the map is
+ # conveniently the same as j_unique, so share it.
+ self._client_point_index_map = self.j_unique
self.circumcenters, self.edge_db, self.triangle_nodes, \
self.triangle_neighbors = delaunay(self.x, self.y)
self.hull = self._compute_convex_hull()
- def _collapse_duplicate_points(self):
- """Generate index array that picks out unique x,y points.
-
- This appears to be required by the underlying delaunay triangulation
- code.
+ def _get_duplicate_point_indices(self):
+ """Return array of indices of x,y points that are duplicates of
+ previous points. Indices are in no particular order.
"""
- # Find the indices of the unique entries
+ # Indices of sorted x,y points.
j_sorted = np.lexsort(keys=(self.x, self.y))
- mask_unique = np.hstack([
- True,
- (np.diff(self.x[j_sorted]) != 0) | (np.diff(self.y[j_sorted]) != 0),
+ mask_duplicates = np.hstack([
+ False,
+ (np.diff(self.x[j_sorted]) == 0) & (np.diff(self.y[j_sorted]) == 0),
])
- return j_sorted[mask_unique]
+
+ # Array of duplicate point indices, in no particular order.
+ return j_sorted[mask_duplicates]
def _compute_convex_hull(self):
"""Extract the convex hull from the triangulation information.
@@ -131,6 +162,16 @@ def _compute_convex_hull(self):
return hull
+ def to_client_point_indices(self, array):
+ """Converts any array of point indices used within this class to
+ refer to point indices within the (x,y) arrays specified in the
+ constructor before duplicates were removed.
+ """
+ if self._client_point_index_map is not None:
+ return self._client_point_index_map[array]
+ else:
+ return array
+
def linear_interpolator(self, z, default_value=np.nan):
"""Get an object which can interpolate within the convex hull by
assigning a plane to each triangle.
@@ -1030,13 +1030,14 @@ def __init__(self, figure, xytip, xybase, width=4, frac=0.1, headwidth=12, **kwa
%(Patch)s
"""
- self.figure = figure
self.xytip = xytip
self.xybase = xybase
self.width = width
self.frac = frac
self.headwidth = headwidth
Patch.__init__(self, **kwargs)
+ # Set self.figure after Patch.__init__, since it sets self.figure to None
+ self.figure = figure
def get_path(self):
# Since this is dpi dependent, we need to recompute the path
@@ -4239,6 +4240,3 @@ def draw(self, renderer):
return
FancyArrowPatch.draw(self, renderer)
-
-
-
@@ -0,0 +1,96 @@
+import numpy as np
+import matplotlib.tri as mtri
+import matplotlib.delaunay as mdel
+from nose.tools import assert_equal
+from numpy.testing import assert_array_equal, assert_array_almost_equal
+
+def test_delaunay():
+ # No duplicate points.
+ x = [0,1,1,0]
+ y = [0,0,1,1]
+ npoints = 4
+ ntriangles = 2
+ nedges = 5
+
+ # Without duplicate points, mpl calls delaunay triangulation and
+ # does not modify it.
+ mpl_triang = mtri.Triangulation(x,y)
+ del_triang = mdel.Triangulation(x,y)
+
+ # Points - floating point.
+ assert_array_almost_equal(mpl_triang.x, x)
+ assert_array_almost_equal(mpl_triang.x, del_triang.x)
+ assert_array_almost_equal(mpl_triang.y, y)
+ assert_array_almost_equal(mpl_triang.y, del_triang.y)
+
+ # Triangles - integers.
+ assert_equal(len(mpl_triang.triangles), ntriangles)
+ assert_equal(np.min(mpl_triang.triangles), 0)
+ assert_equal(np.max(mpl_triang.triangles), npoints-1)
+ assert_array_equal(mpl_triang.triangles, del_triang.triangle_nodes)
+
+ # Edges - integers.
+ assert_equal(len(mpl_triang.edges), nedges)
+ assert_equal(np.min(mpl_triang.edges), 0)
+ assert_equal(np.max(mpl_triang.edges), npoints-1)
+ assert_array_equal(mpl_triang.edges, del_triang.edge_db)
+
+def test_delaunay_duplicate_points():
+ # Issue 838.
+ import warnings
+
+ # Index 2 is the same as index 0.
+ x = [0,1,0,1,0]
+ y = [0,0,0,1,1]
+ duplicate_index = 2
+ npoints = 4 # Number of non-duplicate points.
+ nduplicates = 1
+ ntriangles = 2
+ nedges = 5
+
+ # With duplicate points, mpl calls delaunay triangulation but
+ # modified returned arrays.
+ warnings.simplefilter("ignore") # Ignore DuplicatePointWarning.
+ mpl_triang = mtri.Triangulation(x,y)
+ del_triang = mdel.Triangulation(x,y)
+ warnings.resetwarnings()
+
+ # Points - floating point.
+ assert_equal(len(mpl_triang.x), npoints + nduplicates)
+ assert_equal(len(del_triang.x), npoints)
+ assert_array_almost_equal(mpl_triang.x, x)
+ assert_array_almost_equal(del_triang.x[:duplicate_index], x[:duplicate_index])
+ assert_array_almost_equal(del_triang.x[duplicate_index:], x[duplicate_index+1:])
+
+ assert_equal(len(mpl_triang.y), npoints + nduplicates)
+ assert_equal(len(del_triang.y), npoints)
+ assert_array_almost_equal(mpl_triang.y, y)
+ assert_array_almost_equal(del_triang.y[:duplicate_index], y[:duplicate_index])
+ assert_array_almost_equal(del_triang.y[duplicate_index:], y[duplicate_index+1:])
+
+ # Triangles - integers.
+ assert_equal(len(mpl_triang.triangles), ntriangles)
+ assert_equal(np.min(mpl_triang.triangles), 0)
+ assert_equal(np.max(mpl_triang.triangles), npoints-1 + nduplicates)
+ assert_equal(len(del_triang.triangle_nodes), ntriangles)
+ assert_equal(np.min(del_triang.triangle_nodes), 0)
+ assert_equal(np.max(del_triang.triangle_nodes), npoints-1)
+ # Convert mpl triangle point indices to delaunay's.
+ converted_indices = np.where(mpl_triang.triangles > duplicate_index,
+ mpl_triang.triangles - nduplicates,
+ mpl_triang.triangles)
+ assert_array_equal(del_triang.triangle_nodes, converted_indices)
+
+ # Edges - integers.
+ assert_equal(len(mpl_triang.edges), nedges)
+ assert_equal(np.min(mpl_triang.edges), 0)
+ assert_equal(np.max(mpl_triang.edges), npoints-1 + nduplicates)
+ assert_equal(len(del_triang.edge_db), nedges)
+ assert_equal(np.min(del_triang.edge_db), 0)
+ assert_equal(np.max(del_triang.edge_db), npoints-1)
+ # Convert mpl edge point indices to delaunay's.
+ converted_indices = np.where(mpl_triang.edges > duplicate_index,
+ mpl_triang.edges - nduplicates,
+ mpl_triang.edges)
+ assert_array_equal(del_triang.edge_db, converted_indices)
+
@@ -70,9 +70,11 @@ def __init__(self, x, y, triangles=None, mask=None):
if triangles is None:
# No triangulation specified, so use matplotlib.delaunay.
dt = delaunay.Triangulation(self.x, self.y)
- self.triangles = np.asarray(dt.triangle_nodes, dtype=np.int32)
+ self.triangles = np.asarray(dt.to_client_point_indices(dt.triangle_nodes),
+ dtype=np.int32)
if mask is None:
- self._edges = np.asarray(dt.edge_db, dtype=np.int32)
+ self._edges = np.asarray(dt.to_client_point_indices(dt.edge_db),
+ dtype=np.int32)
# Delaunay triangle_neighbors uses different edge indexing,
# so convert.
neighbors = np.asarray(dt.triangle_neighbors, dtype=np.int32)
View
@@ -1066,8 +1066,7 @@ _image_module::fromarray2(const Py::Tuple& args)
int rgba = A->dimensions[2] == 4;
double r, g, b, alpha;
const size_t N = imo->rowsIn * imo->colsIn;
- size_t i = 0;
- while (i < N)
+ for (size_t i = 0; i < N; ++i)
{
r = *(double *)(A->data++);
g = *(double *)(A->data++);
View
@@ -203,8 +203,9 @@ void GlyphToType3::PSConvert(TTStreamWriter& stream)
/* Step thru the coutours. */
/* I believe that a contour is a detatched */
/* set of curves and lines. */
- i=j=k=0;
- while ( i < num_ctr )
+ for(i = j = k = 0;
+ i != NOMOREOUTCTR && i < num_ctr;
+ k = nextinctr(i, k), (k == NOMOREINCTR && (i = k = nextoutctr(i))))
{
// A TrueType contour consists of on-path and off-path points.
// Two consecutive on-path points are to be joined with a
@@ -224,6 +225,11 @@ void GlyphToType3::PSConvert(TTStreamWriter& stream)
}
}
+ if (points.size() == 0) {
+ // Don't try to access the last element of an empty list
+ continue;
+ }
+
// For any two consecutive off-path points, insert the implied
// on-path point.
FlaggedPoint prev = points.back();
@@ -254,44 +260,35 @@ void GlyphToType3::PSConvert(TTStreamWriter& stream)
points.push_back(points.front());
}
- // For output, a vector is more convenient than a list.
- std::vector<FlaggedPoint> points_v(points.begin(), points.end());
// The first point
stack(stream, 3);
- PSMoveto(stream, points_v.front().x, points_v.front().y);
+ PSMoveto(stream, points.front().x, points.front().y);
// Step through the remaining points
- for (size_t p = 1; p < points_v.size(); )
+ std::list<FlaggedPoint>::const_iterator it = points.begin();
+ for (it++; it != points.end(); /* incremented inside */)
{
- const FlaggedPoint& point = points_v.at(p);
+ const FlaggedPoint& point = *it;
if (point.flag == ON_PATH)
{
stack(stream, 3);
PSLineto(stream, point.x, point.y);
- p++;
+ it++;
} else {
- assert(points_v.at(p-1).flag == ON_PATH);
- assert(points_v.at(p+1).flag == ON_PATH);
+ std::list<FlaggedPoint>::const_iterator prev = it, next = it;
+ prev--;
+ next++;
+ assert(prev->flag == ON_PATH);
+ assert(next->flag == ON_PATH);
stack(stream, 7);
PSCurveto(stream,
- points_v.at(p-1).x, points_v.at(p-1).y,
+ prev->x, prev->y,
point.x, point.y,
- points_v.at(p+1).x, points_v.at(p+1).y);
- p += 2;
+ next->x, next->y);
+ it++;
+ it++;
}
}
-
- k=nextinctr(i,k);
-
- if (k==NOMOREINCTR)
- {
- i=k=nextoutctr(i);
- }
-
- if (i==NOMOREOUTCTR)
- {
- break;
- }
}
/* Now, we can fill the whole thing. */

0 comments on commit df31677

Please sign in to comment.