-
Notifications
You must be signed in to change notification settings - Fork 1
/
minimal_shell_partitioner.h
444 lines (418 loc) · 17.4 KB
/
minimal_shell_partitioner.h
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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
/*
MIT License
Copyright (c) 2022 Michael Vennettilli
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <CGAL/Linear_cell_complex_for_combinatorial_map.h>
#include <CGAL/Polyhedron_3_to_lcc.h>
#include <CGAL/draw_linear_cell_complex.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/convex_hull_3.h>
#include <vector>
#include <random>
#include <math.h>
#include <cmath>
#include <map>
#include <utility>
#ifndef MINIMAL_SHELL_PARTITIONER_H
#define MINIMAL_SHELL_PARTITIONER_H
// Typedefs pertaining to the kernel
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Polyhedron_3<K> Polyhedron_3;
typedef K::Point_3 Point_3;
// Typedefs pertaining to the convex hull LCC
typedef CGAL::Linear_cell_complex_for_combinatorial_map<2,3> LCC_CH;
typedef LCC_CH::Dart_handle Dart_handle_CH;
// Typedefs pertaining to the shell LCC
typedef CGAL::Linear_cell_complex_for_combinatorial_map<3> LCC_3;
typedef LCC_3::Dart_handle Dart_handle_3;
typedef LCC_3::Vertex_attribute_handle Vertex_handle_3;
// Typedefs pertaining to maps and pairs
typedef std::map<Dart_handle_CH, Vertex_handle_3> CHDH_to_VH3_map;
typedef std::map<Dart_handle_CH, Dart_handle_3> glue_vol_blueprint;
typedef std::pair<CHDH_to_VH3_map,CHDH_to_VH3_map> CH_to_in_out_vertices;
typedef std::pair<std::vector<Dart_handle_3>, std::map<Dart_handle_3, Dart_handle_3>> inner_face_and_map;
inline int positive_modulo(int i, int base){
return (base + (i % base)) % base;
}
/*
This function iterates over the vertex attributes for the convex hull
and prints out the points.
*/
void print_vertices(LCC_CH &lcc){
for (LCC_CH::Vertex_attribute_range::iterator
vertex_it=lcc.vertex_attributes().begin(),
vertex_it_end=lcc.vertex_attributes().end();
vertex_it!=vertex_it_end; ++vertex_it)
{
std::cout<<"point: "<<lcc.point_of_vertex_attribute(vertex_it)<<std::endl;
}
}
/*
This computes the circumcenter of the face containing a given dart.
*/
LCC_3::Point circumcenter(LCC_CH &chull, Dart_handle_CH &dh){
// Get the points
LCC_CH::Point a = chull.point(dh);
LCC_CH::Point b = chull.point(chull.beta(dh,1));
LCC_CH::Point c = chull.point(chull.beta(dh,0));
// Compute the differences
LCC_CH::Vector ba = b-a;
LCC_CH::Vector ca = c-a;
// Compute the cross products
LCC_CH::Vector X = CGAL::cross_product(ba,ca);
LCC_CH::Vector Xba = CGAL::cross_product(X,ba);
LCC_CH::Vector Xca = CGAL::cross_product(X,ca);
// Compute the circumcenter
LCC_3::Point cc = a + (Xba*ca.squared_length()-Xca*ba.squared_length())/(2*X.squared_length());
return cc;
}
std::vector<Point_3> random_spherical_points(int num_pts){
std::vector<Point_3> points;
// Set up the random number generation
std::mt19937 mt(time(NULL));
double azimuthal_angle, polar_angle;
const double pi = 3.14159265;
std::uniform_real_distribution<double> azimuthal_dist(-1.0,1.0);
std::uniform_real_distribution<double> polar_dist(0.0,2*pi);
for(int i=0; i<num_pts; i++){
azimuthal_angle = acos(azimuthal_dist(mt));
polar_angle = polar_dist(mt);
points.push_back(Point_3(sin(azimuthal_angle)*cos(polar_angle),
sin(azimuthal_angle)*sin(polar_angle), cos(azimuthal_angle)));
}
return points;
}
/*
Computes the convex hull as a linear cell complex oriented clockwise
*/
LCC_CH make_chull(std::vector<Point_3> &points){
// Compute the convex hull and convert to a linear cell complex
Polyhedron_3 chull_polyhedron;
CGAL::convex_hull_3(points.begin(), points.end(), chull_polyhedron);
LCC_CH chull_lcc;
Dart_handle_CH dh=CGAL::import_from_polyhedron_3<LCC_CH,Polyhedron_3>
(chull_lcc, chull_polyhedron);
// Check the orientation by computing the dot product between a cross product
// generated by the ordered vertices of the face and the barycenter of that
// face. Reverse the orientation if the dot product is positive.
LCC_CH::Vector v1 = chull_lcc.point(chull_lcc.beta(dh,1))-chull_lcc.point(dh);
LCC_CH::Vector v2 = chull_lcc.point(chull_lcc.beta(dh,0))-chull_lcc.point(dh);
LCC_CH::Vector barycenter = chull_lcc.barycenter<2>(dh) - CGAL::ORIGIN;
double signature = CGAL::cross_product(v1,v2)*barycenter;
if(signature>0.0) chull_lcc.reverse_orientation();
return chull_lcc;
}
/*
Use duality to get the vertices of the shell from the convex hull and a pair of
maps that send darts on a face of the convex hull to the corresponding inner/
outer vertex of the shell.
*/
CH_to_in_out_vertices make_dual_vertices(LCC_CH &chull,
LCC_3 &shell, double r_in, double r_out){
CHDH_to_VH3_map chull_to_inner_vertex;
CHDH_to_VH3_map chull_to_outer_vertex;
// Iterate over one dart per face in chull
for(LCC_CH::One_dart_per_cell_range<2>::iterator
face_it=chull.one_dart_per_cell<2>().begin(), face_it_end=chull.one_dart_per_cell<2>().end();
face_it!=face_it_end;++face_it){
// Compute the projections of the dual point onto the inner and outer spheres
// Can use the barycenter of the face as an alternative
LCC_3::Vector center = circumcenter(chull,face_it) - CGAL::ORIGIN;
LCC_3::Point inner_point = CGAL::ORIGIN + (center * (r_in/std::sqrt(center.squared_length())));
LCC_3::Point outer_point = CGAL::ORIGIN + (center * (r_out/std::sqrt(center.squared_length())));
// Add the points to the vertex container and get their handles.
Vertex_handle_3 inner_handle = shell.create_vertex_attribute<>(inner_point);
Vertex_handle_3 outer_handle = shell.create_vertex_attribute<>(outer_point);
// Iterate over the face and add the (dart, dual vertex) pairs to the
// appropriate maps.
Dart_handle_CH dh = face_it;
do {
chull_to_inner_vertex[dh] = inner_handle;
chull_to_outer_vertex[dh] = outer_handle;
dh = chull.beta(dh,1);
} while (dh != face_it);
}
// Make the pair of maps and export it.
CH_to_in_out_vertices map_pair = make_pair(chull_to_inner_vertex, chull_to_outer_vertex);
return map_pair;
}
/*
This makes the inner and outer faces from one dart incident to a vertex in the
convex hull and updates a map that details how to glue the volumes. It returns
a vector containing the darts in the inner face and a map from darts in the
inner face to the outer face.
*/
inner_face_and_map make_inner_outer_pair(LCC_CH &chull, Dart_handle_CH &dh_ch_start,
LCC_3 &shell, CH_to_in_out_vertices &map_pair, glue_vol_blueprint &glue_vol_map){
std::vector<Dart_handle_3> inner_face, outer_face;
std::map<Dart_handle_3, Dart_handle_3> inner_outer_map;
Vertex_handle_3 inner_vh, outer_vh;
Dart_handle_3 inner_dh, outer_dh;
// Create a dart handle that will be used to iterate over the faces.
Dart_handle_CH dh_ch = dh_ch_start;
// Advance the faces until you've returned to the original one.
do {
// Get the vertex handles from the convex hull dart handle
inner_vh = map_pair.first[dh_ch];
outer_vh = map_pair.second[dh_ch];
inner_dh = shell.create_dart(inner_vh);
inner_face.push_back(inner_dh);
outer_dh = shell.create_dart(outer_vh);
outer_face.push_back(outer_dh);
// Add the appropriate entry to glue_vol_map
glue_vol_map[chull.beta(dh_ch,0)]=inner_dh;
// Get the dart incident to the same vertex at the next (clockwise) face
dh_ch = chull.beta(dh_ch, 0, 2);
} while (dh_ch != dh_ch_start);
// We make the association between the inner and outer darts and sew the faces.
int vertices = inner_face.size();
for(int i=0; i<vertices;++i){
inner_outer_map[inner_face[i]] = outer_face[positive_modulo(i+1,vertices)];
// The inner face should go clockwise
shell.sew<1>(inner_face[i], inner_face[positive_modulo(i+1,vertices)]);
// The outer face should go counterclockwise
shell.sew<1>(outer_face[i], outer_face[positive_modulo(i-1,vertices)]);
}
inner_face_and_map result = make_pair(inner_face, inner_outer_map);
return result;
}
/*
This function adds the lateral faces to connect the inner and outer faces
and close the volumes.
*/
void make_lateral_and_close(LCC_3 &shell, inner_face_and_map &inner_and_map_pair){
std::vector<Dart_handle_3> inner_face = inner_and_map_pair.first;
std::map<Dart_handle_3, Dart_handle_3> inner_outer_map = inner_and_map_pair.second;
Dart_handle_3 l1, l2, l3, l4;
Vertex_handle_3 vh1, vh2, vh3, vh4;
int vertices = inner_face.size();
for(int i=0; i<vertices; i++){
// The dart 2-sewn to the inner dart starts at its destination.
vh1 = shell.vertex_attribute(shell.beta(inner_face[i],1));
l1 = shell.create_dart(vh1);
// The dart after the new 2-sewn one has the same origin as the inner dart.
vh2 = shell.vertex_attribute(inner_face[i]);
l2 = shell.create_dart(vh2);
// The dart 2-sewn to the outer dart starts at its destination.
vh3 = shell.vertex_attribute(shell.beta(inner_outer_map[inner_face[i]],1));
l3 = shell.create_dart(vh3);
// The dart after the new 2-sewn one has the same origin as the outer dart.
vh4 = shell.vertex_attribute(inner_outer_map[inner_face[i]]);
l4 = shell.create_dart(vh4);
// Connect the lateral darts to the inner and outer darts and make the face.
shell.sew<2>(inner_face[i], l1);
shell.sew<2>(inner_outer_map[inner_face[i]],l3);
shell.sew<1>(l1,l2);
shell.sew<1>(l2,l3);
shell.sew<1>(l3,l4);
shell.sew<1>(l4,l1);
}
// Now we 2-sew the lateral faces together.
for(int i=0; i<vertices; i++){
l1 = shell.beta(inner_face[i],2,1);
l2 = shell.beta(inner_face[i],0,2,0);
shell.sew<2>(l1,l2);
}
return;
}
/*
This is to be used after the closed volumes have been formed. This 3-sews
them together.
*/
void glue_vols(LCC_CH &chull, LCC_3 &shell, glue_vol_blueprint &glue_vol_map){
Dart_handle_CH edge_it_flipped;
Dart_handle_3 dh_sh1, dh_sh2;
// Iterate over one dart per edge in the convex hull.
for(LCC_CH::One_dart_per_cell_range<1>::iterator
edge_it=chull.one_dart_per_cell<1>().begin(), edge_it_end=chull.one_dart_per_cell<1>().end();
edge_it!=edge_it_end;++edge_it){
// Get the other dart corresponding to the edge in the convex hull.
edge_it_flipped = chull.beta(edge_it,2);
// The glue_vol_map maps edges onto inner darts. To get the lateral
// darts, you need to use beta_2.
dh_sh1 = shell.beta(glue_vol_map[edge_it],2);
dh_sh2 = shell.beta(glue_vol_map[edge_it_flipped],2);
// 3-sew the darts together.
shell.sew<3>(dh_sh1,dh_sh2);
}
return;
}
/*
This function puts everything together and uses duality to convert the convex
hull into a partition of the spherical shell.
*/
LCC_3 generate_shell(std::vector<Point_3> &points, double r_in, double r_out){
LCC_3 shell;
glue_vol_blueprint glue_vol_map;
LCC_CH chull = make_chull(points);
CH_to_in_out_vertices chd_to_shv = make_dual_vertices(chull, shell, r_in, r_out);
// Iterate over one dart per vertex in chull
for(LCC_CH::One_dart_per_cell_range<0>::iterator
dart_it=chull.one_dart_per_cell<0>().begin(), dart_it_end=chull.one_dart_per_cell<0>().end();
dart_it!=dart_it_end;++dart_it){
// Make the inner and outer faces
inner_face_and_map inner_and_map_pair = make_inner_outer_pair(chull, dart_it, shell,
chd_to_shv, glue_vol_map);
// Make the lateral edges and close the volume.
make_lateral_and_close(shell, inner_and_map_pair);
}
// Glue the volumes and return the result.
glue_vols(chull, shell, glue_vol_map);
return shell;
}
/*
This computes the center of mass of a face.
*/
LCC_3::Point face_center_of_mass(LCC_3 &lcc, Dart_handle_3 dh_start){
LCC_3::Point a, b;
double weight = 0.0;
LCC_3::Vector midpoint = LCC_3::Vector(0.0,0.0,0.0);
LCC_3::Vector cumulative_sum = LCC_3::Vector(0.0,0.0,0.0);
double perimeter = 0.0;
Dart_handle_3 dh = dh_start;
do {
a = lcc.point(lcc.beta(dh,1));
b = lcc.point(dh);
weight = std::sqrt((b-a).squared_length());
midpoint = ((a-CGAL::ORIGIN)+(b-CGAL::ORIGIN))*0.5;
cumulative_sum += midpoint*weight;
perimeter += weight;
dh = lcc.beta(dh,1);
} while(dh != dh_start);
LCC_3::Point center_of_mass = CGAL::ORIGIN + (cumulative_sum/perimeter);
return center_of_mass;
}
/*
This triangulates all faces in a LCC_3 by inserting the center of mass. Caution
needs to be used. Inserting a point into a face creates more faces, so use a mark
to indicate all new faces that have already been triangulated.
*/
void triangulate_all_faces(LCC_3 &lcc){
LCC_3::size_type changed = lcc.get_new_mark();
Dart_handle_3 dh_start, dh;
LCC_3::Point center_of_mass;
// Loop over all darts until you reach the (changing) end.
LCC_3::Dart_range::iterator dart_it=lcc.darts().begin();
while(dart_it!=lcc.darts().end()){
// Have we already visited this dart?
if(!lcc.is_marked(dart_it,changed)){
// It belongs to a face we haven't divided yet. Split it.
center_of_mass = face_center_of_mass(lcc,dart_it);
dh_start = lcc.insert_point_in_cell<2>(dart_it, center_of_mass);
dh = dh_start;
// Mark all of the resulting faces belonging to the same volume as
// dh_start
while(!lcc.is_marked(dh,changed)){
lcc.mark(dh,changed);
lcc.mark(lcc.beta(dh,1),changed);
lcc.mark(lcc.beta(dh,0),changed);
dh = lcc.beta(dh,2,1);
}
// If dh_start is 3-sewn, we need to mark darts on the same face on
// the other volume.
if(!lcc.is_free<3>(dh_start)){
dh = lcc.beta(dh_start,3,1);
while(!lcc.is_marked(dh,changed)){
lcc.mark(dh,changed);
lcc.mark(lcc.beta(dh,1),changed);
lcc.mark(lcc.beta(dh,0),changed);
dh = lcc.beta(dh,2,1);
}
}
}
++dart_it;
}
// Unmark everything before returning!
lcc.unmark_all(changed);
lcc.free_mark(changed);
return;
}
/*
Compute a diagonal element for the matrix that arises in the generalization
of Lloyd's method to volumes on the sphere.
*/
double get_lloyd_scalar(LCC_3 &lcc, Dart_handle_3 dh, int i){
// Get the ordered points, then compute the result.
LCC_3::Point v0 = lcc.point(dh);
LCC_3::Point v1 = lcc.point(lcc.beta(dh,1));
LCC_3::Point v2 = lcc.point(lcc.beta(dh,0));
double result = (v0[i]+v1[i]+v2[i])*(v0[i]+v1[i]+v2[i])
- (v0[i]*v1[i]+v0[i]*v2[i]+v1[i]*v2[i]);
return result;
}
/*
Given a dart on a face incident to a volume, this computes the diagonal matrix
and the outer normal, multiplies them together and returns the result.
*/
LCC_3::Vector lloyd_single_face(LCC_3 &lcc, Dart_handle_3 dh){
LCC_3::Point v0 = lcc.point(dh);
LCC_3::Point v1 = lcc.point(lcc.beta(dh,1));
LCC_3::Point v2 = lcc.point(lcc.beta(dh,0));
LCC_3::Vector outer_normal = CGAL::cross_product(v1-v0,v2-v0);
double c0 = get_lloyd_scalar(lcc, dh, 0);
double c1 = get_lloyd_scalar(lcc, dh, 1);
double c2 = get_lloyd_scalar(lcc, dh, 2);
LCC_3::Vector contribution = LCC_3::Vector(c0*outer_normal[0],
c1*outer_normal[1],c2*outer_normal[2]);
return contribution;
}
/*
Iterates over each volume in a 3-map and then over each face incident to that volume.
Computes the sum of the contributions from each face, normalizes the result, then
add export them as seeds for computing the convex hull.
*/
std::vector<LCC_CH::Point> get_seeds_lloyd(LCC_3 &lcc){
std::vector<LCC_CH::Point> new_seeds;
LCC_CH::Point seed;
LCC_CH::Vector lloyd_sum;
for(LCC_3::One_dart_per_cell_range<3>::iterator
vol_it=lcc.one_dart_per_cell<3>().begin(), vol_end=lcc.one_dart_per_cell<3>().end();
vol_it!=vol_end;++vol_it){
// Reset the current seed and the sum.
seed = LCC_CH::Point(0.0,0.0,0.0);
lloyd_sum = LCC_CH::Vector(0.0,0.0,0.0);
// Iterate over faces
for(LCC_3::One_dart_per_incident_cell_range<2,3>::iterator
face_it=lcc.one_dart_per_incident_cell<2,3>(vol_it).begin(),
face_end=lcc.one_dart_per_incident_cell<2,3>(vol_it).end();
face_it!=face_end; ++face_it){
// Add the contribution from each face to the running sum
lloyd_sum += lloyd_single_face(lcc,face_it);
}
// Normalize the sum, convert it to a point, then store it.
lloyd_sum = lloyd_sum/std::sqrt(lloyd_sum.squared_length());
seed = CGAL::ORIGIN + lloyd_sum;
new_seeds.push_back(seed);
}
return new_seeds;
}
/*
This method recomputes the LCC_3 using Lloyd relaxation for a specified
number of iterations.
*/
void lloyd_relaxation(LCC_3 &lcc, int num_iter, double r_in, double r_out){
std::vector<LCC_CH::Point> seeds;
for(int i=0; i<num_iter; ++i){
triangulate_all_faces(lcc);
seeds = get_seeds_lloyd(lcc);
lcc = generate_shell(seeds, r_in, r_out);
}
return;
}
#endif