Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit df31677

Browse files
committed
Merge remote-tracking branch 'origin/v1.1.x'
Conflicts: lib/matplotlib/delaunay/triangulate.py
2 parents 67df281 + 97f7130 commit df31677

File tree

6 files changed

+179
-46
lines changed

6 files changed

+179
-46
lines changed

lib/matplotlib/delaunay/triangulate.py

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,29 @@ class Triangulation(object):
6363
6464
hull -- list of point_id's giving the nodes which form the convex hull
6565
of the point set. This list is sorted in counter-clockwise order.
66+
67+
Duplicate points.
68+
If there are no duplicate points, Triangulation stores the specified
69+
x and y arrays and there is no difference between the client's and
70+
Triangulation's understanding of point indices used in edge_db,
71+
triangle_nodes and hull.
72+
73+
If there are duplicate points, they are removed from the stored
74+
self.x and self.y as the underlying delaunay code cannot deal with
75+
duplicates. len(self.x) is therefore equal to len(x) minus the
76+
number of duplicate points. Triangulation's edge_db, triangle_nodes
77+
and hull refer to point indices in self.x and self.y, for internal
78+
consistency within Triangulation and the corresponding Interpolator
79+
classes. Client code must take care to deal with this in one of
80+
two ways:
81+
82+
1. Ignore the x,y it specified in Triangulation's constructor and
83+
use triangulation.x and triangulation.y instead, as these are
84+
consistent with edge_db, triangle_nodes and hull.
85+
86+
2. If using the x,y the client specified then edge_db,
87+
triangle_nodes and hull should be passed through the function
88+
to_client_point_indices() first.
6689
"""
6790
def __init__(self, x, y):
6891
self.x = np.asarray(x, dtype=np.float64)
@@ -72,38 +95,46 @@ def __init__(self, x, y):
7295
raise ValueError("x,y must be equal-length 1-D arrays")
7396

7497
self.old_shape = self.x.shape
75-
j_unique = self._collapse_duplicate_points()
98+
duplicates = self._get_duplicate_point_indices()
7699

77-
if j_unique.shape != self.x.shape:
100+
if len(duplicates) > 0:
78101
warnings.warn(
79102
"Input data contains duplicate x,y points; some values are ignored.",
80103
DuplicatePointWarning,
81104
)
82-
self.j_unique = j_unique
105+
106+
# self.j_unique is the array of non-duplicate indices, in
107+
# increasing order.
108+
self.j_unique = np.delete(np.arange(len(self.x)), duplicates)
83109
self.x = self.x[self.j_unique]
84110
self.y = self.y[self.j_unique]
85111
else:
86112
self.j_unique = None
87113

114+
# If there are duplicate points, need a map of point indices used
115+
# by delaunay to those used by client. If there are no duplicate
116+
# points then the map is not needed. Either way, the map is
117+
# conveniently the same as j_unique, so share it.
118+
self._client_point_index_map = self.j_unique
88119

89120
self.circumcenters, self.edge_db, self.triangle_nodes, \
90121
self.triangle_neighbors = delaunay(self.x, self.y)
91122

92123
self.hull = self._compute_convex_hull()
93124

94-
def _collapse_duplicate_points(self):
95-
"""Generate index array that picks out unique x,y points.
96-
97-
This appears to be required by the underlying delaunay triangulation
98-
code.
125+
def _get_duplicate_point_indices(self):
126+
"""Return array of indices of x,y points that are duplicates of
127+
previous points. Indices are in no particular order.
99128
"""
100-
# Find the indices of the unique entries
129+
# Indices of sorted x,y points.
101130
j_sorted = np.lexsort(keys=(self.x, self.y))
102-
mask_unique = np.hstack([
103-
True,
104-
(np.diff(self.x[j_sorted]) != 0) | (np.diff(self.y[j_sorted]) != 0),
131+
mask_duplicates = np.hstack([
132+
False,
133+
(np.diff(self.x[j_sorted]) == 0) & (np.diff(self.y[j_sorted]) == 0),
105134
])
106-
return j_sorted[mask_unique]
135+
136+
# Array of duplicate point indices, in no particular order.
137+
return j_sorted[mask_duplicates]
107138

108139
def _compute_convex_hull(self):
109140
"""Extract the convex hull from the triangulation information.
@@ -131,6 +162,16 @@ def _compute_convex_hull(self):
131162

132163
return hull
133164

165+
def to_client_point_indices(self, array):
166+
"""Converts any array of point indices used within this class to
167+
refer to point indices within the (x,y) arrays specified in the
168+
constructor before duplicates were removed.
169+
"""
170+
if self._client_point_index_map is not None:
171+
return self._client_point_index_map[array]
172+
else:
173+
return array
174+
134175
def linear_interpolator(self, z, default_value=np.nan):
135176
"""Get an object which can interpolate within the convex hull by
136177
assigning a plane to each triangle.

lib/matplotlib/patches.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,13 +1030,14 @@ def __init__(self, figure, xytip, xybase, width=4, frac=0.1, headwidth=12, **kwa
10301030
%(Patch)s
10311031
10321032
"""
1033-
self.figure = figure
10341033
self.xytip = xytip
10351034
self.xybase = xybase
10361035
self.width = width
10371036
self.frac = frac
10381037
self.headwidth = headwidth
10391038
Patch.__init__(self, **kwargs)
1039+
# Set self.figure after Patch.__init__, since it sets self.figure to None
1040+
self.figure = figure
10401041

10411042
def get_path(self):
10421043
# Since this is dpi dependent, we need to recompute the path
@@ -4239,6 +4240,3 @@ def draw(self, renderer):
42394240
return
42404241

42414242
FancyArrowPatch.draw(self, renderer)
4242-
4243-
4244-
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import numpy as np
2+
import matplotlib.tri as mtri
3+
import matplotlib.delaunay as mdel
4+
from nose.tools import assert_equal
5+
from numpy.testing import assert_array_equal, assert_array_almost_equal
6+
7+
def test_delaunay():
8+
# No duplicate points.
9+
x = [0,1,1,0]
10+
y = [0,0,1,1]
11+
npoints = 4
12+
ntriangles = 2
13+
nedges = 5
14+
15+
# Without duplicate points, mpl calls delaunay triangulation and
16+
# does not modify it.
17+
mpl_triang = mtri.Triangulation(x,y)
18+
del_triang = mdel.Triangulation(x,y)
19+
20+
# Points - floating point.
21+
assert_array_almost_equal(mpl_triang.x, x)
22+
assert_array_almost_equal(mpl_triang.x, del_triang.x)
23+
assert_array_almost_equal(mpl_triang.y, y)
24+
assert_array_almost_equal(mpl_triang.y, del_triang.y)
25+
26+
# Triangles - integers.
27+
assert_equal(len(mpl_triang.triangles), ntriangles)
28+
assert_equal(np.min(mpl_triang.triangles), 0)
29+
assert_equal(np.max(mpl_triang.triangles), npoints-1)
30+
assert_array_equal(mpl_triang.triangles, del_triang.triangle_nodes)
31+
32+
# Edges - integers.
33+
assert_equal(len(mpl_triang.edges), nedges)
34+
assert_equal(np.min(mpl_triang.edges), 0)
35+
assert_equal(np.max(mpl_triang.edges), npoints-1)
36+
assert_array_equal(mpl_triang.edges, del_triang.edge_db)
37+
38+
def test_delaunay_duplicate_points():
39+
# Issue 838.
40+
import warnings
41+
42+
# Index 2 is the same as index 0.
43+
x = [0,1,0,1,0]
44+
y = [0,0,0,1,1]
45+
duplicate_index = 2
46+
npoints = 4 # Number of non-duplicate points.
47+
nduplicates = 1
48+
ntriangles = 2
49+
nedges = 5
50+
51+
# With duplicate points, mpl calls delaunay triangulation but
52+
# modified returned arrays.
53+
warnings.simplefilter("ignore") # Ignore DuplicatePointWarning.
54+
mpl_triang = mtri.Triangulation(x,y)
55+
del_triang = mdel.Triangulation(x,y)
56+
warnings.resetwarnings()
57+
58+
# Points - floating point.
59+
assert_equal(len(mpl_triang.x), npoints + nduplicates)
60+
assert_equal(len(del_triang.x), npoints)
61+
assert_array_almost_equal(mpl_triang.x, x)
62+
assert_array_almost_equal(del_triang.x[:duplicate_index], x[:duplicate_index])
63+
assert_array_almost_equal(del_triang.x[duplicate_index:], x[duplicate_index+1:])
64+
65+
assert_equal(len(mpl_triang.y), npoints + nduplicates)
66+
assert_equal(len(del_triang.y), npoints)
67+
assert_array_almost_equal(mpl_triang.y, y)
68+
assert_array_almost_equal(del_triang.y[:duplicate_index], y[:duplicate_index])
69+
assert_array_almost_equal(del_triang.y[duplicate_index:], y[duplicate_index+1:])
70+
71+
# Triangles - integers.
72+
assert_equal(len(mpl_triang.triangles), ntriangles)
73+
assert_equal(np.min(mpl_triang.triangles), 0)
74+
assert_equal(np.max(mpl_triang.triangles), npoints-1 + nduplicates)
75+
assert_equal(len(del_triang.triangle_nodes), ntriangles)
76+
assert_equal(np.min(del_triang.triangle_nodes), 0)
77+
assert_equal(np.max(del_triang.triangle_nodes), npoints-1)
78+
# Convert mpl triangle point indices to delaunay's.
79+
converted_indices = np.where(mpl_triang.triangles > duplicate_index,
80+
mpl_triang.triangles - nduplicates,
81+
mpl_triang.triangles)
82+
assert_array_equal(del_triang.triangle_nodes, converted_indices)
83+
84+
# Edges - integers.
85+
assert_equal(len(mpl_triang.edges), nedges)
86+
assert_equal(np.min(mpl_triang.edges), 0)
87+
assert_equal(np.max(mpl_triang.edges), npoints-1 + nduplicates)
88+
assert_equal(len(del_triang.edge_db), nedges)
89+
assert_equal(np.min(del_triang.edge_db), 0)
90+
assert_equal(np.max(del_triang.edge_db), npoints-1)
91+
# Convert mpl edge point indices to delaunay's.
92+
converted_indices = np.where(mpl_triang.edges > duplicate_index,
93+
mpl_triang.edges - nduplicates,
94+
mpl_triang.edges)
95+
assert_array_equal(del_triang.edge_db, converted_indices)
96+

lib/matplotlib/tri/triangulation.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ def __init__(self, x, y, triangles=None, mask=None):
7070
if triangles is None:
7171
# No triangulation specified, so use matplotlib.delaunay.
7272
dt = delaunay.Triangulation(self.x, self.y)
73-
self.triangles = np.asarray(dt.triangle_nodes, dtype=np.int32)
73+
self.triangles = np.asarray(dt.to_client_point_indices(dt.triangle_nodes),
74+
dtype=np.int32)
7475
if mask is None:
75-
self._edges = np.asarray(dt.edge_db, dtype=np.int32)
76+
self._edges = np.asarray(dt.to_client_point_indices(dt.edge_db),
77+
dtype=np.int32)
7678
# Delaunay triangle_neighbors uses different edge indexing,
7779
# so convert.
7880
neighbors = np.asarray(dt.triangle_neighbors, dtype=np.int32)

src/_image.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1066,8 +1066,7 @@ _image_module::fromarray2(const Py::Tuple& args)
10661066
int rgba = A->dimensions[2] == 4;
10671067
double r, g, b, alpha;
10681068
const size_t N = imo->rowsIn * imo->colsIn;
1069-
size_t i = 0;
1070-
while (i < N)
1069+
for (size_t i = 0; i < N; ++i)
10711070
{
10721071
r = *(double *)(A->data++);
10731072
g = *(double *)(A->data++);

ttconv/pprdrv_tt2.cpp

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -203,8 +203,9 @@ void GlyphToType3::PSConvert(TTStreamWriter& stream)
203203
/* Step thru the coutours. */
204204
/* I believe that a contour is a detatched */
205205
/* set of curves and lines. */
206-
i=j=k=0;
207-
while ( i < num_ctr )
206+
for(i = j = k = 0;
207+
i != NOMOREOUTCTR && i < num_ctr;
208+
k = nextinctr(i, k), (k == NOMOREINCTR && (i = k = nextoutctr(i))))
208209
{
209210
// A TrueType contour consists of on-path and off-path points.
210211
// Two consecutive on-path points are to be joined with a
@@ -224,6 +225,11 @@ void GlyphToType3::PSConvert(TTStreamWriter& stream)
224225
}
225226
}
226227

228+
if (points.size() == 0) {
229+
// Don't try to access the last element of an empty list
230+
continue;
231+
}
232+
227233
// For any two consecutive off-path points, insert the implied
228234
// on-path point.
229235
FlaggedPoint prev = points.back();
@@ -254,44 +260,35 @@ void GlyphToType3::PSConvert(TTStreamWriter& stream)
254260
points.push_back(points.front());
255261
}
256262

257-
// For output, a vector is more convenient than a list.
258-
std::vector<FlaggedPoint> points_v(points.begin(), points.end());
259263
// The first point
260264
stack(stream, 3);
261-
PSMoveto(stream, points_v.front().x, points_v.front().y);
265+
PSMoveto(stream, points.front().x, points.front().y);
262266

263267
// Step through the remaining points
264-
for (size_t p = 1; p < points_v.size(); )
268+
std::list<FlaggedPoint>::const_iterator it = points.begin();
269+
for (it++; it != points.end(); /* incremented inside */)
265270
{
266-
const FlaggedPoint& point = points_v.at(p);
271+
const FlaggedPoint& point = *it;
267272
if (point.flag == ON_PATH)
268273
{
269274
stack(stream, 3);
270275
PSLineto(stream, point.x, point.y);
271-
p++;
276+
it++;
272277
} else {
273-
assert(points_v.at(p-1).flag == ON_PATH);
274-
assert(points_v.at(p+1).flag == ON_PATH);
278+
std::list<FlaggedPoint>::const_iterator prev = it, next = it;
279+
prev--;
280+
next++;
281+
assert(prev->flag == ON_PATH);
282+
assert(next->flag == ON_PATH);
275283
stack(stream, 7);
276284
PSCurveto(stream,
277-
points_v.at(p-1).x, points_v.at(p-1).y,
285+
prev->x, prev->y,
278286
point.x, point.y,
279-
points_v.at(p+1).x, points_v.at(p+1).y);
280-
p += 2;
287+
next->x, next->y);
288+
it++;
289+
it++;
281290
}
282291
}
283-
284-
k=nextinctr(i,k);
285-
286-
if (k==NOMOREINCTR)
287-
{
288-
i=k=nextoutctr(i);
289-
}
290-
291-
if (i==NOMOREOUTCTR)
292-
{
293-
break;
294-
}
295292
}
296293

297294
/* Now, we can fill the whole thing. */

0 commit comments

Comments
 (0)