Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/matplotlib/tri/_triangulation.py: 18%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

98 statements  

1import sys 

2 

3import numpy as np 

4 

5from matplotlib import _api 

6 

7 

8class Triangulation: 

9 """ 

10 An unstructured triangular grid consisting of npoints points and 

11 ntri triangles. The triangles can either be specified by the user 

12 or automatically generated using a Delaunay triangulation. 

13 

14 Parameters 

15 ---------- 

16 x, y : (npoints,) array-like 

17 Coordinates of grid points. 

18 triangles : (ntri, 3) array-like of int, optional 

19 For each triangle, the indices of the three points that make 

20 up the triangle, ordered in an anticlockwise manner. If not 

21 specified, the Delaunay triangulation is calculated. 

22 mask : (ntri,) array-like of bool, optional 

23 Which triangles are masked out. 

24 

25 Attributes 

26 ---------- 

27 triangles : (ntri, 3) array of int 

28 For each triangle, the indices of the three points that make 

29 up the triangle, ordered in an anticlockwise manner. If you want to 

30 take the *mask* into account, use `get_masked_triangles` instead. 

31 mask : (ntri, 3) array of bool or None 

32 Masked out triangles. 

33 is_delaunay : bool 

34 Whether the Triangulation is a calculated Delaunay 

35 triangulation (where *triangles* was not specified) or not. 

36 

37 Notes 

38 ----- 

39 For a Triangulation to be valid it must not have duplicate points, 

40 triangles formed from colinear points, or overlapping triangles. 

41 """ 

42 def __init__(self, x, y, triangles=None, mask=None): 

43 from matplotlib import _qhull 

44 

45 self.x = np.asarray(x, dtype=np.float64) 

46 self.y = np.asarray(y, dtype=np.float64) 

47 if self.x.shape != self.y.shape or self.x.ndim != 1: 

48 raise ValueError("x and y must be equal-length 1D arrays, but " 

49 f"found shapes {self.x.shape!r} and " 

50 f"{self.y.shape!r}") 

51 

52 self.mask = None 

53 self._edges = None 

54 self._neighbors = None 

55 self.is_delaunay = False 

56 

57 if triangles is None: 

58 # No triangulation specified, so use matplotlib._qhull to obtain 

59 # Delaunay triangulation. 

60 self.triangles, self._neighbors = _qhull.delaunay(x, y, sys.flags.verbose) 

61 self.is_delaunay = True 

62 else: 

63 # Triangulation specified. Copy, since we may correct triangle 

64 # orientation. 

65 try: 

66 self.triangles = np.array(triangles, dtype=np.int32, order='C') 

67 except ValueError as e: 

68 raise ValueError('triangles must be a (N, 3) int array, not ' 

69 f'{triangles!r}') from e 

70 if self.triangles.ndim != 2 or self.triangles.shape[1] != 3: 

71 raise ValueError( 

72 'triangles must be a (N, 3) int array, but found shape ' 

73 f'{self.triangles.shape!r}') 

74 if self.triangles.max() >= len(self.x): 

75 raise ValueError( 

76 'triangles are indices into the points and must be in the ' 

77 f'range 0 <= i < {len(self.x)} but found value ' 

78 f'{self.triangles.max()}') 

79 if self.triangles.min() < 0: 

80 raise ValueError( 

81 'triangles are indices into the points and must be in the ' 

82 f'range 0 <= i < {len(self.x)} but found value ' 

83 f'{self.triangles.min()}') 

84 

85 # Underlying C++ object is not created until first needed. 

86 self._cpp_triangulation = None 

87 

88 # Default TriFinder not created until needed. 

89 self._trifinder = None 

90 

91 self.set_mask(mask) 

92 

93 def calculate_plane_coefficients(self, z): 

94 """ 

95 Calculate plane equation coefficients for all unmasked triangles from 

96 the point (x, y) coordinates and specified z-array of shape (npoints). 

97 The returned array has shape (npoints, 3) and allows z-value at (x, y) 

98 position in triangle tri to be calculated using 

99 ``z = array[tri, 0] * x + array[tri, 1] * y + array[tri, 2]``. 

100 """ 

101 return self.get_cpp_triangulation().calculate_plane_coefficients(z) 

102 

103 @property 

104 def edges(self): 

105 """ 

106 Return integer array of shape (nedges, 2) containing all edges of 

107 non-masked triangles. 

108 

109 Each row defines an edge by its start point index and end point 

110 index. Each edge appears only once, i.e. for an edge between points 

111 *i* and *j*, there will only be either *(i, j)* or *(j, i)*. 

112 """ 

113 if self._edges is None: 

114 self._edges = self.get_cpp_triangulation().get_edges() 

115 return self._edges 

116 

117 def get_cpp_triangulation(self): 

118 """ 

119 Return the underlying C++ Triangulation object, creating it 

120 if necessary. 

121 """ 

122 from matplotlib import _tri 

123 if self._cpp_triangulation is None: 

124 self._cpp_triangulation = _tri.Triangulation( 

125 # For unset arrays use empty tuple which has size of zero. 

126 self.x, self.y, self.triangles, 

127 self.mask if self.mask is not None else (), 

128 self._edges if self._edges is not None else (), 

129 self._neighbors if self._neighbors is not None else (), 

130 not self.is_delaunay) 

131 return self._cpp_triangulation 

132 

133 def get_masked_triangles(self): 

134 """ 

135 Return an array of triangles taking the mask into account. 

136 """ 

137 if self.mask is not None: 

138 return self.triangles[~self.mask] 

139 else: 

140 return self.triangles 

141 

142 @staticmethod 

143 def get_from_args_and_kwargs(*args, **kwargs): 

144 """ 

145 Return a Triangulation object from the args and kwargs, and 

146 the remaining args and kwargs with the consumed values removed. 

147 

148 There are two alternatives: either the first argument is a 

149 Triangulation object, in which case it is returned, or the args 

150 and kwargs are sufficient to create a new Triangulation to 

151 return. In the latter case, see Triangulation.__init__ for 

152 the possible args and kwargs. 

153 """ 

154 if isinstance(args[0], Triangulation): 

155 triangulation, *args = args 

156 if 'triangles' in kwargs: 

157 _api.warn_external( 

158 "Passing the keyword 'triangles' has no effect when also " 

159 "passing a Triangulation") 

160 if 'mask' in kwargs: 

161 _api.warn_external( 

162 "Passing the keyword 'mask' has no effect when also " 

163 "passing a Triangulation") 

164 else: 

165 x, y, triangles, mask, args, kwargs = \ 

166 Triangulation._extract_triangulation_params(args, kwargs) 

167 triangulation = Triangulation(x, y, triangles, mask) 

168 return triangulation, args, kwargs 

169 

170 @staticmethod 

171 def _extract_triangulation_params(args, kwargs): 

172 x, y, *args = args 

173 # Check triangles in kwargs then args. 

174 triangles = kwargs.pop('triangles', None) 

175 from_args = False 

176 if triangles is None and args: 

177 triangles = args[0] 

178 from_args = True 

179 if triangles is not None: 

180 try: 

181 triangles = np.asarray(triangles, dtype=np.int32) 

182 except ValueError: 

183 triangles = None 

184 if triangles is not None and (triangles.ndim != 2 or 

185 triangles.shape[1] != 3): 

186 triangles = None 

187 if triangles is not None and from_args: 

188 args = args[1:] # Consumed first item in args. 

189 # Check for mask in kwargs. 

190 mask = kwargs.pop('mask', None) 

191 return x, y, triangles, mask, args, kwargs 

192 

193 def get_trifinder(self): 

194 """ 

195 Return the default `matplotlib.tri.TriFinder` of this 

196 triangulation, creating it if necessary. This allows the same 

197 TriFinder object to be easily shared. 

198 """ 

199 if self._trifinder is None: 

200 # Default TriFinder class. 

201 from matplotlib.tri._trifinder import TrapezoidMapTriFinder 

202 self._trifinder = TrapezoidMapTriFinder(self) 

203 return self._trifinder 

204 

205 @property 

206 def neighbors(self): 

207 """ 

208 Return integer array of shape (ntri, 3) containing neighbor triangles. 

209 

210 For each triangle, the indices of the three triangles that 

211 share the same edges, or -1 if there is no such neighboring 

212 triangle. ``neighbors[i, j]`` is the triangle that is the neighbor 

213 to the edge from point index ``triangles[i, j]`` to point index 

214 ``triangles[i, (j+1)%3]``. 

215 """ 

216 if self._neighbors is None: 

217 self._neighbors = self.get_cpp_triangulation().get_neighbors() 

218 return self._neighbors 

219 

220 def set_mask(self, mask): 

221 """ 

222 Set or clear the mask array. 

223 

224 Parameters 

225 ---------- 

226 mask : None or bool array of length ntri 

227 """ 

228 if mask is None: 

229 self.mask = None 

230 else: 

231 self.mask = np.asarray(mask, dtype=bool) 

232 if self.mask.shape != (self.triangles.shape[0],): 

233 raise ValueError('mask array must have same length as ' 

234 'triangles array') 

235 

236 # Set mask in C++ Triangulation. 

237 if self._cpp_triangulation is not None: 

238 self._cpp_triangulation.set_mask( 

239 self.mask if self.mask is not None else ()) 

240 

241 # Clear derived fields so they are recalculated when needed. 

242 self._edges = None 

243 self._neighbors = None 

244 

245 # Recalculate TriFinder if it exists. 

246 if self._trifinder is not None: 

247 self._trifinder._initialize()