@@ -61,6 +61,29 @@ class Triangulation(object):
6161
6262 hull -- list of point_id's giving the nodes which form the convex hull
6363 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.
6487 """
6588 def __init__ (self , x , y ):
6689 self .x = np .asarray (x , dtype = np .float64 )
@@ -70,38 +93,48 @@ def __init__(self, x, y):
7093 raise ValueError ("x,y must be equal-length 1-D arrays" )
7194
7295 self .old_shape = self .x .shape
73- j_unique = self ._collapse_duplicate_points ()
96+ duplicates = self ._get_duplicate_point_indices ()
7497
75- if j_unique . shape != self . x . shape :
98+ if len ( duplicates ) > 0 :
7699 warnings .warn (
77100 "Input data contains duplicate x,y points; some values are ignored." ,
78101 DuplicatePointWarning ,
79102 )
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 )
81107 self .x = self .x [self .j_unique ]
82108 self .y = self .y [self .j_unique ]
109+
110+ # Create map of point indices used by delaunay to those used
111+ # by client.
112+ self ._client_point_index_map = np .delete (np .arange (self .old_shape [0 ]),
113+ duplicates )
83114 else :
84115 self .j_unique = None
85-
116+ self . _client_point_index_map = None
86117
87118 self .circumcenters , self .edge_db , self .triangle_nodes , \
88119 self .triangle_neighbors = delaunay (self .x , self .y )
89120
90121 self .hull = self ._compute_convex_hull ()
91122
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.
97126 """
98- # Find the indices of the unique entries
127+ # Indices of sorted x,y points.
99128 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 ),
103134 ])
104- return j_sorted [mask_unique ]
135+
136+ # Array of duplicate point indices, in no particular order.
137+ return j_sorted [mask_duplicates ]
105138
106139 def _compute_convex_hull (self ):
107140 """Extract the convex hull from the triangulation information.
@@ -129,6 +162,16 @@ def _compute_convex_hull(self):
129162
130163 return hull
131164
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+
132175 def linear_interpolator (self , z , default_value = np .nan ):
133176 """Get an object which can interpolate within the convex hull by
134177 assigning a plane to each triangle.
0 commit comments