-
Notifications
You must be signed in to change notification settings - Fork 171
/
Copy pathtriangle.m
295 lines (273 loc) · 7.94 KB
/
triangle.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
function [TV,TF,TN,VV,VE,VRP,VRD] = triangle(varargin)
% TRIANGLE interface to the Triangle Program.
%
% [TV,TF,TN] = triangle(filename,options) Triangulates file in filename
% [TV,TF,TN] = triangle(V,options) Triangulates convex hull of given points
% [TV,TF,TN] = triangle(V,F,options) Refine an existing mesh
% [TV,TF,TN] = triangle(V,E,H,options) Triangulates a planar straight line graph
%
% Inputs:
% filename triangulates existing .poly or .node file
% V #V by 3 constrained points of triangulation
% F #F by 3 list of triangle indices
% E #E by 2 list of constraint edge indices
% H #H by 2 hole points
% options
% 'Quiet' Don't display command/output {true}
% 'Quality' Quality mesh generation with no angles smaller than 20
% degrees. An alternate minimum angle may be specified after the `q'.
% 'MaxArea' Imposes a maximum triangle area constraint. A fixed area
% constraint (that applies to every triangle) may be specified, or a
% list of #F area constraints (negative area means triangle is left
% unconstrained)
% 'ConstrainRefine' only if refining an existing mesh, means that edges
% should not be eliminated. Can specify edges after this option,
% otherwise all edges of existing mesh are used.
% 'NoBoundarySteiners' Prohibits the insertion of Steiner points on
% the mesh boundary.
% 'NoEdgeSteiners' prohibits insertion of Steiner points on any segment,
% including internal segments.
% 'MaxSteiners' specifies max number of allowed Steiner points.
% 'Flags' specifies flags string explicitly
% Outputs:
% TV #TV by 2 list of mesh vertices
% TF #TF by 3 list of triangle indices
% TN #TN by 3 list of triangle neighbors
% VV #VV by 2 list of voronoi vertices
% VE #VE by 2 list of voronoi internal edges
% VRP #VRP by 1 list of voronoi infinite ray start points
% VRD #VRP by 2 list of voronoi infinite direction unit vectors
%
% Note: In the command line program, Triangle, you can specify that you want
% to refine an existing mesh AND constrain the triangulation. I'm not sure
% what this means exactly as far as how the inputs used. It is not yet
% supported here.
%
% Copyright 2011, Alec Jacobson ([email protected])
%
% See also: delaunay, DelaunayTri
%
% default values
quality = -1;
max_area = -1;
no_boundary_steiners = false;
no_edge_steiners = false;
max_steiners = -1;
quiet = true;
% possible actions are:
% 'TriangulatePoly'
% 'RefineMesh'
triangulate_poly = false;
triangulate_existing = false;
refine_mesh = false;
neighbors = nargout >= 3;
voronoi = nargout >= 4;
flags = '';
% parse inputs
if nargin >=1 && ischar(varargin{1})
filename = varargin{1};
% strip file extension to get prefix
if ~isempty(regexp(filename,'\.poly$'))
prefix = regexprep(filename,'\.poly$','');
triangulate_poly = true;
elseif ~isempty(regexp(filename,'\.node$'))
prefix = regexprep(filename,'\.node$','');
else
prefix = file_name;
end
triangulate_existing = true;
ii = 2;
elseif( nargin == 1 || (nargin > 1 && ischar(varargin{2})))
V = varargin{1};
ii = 2;
elseif( nargin == 2 || (nargin > 2 && ischar(varargin{3})))
refine_mesh = true;
V = varargin{1};
MF = varargin{2};
ii = 3;
elseif( nargin == 3 || (nargin > 3 && ischar(varargin{4})))
triangulate_poly = true;
V = varargin{1};
PE = varargin{2};
PH = varargin{3};
ii = 4;
end
% parse options
while(ii <= nargin)
switch varargin{ii}
case 'Quiet'
if( (ii+1)<=nargin && ~ischar(varargin{ii+1}))
ii = ii + 1;
assert(ii <= nargin);
quiet = varargin{ii};
else
quiet = true;
end
case 'Quality'
if( (ii+1)<=nargin && ~ischar(varargin{ii+1}))
ii = ii + 1;
assert(ii <= nargin);
quality = varargin{ii};
else
quality = 20;
end
case 'MaxArea'
ii = ii + 1;
assert(ii <= nargin);
max_area = varargin{ii};
case 'NoBoundarySteiners'
no_boundary_steiners = true;
case 'NoEdgeSteiners'
no_edge_steiners = true;
case 'Flags'
ii = ii + 1;
assert(ii <= nargin);
flags = varargin{ii};
case 'MaxSteiners'
ii = ii + 1;
assert(ii <= nargin);
max_steiners = varargin{ii};
case 'ConstrainRefine'
if(refine_mesh)
triangulate_poly = true;
% Don't think holes are supported here, if they are then
% 'ConstrainRefine' should be able to take two arguements
PH = [];
if( (ii+1)<=nargin && ~ischar(varargin{ii+1}))
ii = ii + 1;
assert(ii <= nargin);
PE = varargin{ii};
else
PE = edges(MF);
end
else
warning('Ignoring ''ConstrainRefine'' option');
end
otherwise
error([varargin{ii} ' is not a valid option']);
end
ii = ii + 1;
end
% warnings
if(quality > 34)
warning( ...
['Quality: ' num2str(quality,'%0.15f') ' > 34, triangle might not terminate']);
end
% command line version
% get a free temporary prefix
if ~exist('prefix','var')
prefix = tempprefix();
end
% build command line parameters string
params = '-';
if ~triangulate_existing
writeNODE([prefix '.node'],V);
end
if triangulate_poly
params = [params 'p'];
if ~triangulate_existing
% print poly file
writePOLY_triangle([prefix '.poly'],[],PE,PH);
end
end
if(refine_mesh)
params = [params 'r'];
% print .ele file
writeELE([prefix '.ele'],MF);
end
if(quality >= 0)
params = [params 'q' num2str(quality,'%0.15f')];
end
if(max(size(max_area)) > 1)
params = [params 'a'];
end
if(max(size(max_area)) > 1)
error('Area constraints specified per triangle not yet supported...');
elseif(max_area >= 0)
params = [params 'a' num2str(max_area,'%0.15f')];
end
if(max_steiners >= 0)
params = [params 'S' num2str(max_steiners)];
end
if(no_boundary_steiners && ~no_edge_steiners)
params = [params 'Y'];
end
if(no_edge_steiners)
params = [params 'YY'];
end
if(voronoi)
params = [params 'v'];
end
if(neighbors)
params = [params 'n'];
end
params = [params ' ' flags];
command = [path_to_triangle ' ' params ' ' prefix];
if ~quiet
fprintf('%s\n',command);
end
[status, result] = system( command );
if(status ~= 0)
error(result);
end
if ~quiet
status
result
end
% read outputs from files
[TV,I] = readNODE([prefix '.1.node']);
TF = readELE([prefix '.1.ele']);
if isempty(TF)
TF = [];
else
% Triangle likes to use 1-indexed though .ele reader is 0-indexed
if(( min(TF(:)) > 1) && (max(TF(:)) > size(TV,1)))
TF = TF-1;
elseif min(TF(:)) == 0 && max(TF(:)) < size(TV,1)
TF = TF+1;
end
end
if isempty(TF) && ~quiet
warning(['No faces produced: ' result]);
end
if(nargout > 2)
TN = [];
VV = [];
VE = [];
VRP = [];
VRD = [];
end
if(neighbors)
TN = readELE([prefix '.1.neigh']);
% Triangle likes to use 1-indexed though .ele reader is 0-indexed
if(( min(TN(:)) > 1) && (max(TN(:)) > size(TF,1)))
TN = TN-1;
end
end
if(voronoi)
[VV] = readNODE([prefix '.1.v.node']);
[VE,VRP,VRD] = readEDGE([prefix '.1.v.edge']);
end
% delete temporary output files
delete([prefix '.1.node']);
delete([prefix '.1.ele']);
if(exist([prefix '.1.poly'],'file'))
delete([prefix '.1.poly']);
end
if(exist([prefix '.1.v.node'],'file'))
delete([prefix '.1.v.node']);
end
if(exist([prefix '.1.v.edge'],'file'))
delete([prefix '.1.v.edge']);
end
% Delete temporary input files
if ~triangulate_existing
delete([prefix '.node']);
end
if triangulate_poly && ~triangulate_existing
delete([prefix '.poly']);
end
if refine_mesh && ~triangulate_existing
delete([prefix '.ele']);
else
end