Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import numpy as np 

2 

3 

4class Triangulation: 

5 """ 

6 An unstructured triangular grid consisting of npoints points and 

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

8 or automatically generated using a Delaunay triangulation. 

9 

10 Parameters 

11 ---------- 

12 x, y : array-like of shape (npoints) 

13 Coordinates of grid points. 

14 triangles : integer array-like of shape (ntri, 3), optional 

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

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

17 specified, the Delaunay triangulation is calculated. 

18 mask : boolean array-like of shape (ntri), optional 

19 Which triangles are masked out. 

20 

21 Attributes 

22 ---------- 

23 edges : int array of shape (nedges, 2) 

24 See `~.Triangulation.edges` 

25 neighbors : int array of shape (ntri, 3) 

26 See `~.Triangulation.neighbors` 

27 mask : bool array of shape (ntri, 3) 

28 Masked out triangles. 

29 is_delaunay : bool 

30 Whether the Triangulation is a calculated Delaunay 

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

32 

33 Notes 

34 ----- 

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

36 triangles formed from colinear points, or overlapping triangles. 

37 """ 

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

39 from matplotlib import _qhull 

40 

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

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

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

44 raise ValueError("x and y must be equal-length 1-D arrays") 

45 

46 self.mask = None 

47 self._edges = None 

48 self._neighbors = None 

49 self.is_delaunay = False 

50 

51 if triangles is None: 

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

53 # Delaunay triangulation. 

54 self.triangles, self._neighbors = _qhull.delaunay(x, y) 

55 self.is_delaunay = True 

56 else: 

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

58 # orientation. 

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

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

61 raise ValueError('triangles must be a (?,3) array') 

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

63 raise ValueError('triangles max element is out of bounds') 

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

65 raise ValueError('triangles min element is out of bounds') 

66 

67 if mask is not None: 

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

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

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

71 'triangles array') 

72 

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

74 self._cpp_triangulation = None 

75 

76 # Default TriFinder not created until needed. 

77 self._trifinder = None 

78 

79 def calculate_plane_coefficients(self, z): 

80 """ 

81 Calculate plane equation coefficients for all unmasked triangles from 

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

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

84 position in triangle tri to be calculated using 

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

86 """ 

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

88 

89 @property 

90 def edges(self): 

91 """ 

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

93 non-masked triangles. 

94 

95 Each row defines an edge by it's start point index and end point 

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

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

98 """ 

99 if self._edges is None: 

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

101 return self._edges 

102 

103 def get_cpp_triangulation(self): 

104 """ 

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

106 if necessary. 

107 """ 

108 from matplotlib import _tri 

109 if self._cpp_triangulation is None: 

110 self._cpp_triangulation = _tri.Triangulation( 

111 self.x, self.y, self.triangles, self.mask, self._edges, 

112 self._neighbors, not self.is_delaunay) 

113 return self._cpp_triangulation 

114 

115 def get_masked_triangles(self): 

116 """ 

117 Return an array of triangles that are not masked. 

118 """ 

119 if self.mask is not None: 

120 return self.triangles[~self.mask] 

121 else: 

122 return self.triangles 

123 

124 @staticmethod 

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

126 """ 

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

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

129 

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

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

132 and kwargs are sufficient to create a new Triangulation to 

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

134 the possible args and kwargs. 

135 """ 

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

137 triangulation, *args = args 

138 else: 

139 x, y, *args = args 

140 

141 # Check triangles in kwargs then args. 

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

143 from_args = False 

144 if triangles is None and args: 

145 triangles = args[0] 

146 from_args = True 

147 

148 if triangles is not None: 

149 try: 

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

151 except ValueError: 

152 triangles = None 

153 

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

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

156 triangles = None 

157 

158 if triangles is not None and from_args: 

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

160 

161 # Check for mask in kwargs. 

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

163 

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

165 return triangulation, args, kwargs 

166 

167 def get_trifinder(self): 

168 """ 

169 Return the default :class:`matplotlib.tri.TriFinder` of this 

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

171 TriFinder object to be easily shared. 

172 """ 

173 if self._trifinder is None: 

174 # Default TriFinder class. 

175 from matplotlib.tri.trifinder import TrapezoidMapTriFinder 

176 self._trifinder = TrapezoidMapTriFinder(self) 

177 return self._trifinder 

178 

179 @property 

180 def neighbors(self): 

181 """ 

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

183 

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

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

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

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

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

189 """ 

190 if self._neighbors is None: 

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

192 return self._neighbors 

193 

194 def set_mask(self, mask): 

195 """ 

196 Set or clear the mask array. This is either None, or a boolean 

197 array of shape (ntri). 

198 """ 

199 if mask is None: 

200 self.mask = None 

201 else: 

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

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

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

205 'triangles array') 

206 

207 # Set mask in C++ Triangulation. 

208 if self._cpp_triangulation is not None: 

209 self._cpp_triangulation.set_mask(self.mask) 

210 

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

212 self._edges = None 

213 self._neighbors = None 

214 

215 # Recalculate TriFinder if it exists. 

216 if self._trifinder is not None: 

217 self._trifinder._initialize()