@@ -61,6 +61,29 @@ class Triangulation(object):
61
61
62
62
hull -- list of point_id's giving the nodes which form the convex hull
63
63
of the point set. This list is sorted in counter-clockwise order.
64
+
65
+ Duplicate points.
66
+ If there are no duplicate points, Triangulation stores the specified
67
+ x and y arrays and there is no difference between the client's and
68
+ Triangulation's understanding of point indices used in edge_db,
69
+ triangle_nodes and hull.
70
+
71
+ If there are duplicate points, they are removed from the stored
72
+ self.x and self.y as the underlying delaunay code cannot deal with
73
+ duplicates. len(self.x) is therefore equal to len(x) minus the
74
+ number of duplicate points. Triangulation's edge_db, triangle_nodes
75
+ and hull refer to point indices in self.x and self.y, for internal
76
+ consistency within Triangulation and the corresponding Interpolator
77
+ classes. Client code must take care to deal with this in one of
78
+ two ways:
79
+
80
+ 1. Ignore the x,y it specified in Triangulation's constructor and
81
+ use triangulation.x and triangulation.y instead, as these are
82
+ consistent with edge_db, triangle_nodes and hull.
83
+
84
+ 2. If using the x,y the client specified then edge_db,
85
+ triangle_nodes and hull should be passed through the function
86
+ to_client_point_indices() first.
64
87
"""
65
88
def __init__ (self , x , y ):
66
89
self .x = np .asarray (x , dtype = np .float64 )
@@ -70,38 +93,48 @@ def __init__(self, x, y):
70
93
raise ValueError ("x,y must be equal-length 1-D arrays" )
71
94
72
95
self .old_shape = self .x .shape
73
- j_unique = self ._collapse_duplicate_points ()
96
+ duplicates = self ._get_duplicate_point_indices ()
74
97
75
- if j_unique . shape != self . x . shape :
98
+ if len ( duplicates ) > 0 :
76
99
warnings .warn (
77
100
"Input data contains duplicate x,y points; some values are ignored." ,
78
101
DuplicatePointWarning ,
79
102
)
80
- self .j_unique = j_unique
103
+
104
+ # self.j_unique is the array of non-duplicate indices, in
105
+ # increasing order.
106
+ self .j_unique = np .delete (np .arange (len (self .x )), duplicates )
81
107
self .x = self .x [self .j_unique ]
82
108
self .y = self .y [self .j_unique ]
83
109
else :
84
110
self .j_unique = None
85
111
112
+ # If there are duplicate points, need a map of point indices used
113
+ # by delaunay to those used by client. If there are no duplicate
114
+ # points then the map is not needed. Either way, the map is
115
+ # conveniently the same as j_unique, so share it.
116
+ self ._client_point_index_map = self .j_unique
86
117
87
118
self .circumcenters , self .edge_db , self .triangle_nodes , \
88
119
self .triangle_neighbors = delaunay (self .x , self .y )
89
120
90
121
self .hull = self ._compute_convex_hull ()
91
122
92
- def _collapse_duplicate_points (self ):
93
- """Generate index array that picks out unique x,y points.
94
-
95
- This appears to be required by the underlying delaunay triangulation
96
- code.
123
+ def _get_duplicate_point_indices (self ):
124
+ """Return array of indices of x,y points that are duplicates of
125
+ previous points. Indices are in no particular order.
97
126
"""
98
- # Find the indices of the unique entries
127
+ # Indices of sorted x,y points.
99
128
j_sorted = np .lexsort (keys = (self .x , self .y ))
100
- mask_unique = np .hstack ([
101
- True ,
102
- (np .diff (self .x [j_sorted ]) != 0 ) | (np .diff (self .y [j_sorted ]) != 0 ),
129
+
130
+ # Mask, in j_sorted order, which is True for duplicate points.
131
+ mask_duplicates = np .hstack ([
132
+ False ,
133
+ (np .diff (self .x [j_sorted ]) == 0 ) & (np .diff (self .y [j_sorted ]) == 0 ),
103
134
])
104
- return j_sorted [mask_unique ]
135
+
136
+ # Array of duplicate point indices, in no particular order.
137
+ return j_sorted [mask_duplicates ]
105
138
106
139
def _compute_convex_hull (self ):
107
140
"""Extract the convex hull from the triangulation information.
@@ -129,6 +162,16 @@ def _compute_convex_hull(self):
129
162
130
163
return hull
131
164
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
+
132
175
def linear_interpolator (self , z , default_value = np .nan ):
133
176
"""Get an object which can interpolate within the convex hull by
134
177
assigning a plane to each triangle.
0 commit comments