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 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628
|
/*
* canonize_part_2.c
*
* This file provides the function
*
* void canonical_retriangulation(Triangulation *manifold);
*
* which accepts an arbitrary subdivision of a canonical cell decomposition
* into Tetrahedra, and replaces it with a canonical retriangulation
* (defined below). If the canonical cell decomposition is itself a
* triangulation (as is typically the case) then the canonical retriangulation
* is just the canonical cell decomposition itself. If the canonical cell
* decomposition is not a triangulation, the canonical retriangulation will
* introduce a finite vertex at the center of each 3-cell.
* canonical_retriangulation() is intended to follow a call to proto_canonize()
* (cf. the function canonize() in canonize.c); it assumes the tet->tilt[]
* fields are correct.
*
* If *manifold has a hyperbolic structure and/or VertexCrossSections,
* they are discarded.
*
*
* Definition of the canonical retriangulation.
*
* The canonical cell decomposition of a cusped hyperbolic 3-manifold
* is defined in
*
* J. Weeks, Convex hulls and isometries of cusped hyperbolic
* 3-manifolds, Topology Appl. 52 (1993) 127-149.
*
* and
*
* M. Sakuma and J. Weeks, The generalized tilt formula,
* Geometriae Dedicata 55 (1995) 115-123.
*
* If the canonical cell decomposition is a triangulation (as is
* typically the case), then the canonical retriangulation is just
* the canonical cell decomposition itself. Otherwise, the canonical
* retriangulation is defined by the following two step procedure.
*
* Step #1. Subdivide each 3-cell by coning its boundary to a point
* in its interior.
*
* Step #2. Each 2-cell in the canonical cell decomposition (just
* the original 2-cells, not the 2-cells introduced in Step #1)
* is the boundary between two 3-cells created at Step #1.
* The union of the two 3-cells is the suspension of the
* 2-cell between two of the vertices introduced in Step #1.
* Triangulate this suspension in the "obvious" symmetrical way,
* as n tetrahedra surrounding a common edge, where n is the
* number of sides of the 2-cell, and the common edge runs
* from one finite vertex to the other.
*
* This two-step procedure defines a canonical retriangulation of
* the canonical cell decomposition. Note that it's not a subdivision.
*
*
* Computing the canonical retriangulation.
*
* The following is a mathematical explanation of the algorithm.
* The details of the implementation are documented in the code itself.
*
* We begin with an arbitrary subdivision of the canonical cell
* decomposition into Tetrahedra.
*
* Definition. A 2-cell in the Triangulation is called "opaque" if
* it lies in the 2-skeleton of the canonical cell decomposition,
* and "transparent" if lies in the interior of a 3-cell of the
* canonical decomposition.
*
* Step #1.
* To cone a 3-cell to a point in its interior, first do a one-to-four
* move to cone a single Tetrahedron to a point in its interior.
* If that was the only Tetrahedron in the 3-cell, we're done.
* Otherwise, perform a two-to-three move across a transparent face of
* the coned tetrahedron. This yields a coned hexahedron. Continue
* in this fashion until all the Tetrahedra in the 3-cell have been
* absorbed into the coned polyhedron. This coned polyhedron may have
* some pair of faces on its boundary identified to yield the original
* 3-cell. Where this occurs, call cancel_tetrahedra() to simplify the
* coned polyhedron. (This operation is documented more thoroughly
* in the code itself.)
*
* Step #2.
* Do a two-to-three move across each opaque face, then cancel
* all pairs of Tetrahedra surrounding EdgeClasses of order 2.
* You may take it as an exercise for the reader to prove that this
* has the desired effect, or you may read the explanation provided
* in the function step_two() below.
*
*
* Programming note: I have coded this algorithm for simplicity
* rather than speed. But even though the code is "less efficient"
* because it does, e.g., some unnecessary rescanning of lists, I don't
* think this will make a measurable difference in practice, and
* in any case I think it's a small price to pay to keep the logic
* of the code clean and simple.
*/
#include "kernel.h"
#include "canonize.h"
static void remove_vertex_cross_sections(Triangulation *manifold);
static void attach_canonize_info(Triangulation *manifold);
static void free_canonize_info(Triangulation *manifold);
static void label_opaque_faces(Triangulation *manifold);
static void step_one(Triangulation *manifold);
static void initialize_tet_status(Triangulation *manifold);
static Boolean cone_3_cell(Triangulation *manifold, int *num_finite_vertices);
static Boolean find_unconed_tet(Triangulation *manifold, Tetrahedron **tet0);
static Boolean expand_coned_region(Triangulation *manifold);
static Boolean attempt_cancellation(Triangulation *manifold);
static Boolean verify_coned_region(Triangulation *manifold);
static void step_two(Triangulation *manifold);
static Boolean eliminate_opaque_face(Triangulation *manifold);
void canonical_retriangulation(
Triangulation *manifold)
{
/*
* Remove the hyperbolic structures and VertexCrossSections, if any.
*/
remove_hyperbolic_structures(manifold);
remove_vertex_cross_sections(manifold);
/*
* If the canonical cell decomposition is a triangulation, we're done.
* (Note: Comment out this line if you want to invoke the more
* elaborate canonical retriangulation scheme for all manifolds, not
* just those whose canonical cell decompositions contain cells other
* than tetrahedra.)
*/
if (is_canonical_triangulation(manifold) == TRUE)
return;
/*
* Add a CanonizeInfo field to each Tetrahedron to hold local variables.
* These variables must be accessible to the low-level retriangulation
* routines in simplify_triangulation.c, which is why we use the
* special purpose pointer canonize_info in the Tetrahedron data
* structure, rather than using the general purpose "extra" pointer.
*/
attach_canonize_info(manifold);
/*
* Note which 2-cells are opaque and which are transparent.
*/
label_opaque_faces(manifold);
/*
* Carry out the two step retriangulation algorithm described above.
*/
step_one(manifold);
step_two(manifold);
/*
* Free the CanonizeInfo fields.
*/
free_canonize_info(manifold);
/*
* We can't possibly compute the Chern-Simons invariant
* for a manifold with finite vertices, so we set
* CS_fudge_is_known to FALSE. However, we can leave
* CS_value_is_known as TRUE if it is already TRUE, since
* the manifold is still the same.
*/
manifold->CS_fudge_is_known = FALSE;
}
static void remove_vertex_cross_sections(
Triangulation *manifold)
{
Tetrahedron *tet;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
if (tet->cross_section != NULL)
{
my_free(tet->cross_section);
tet->cross_section = NULL;
}
}
static void attach_canonize_info(
Triangulation *manifold)
{
Tetrahedron *tet;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
{
/*
* Just to be safe . . .
*/
if (tet->canonize_info != NULL)
uFatalError("attach_canonize_info", "canonize_part_2");
/*
* Attach the CanonizeInfo.
*/
tet->canonize_info = NEW_STRUCT(CanonizeInfo);
}
}
static void free_canonize_info(
Triangulation *manifold)
{
Tetrahedron *tet;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
{
/*
* Free the CanonizeInfo structure.
*/
my_free(tet->canonize_info);
/*
* Set the canonize_info pointer to NULL just to be safe.
*/
tet->canonize_info = NULL;
}
}
static void label_opaque_faces(
Triangulation *manifold)
{
Tetrahedron *tet,
*nbr_tet;
FaceIndex f,
nbr_f;
double sum_of_tilts;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
for (f = 0; f < 4; f++)
{
nbr_tet = tet->neighbor[f];
nbr_f = EVALUATE(tet->gluing[f], f);
sum_of_tilts = tet->tilt[f] + nbr_tet->tilt[nbr_f];
tet->canonize_info->face_status[f] =
sum_of_tilts < - CONCAVITY_EPSILON ?
opaque_face :
transparent_face;
}
}
static void step_one(
Triangulation *manifold)
{
int num_finite_vertices;
/*
* Initialize each part_of_coned_cell flag to FALSE.
*/
initialize_tet_status(manifold);
/*
* Keep track of the number of finite vertices that
* have been introduced, so they can be given consecutive
* negative integers as indices.
*/
num_finite_vertices = 0;
/*
* Cone each 3-cell of the canonical cell decomposition to a point
* in its interior. Each call to cone_3_cell() cones a single 3-cell,
* so we must keep calling cone_3_cell() until it returns FALSE.
*/
while (cone_3_cell(manifold, &num_finite_vertices) == TRUE)
;
}
static void initialize_tet_status(
Triangulation *manifold)
{
Tetrahedron *tet;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
tet->canonize_info->part_of_coned_cell = FALSE;
}
static Boolean cone_3_cell(
Triangulation *manifold,
int *num_finite_vertices)
{
Tetrahedron *tet0;
/*
* Is there a Tetrahedron which is not yet part of a coned cell?
* If so, proceed.
* If not, return FALSE.
*/
if (find_unconed_tet(manifold, &tet0) == FALSE)
return FALSE;
/*
* Cone tet0 to its center. This will introduce a finite vertex.
* Each of the four new Tetrahedra will have part_of_coned_cell
* set to TRUE. The face_status for the "exterior" faces will
* be preserved, and the face_status for the "interior" faces
* will be set to inside_cone_face.
*/
one_to_four(tet0, &manifold->num_tetrahedra, -++*num_finite_vertices);
/*
* Expand the coned region. Whenever a Tetrahedron with
* part_of_coned_cell == TRUE borders a Tetrahedron with
* part_of_coned_cell == FALSE across a transparent face,
* do a two-to-three move across the face to include the
* (space occupied by the) latter Tetrahedron in the (growing)
* coned region. Keep doing this as long as progress is
* being made. The two-to-three move will set
* part_of_coned_cell = TRUE for the three new Tetrahedra
* it creates. It will preserve the face_status of the
* "exterior" faces of the new Tetrahedra, and set the face_status
* of the "interior" faces to inside_cone_face.
*
* (Yes, I know this isn't the optimally efficient way to do this,
* but I don't think that's important. Please see the programming
* note at the end of the documentation at the top of this file.)
*/
while (expand_coned_region(manifold) == TRUE)
;
/*
* If the initial, arbitrary subdivision of the 3-cell contained
* edges in its interior (as would be the case with an octahedron,
* for example), then the coned 3-cell we just produced will have
* some identifications on its boundary (for example, in the case
* of the octahedron we'd have a coned decahedron with two adjacent
* boundary faces identified to yield the octahedron). Assuming at
* least one pair of identified faces are adjacent, we can call
* cancel_tetrahedra() to simplify the structure of the coned region
* from a coned n-hedron with k pairs of faces identified to a coned
* (n-2)-hedron with (k-1) pairs of faces identified. As long as some
* pair of identified faces are adjacent (and identified in the obvious
* way) we can keep repeating the simplification to arrive at a coned
* (n-2k)-hedron with no faces identified, which is exactly what
* we want in Step #1 of this algorithm.
*
* It's theoretically possible to have faces of a coned polyehdron
* identified with no pair of identified faces adjacent to each other.
* For example, consider the complement of the house-with-two-rooms
* in the 3-sphere. Fortunately such examples are so rare and so
* complicated that I doubt any will ever show up as triangulations
* of 3-cells in canonical cell decompositions. If one did show up,
* verify_coned_region() would detect the condition and call uFatalError().
*
* Proposition. Identifying two adjacent faces (in the "obvious" way)
* creates an EdgeClass of order 2, and this is the only way
* EdgeClasses of order 2 arise.
*
* Proof. It's obvious that such an identification creates an
* EdgeClass of order 2. We must prove that this is the only way
* they may arise. Consider separately EdgeClasses from the
* original Triangulation (i.e. from the original arbitrary subdivision
* of the canonical cell decomposition), which connect one ideal
* vertex to another, and EdgeClasses which are added during the
* coning process, which connect an ideal vertex to a finite vertex.
*
* Case 1. Original EdgeClasses, which connect one ideal vertex
* to another. There are no EdgeClasses of order 2 in the initial
* Triangulation, because it is geometric. In a coned polyhedron
* (ignoring boundary identifications for the moment) each
* EdgeClass connecting one ideal vertex to another is incident
* to precisely two of the coned polyhedron's Tetrahedra. If the
* EdgeClass lies on the boundary of the 3-cell (of the canonical
* cell decomposition) then its true order will be greater than two.
* If it lies in the interior of the 3-cell, then the only way for
* the order to be exactly two is to have the two adjacent faces
* identified.
*
* Case 2. EdgeClasses added during the coning process, which
* connect an ideal vertex to a finite vertex. We assume such
* an EdgeClass has order 2, and will deduce a contradiction.
* Consider the coned polyhedron, ignoring boundary identifications
* for the moment. Consider the two faces on the boundary of
* incident to the ideal endpoint of the given EdgeClass. These
* faces are part of the original (geometric!) subdivision of the
* canonical cell decomposition. They have all three ideal vertices
* in common, therefore they must coincide. But this implies that
* the boundary component at the ideal vertex of the given EdgeClass
* is topologically a sphere.
*
* Q.E.D.
*/
while (attempt_cancellation(manifold) == TRUE)
;
/*
* As explained above, once we've cancelled all Tetrahedra incident
* to EdgeClasses of order 2, we will almost surely have the required
* coning of the 3-cell's boundary to a point in its interior, unless
* we encounter a situation like the complement of the
* house-with-two-rooms. verify_coned_region() checks that we
* do in fact have a coning of the original 3-cell; that is,
* no Tetrahedron with part_of_coned_cell == TRUE has a face with
* face_status transparent_face.
*/
if (verify_coned_region(manifold) == FALSE)
uFatalError("cone_3_cell", "canonize_part_2");
return TRUE;
}
static Boolean find_unconed_tet(
Triangulation *manifold,
Tetrahedron **tet0)
{
Tetrahedron *tet;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
if (tet->canonize_info->part_of_coned_cell == FALSE)
{
*tet0 = tet;
return TRUE;
}
return FALSE;
}
static Boolean expand_coned_region(
Triangulation *manifold)
{
Tetrahedron *tet;
FaceIndex f;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
if (tet->canonize_info->part_of_coned_cell == TRUE)
for (f = 0; f < 4; f++)
if (tet->canonize_info->face_status[f] == transparent_face)
if (tet->neighbor[f]->canonize_info->part_of_coned_cell == FALSE)
{
if (two_to_three(tet, f, &manifold->num_tetrahedra) == func_OK)
return TRUE;
else /* this should never occur */
uFatalError("expand_coned_region", "canonize_part_2");
}
return FALSE;
}
static Boolean attempt_cancellation(
Triangulation *manifold)
{
EdgeClass *edge,
*where_to_resume;
for (edge = manifold->edge_list_begin.next;
edge != &manifold->edge_list_end;
edge = edge->next)
if (edge->order == 2)
{
if (cancel_tetrahedra(edge, &where_to_resume, &manifold->num_tetrahedra) == func_OK)
return TRUE;
else
/*
* I don't think failure is possible, but if it is
* I want to know about it.
*/
uFatalError("attempt_cancellation", "canonize_part_2");
}
return FALSE;
}
static Boolean verify_coned_region(
Triangulation *manifold)
{
/*
* Because one_to_four(), two_to_three() and cancel_tetrahedra()
* all maintain the coned polyhedron as some sort of coned
* polyhedron, all we need to check is that it has no remaining
* identifications on its boundary. That is, we need to check
* that the faces of each Tetrahedron with part_of_coned_cell == TRUE
* have face_status opaque_face or inside_cone_face, but never
* transparent_face.
*/
Tetrahedron *tet;
FaceIndex f;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
if (tet->canonize_info->part_of_coned_cell == TRUE)
for (f = 0; f < 4; f++)
if (tet->canonize_info->face_status[f] == transparent_face)
return FALSE;
return TRUE;
}
static void step_two(
Triangulation *manifold)
{
/*
* Step #1 of the algorithm has already been carried out, so
* we know that every 3-cell in the canonical cell decomposition
* has been subdivided by coning the boundary to a point in
* the interior. There are three types of EdgeClasses:
*
* (A) Those which lie in the 1-skeleton of the canonical
* cell decomposition. They have order at least 6.
*
* (B) Those which lie in the 2-skeleton of the canonical
* cell decomposition, but not in the 1-skeleton. They
* serve to (artificially) subdivide the faces of the 3-cells
* into triangles. Each has order precisely 4.
*
* (C) Those in the interior of the 3-cells. They connect ideal
* vertices to finite vertices, and have order at least 3.
*
* Step #2 of the algorithm consists of two substeps:
*
* Substep A. Do a two-to-three move across every opaque face.
*
* Substep B. Perform all possible cancellations of Tetrahedra
* surrounding EdgeClasses of order 2.
*
* After Substep A is complete, we know that
*
* EdgeClasses of type A will have order at least 3.
* EdgeClasses of type B will have order precisely 2.
* EdgeClasses of type C will have order at least 6.
* The new EdgeClasses introduced in Substep A will have
* order precisely 3.
*
* Therefore Substep B will eliminate precisely the EdgeClasses
* of type B. It's easy to see that removing these EdgeClasses
* creates the Triangulation specified in Step #2 of the definition
* of the canonical retriangulation at the top of this file.
*/
while (eliminate_opaque_face(manifold) == TRUE)
;
while (attempt_cancellation(manifold) == TRUE)
;
}
static Boolean eliminate_opaque_face(
Triangulation *manifold)
{
Tetrahedron *tet;
FaceIndex f;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
for (f = 0; f < 4; f++)
if (tet->canonize_info->face_status[f] == opaque_face)
{
if (two_to_three(tet, f, &manifold->num_tetrahedra) == func_OK)
return TRUE;
else /* this should never occur */
uFatalError("expand_coned_region", "canonize_part_2");
}
return FALSE;
}
|