static void compute_singular_action( Triangulation *manifold, Isometry *isometry) { Boolean lower_index_is_at_top; EdgeIndex index, image_index; VertexIndex image_v1,image_v2,v1,v2; Tetrahedron *tet, *image_tet; EdgeClass *edge, *image_edge; PositionedTet ptet, ptet0; for( edge = manifold->edge_list_begin.next; edge!=&manifold->edge_list_end; edge = edge->next ) if (edge->is_singular) { if (edge->singular_index<0) uFatalError("compute_singular_action","isometry_cusped"); tet = edge->incident_tet; index = edge->incident_edge_index; v1 = MIN(one_vertex_at_edge[index], other_vertex_at_edge[index]); v2 = MAX(one_vertex_at_edge[index], other_vertex_at_edge[index]); image_tet = tet->image; image_v1 = EVALUATE(tet->map,v1); image_v2 = EVALUATE(tet->map,v2); image_index = edge_between_vertices[image_v1][image_v2]; image_edge = image_tet->edge_class[image_index]; if (image_edge->singular_index<0 || image_edge->singular_order != edge->singular_order ) uFatalError("compute_singular_action","isometry_cusped"); /* i have never kept track of a direction only the singular edges. prehaps this is something i should be doing... * for now, here is how we'll kept track of orientation of these maps from singular edge to singular edge: * each edge has an incident tet and an incident edge index, define an ordering on the ends of the singular * edge by the ordering of the vertex indices of this edge. * * there may be some clever way of doing this with the handedness of the edge classes. i am not sure. */ set_left_edge( image_edge, &ptet0 ); ptet = ptet0; lower_index_is_at_top = ( ptet.bottom_face < ptet.right_face ); isometry->singular_image[edge->singular_index] = 0; do{ if (ptet.tet == image_tet && edge_between_faces[ptet.near_face][ptet.left_face] == image_index) { if (lower_index_is_at_top) { if (image_v1 == ptet.bottom_face) isometry->singular_image[edge->singular_index] = image_edge->singular_index + 1; else isometry->singular_image[edge->singular_index] = -(image_edge->singular_index + 1); } else { if (image_v1 == ptet.bottom_face) isometry->singular_image[edge->singular_index] = -(image_edge->singular_index + 1); else isometry->singular_image[edge->singular_index] = image_edge->singular_index + 1; } break; } veer_left( &ptet ); }while(!same_positioned_tet( &ptet0, &ptet )); if (isometry->singular_image[edge->singular_index] == 0) uFatalError("compute_singular_action","isometry_cusped"); } }
static void merge_incident_generators( Triangulation *manifold, EdgeClass *edge) { PositionedTet ptet, ptet0; Tetrahedron *tetA, *tetB, *tet; FaceIndex faceA, faceB, face; int indexA, indexB; Boolean generator_A_has_been_found, directions_agree; /* * Find the two incident generators by letting ptet * rotate around the EdgeClass. The first time we * encounter a nontrivial generator, call it * faceA of tetA; the second time, faceB of tetB. */ set_left_edge(edge, &ptet0); ptet = ptet0; generator_A_has_been_found = FALSE; while (TRUE) { /* * If we've found an active generator, record it. * If this is the second one we've found, break out of the loop. */ if (ptet.tet->generator_status[ptet.near_face] != not_a_generator) { if (generator_A_has_been_found == FALSE) { tetA = ptet.tet; faceA = ptet.near_face; generator_A_has_been_found = TRUE; } else { tetB = ptet.tet; faceB = ptet.near_face; break; } } /* * Move on to the next Tetrahedron incident to the EdgeClass. */ veer_left(&ptet); /* * If we've come all the way around the EdgeClass without * finding both generators, something has gone terribly wrong. */ if (same_positioned_tet(&ptet, &ptet0)) uFatalError("kill_the_incident_generator", "choose_generators"); } /* * If the two generators are the same, then either their product is * aA (in which case there is no further work to be done) or aa (in * which case they cannot be merged). Either way, we simply return. * [JRW 95/1/19. Actually, I don't think the first case (aA) is * likely to occur. The n-gons which are subdivided into triangles * have no interior vertices, so under normal circumstances the * generators we're merging should be distinct. If they're not, * it means we have a "face" which is topologically a cylinder, * or something weird like that. At any rate, we should return * without taking any action.] */ indexA = tetA->generator_index[faceA]; indexB = tetB->generator_index[faceB]; if (indexA == indexB) return; /* * Do the directions of the generators agree or disagree? * Note that the generator will point in the same direction * relative to the boundary of the fundamental domain iff * one is an outbound_generator and the other is an inbound_generator * relative to the preceding cyclic traversal around the EdgeClass. */ directions_agree = (tetA->generator_status[faceA] != tetB->generator_status[faceB]); /* * If directions_agree is FALSE, reverse the direction of generator A. * Then let generator A inherit the index of generator B. * * Let the highest numbered generator inherit the former index * of generator A, and decrement the number_of_generators count. * * Even in the special cases where indexA or indexB is the highest * index, generators A and B get merged, and the previously highest * index will no longer occur. This keeps the indices contiguous. */ manifold->num_generators--; for (tet = manifold->tet_list_begin.next; tet != &manifold->tet_list_end; tet = tet->next) for (face = 0; face < 4; face++) { if (tet->generator_index[face] == indexA) { if (directions_agree == FALSE) { if (tet->generator_status[face] == outbound_generator) tet->generator_status[face] = inbound_generator; else if (tet->generator_status[face] == inbound_generator) tet->generator_status[face] = outbound_generator; else uFatalError("merge_incident_generators", "choose_generators"); } tet->generator_index[face] = indexB; } if (tet->generator_index[face] == manifold->num_generators) tet->generator_index[face] = indexA; } /* * The EdgeClass no longer represents an active relation. */ edge->active_relation = FALSE; }
static void compute_translation( PositionedTet *initial_ptet, PeripheralCurve which_curve, TraceDirection which_direction, Complex translation[2], /* returns translations based on ultimate */ /* and penultimate shapes */ FillingStatus which_structure) { PositionedTet ptet; int i, initial_strand, strand, *this_vertex, near_strands, left_strands; Complex left_endpoint[2], /* left_endpoint[ultimate/penultimate] */ right_endpoint[2], /* right_endpoint[ultimate/penultimate] */ old_diff, new_diff, rotation; /* * Place the near edge of the top vertex of the initial_ptet in the * complex plane with its left endpoint at zero and its right endpoint at one. * Trace the curve which_curve in the direction which_direction, using the * shapes of the ideal tetrahedra to compute the position of endpoints of * each edge we cross. When we return to our starting point in the manifold, * the position of the left endpoint (or the position of the right endpoint * minus one) will tell us the translation. * * Note that we are working in the orientation double cover of the cusp. * * Here's how we keep track of where we are. At each step, we are always * at the near edge of the top vertex (i.e. the truncated vertex opposite * the bottom face) of the PositionedTet ptet. The curve (i.e. the * meridian or longitude) may cross that edge several times. The variable * "strand" keeps track of which intersection we are at; 0 means we're at * the strand on the far left, 1 means we're at the next strand, etc. */ ptet = *initial_ptet; initial_strand = 0; strand = initial_strand; for (i = 0; i < 2; i++) /* i = ultimate, penultimate */ { left_endpoint[i] = Zero; right_endpoint[i] = One; } do { /* * Note the curve's intersection numbers with the near side and left side. */ this_vertex = ptet.tet->curve[which_curve][ptet.orientation][ptet.bottom_face]; near_strands = this_vertex[ptet.near_face]; left_strands = this_vertex[ptet.left_face]; /* * If we are tracing the curve backwards, negate the intersection numbers * so the rest of compute_translation() can enjoy the illusion that we * are tracing the curve forwards. */ if (which_direction == trace_backwards) { near_strands = - near_strands; left_strands = - left_strands; } /* * Does the current strand bend to the left or to the right? */ if (strand < FLOW(near_strands, left_strands)) { /* * The current strand bends to the left. */ /* * The left_endpoint remains fixed. * Update the right_endpoint. * * The plan is to compute the vector old_diff which runs * from left_endpoint to right_endpoint, multiply it by the * complex edge parameter to get the vector new_diff which * runs from left_endpoint to the new value of right_endpoint, * and then add new_diff to left_endpoint to get the new * value of right_endpoint itself. * * Note that the complex edge parameters are always expressed * relative to the right_handed Orientation, so if we are * viewing this Tetrahedron relative to the left_handed * Orientation, we must take the conjugate-inverse of the * edge parameter. */ for (i = 0; i < 2; i++) /* i = ultimate, penultimate */ { old_diff = complex_minus(right_endpoint[i], left_endpoint[i]); rotation = ptet.tet->shape[which_structure]->cwl[i][edge3_between_faces[ptet.near_face][ptet.left_face]].rect; if (ptet.orientation == left_handed) { rotation = complex_div(One, rotation); /* invert . . . */ rotation.imag = - rotation.imag; /* . . . and conjugate */ } new_diff = complex_mult(old_diff, rotation); right_endpoint[i] = complex_plus(left_endpoint[i], new_diff); } /* * strand remains unchanged. */ /* * Move the PositionedTet onward, following the curve. */ veer_left(&ptet); } else { /* * The current strand bends to the right. * * Proceed as above, but note that * * (1) We now divide by the complex edge parameter * instead of multiplying by it. * * (2) We must adjust the variable "strand". Some of the strands * from the near edge may be peeling off to the left (in which * case left_strands is negative), or some strands from the left * edge may be joining those from the near edge in passing to * the right edge (in which case left_strands is positive). * Either way, the code "strand += left_strands" is correct. */ for (i = 0; i < 2; i++) /* i = ultimate, penultimate */ { old_diff = complex_minus(left_endpoint[i], right_endpoint[i]); rotation = ptet.tet->shape[which_structure]->cwl[i][edge3_between_faces[ptet.near_face][ptet.right_face]].rect; if (ptet.orientation == left_handed) { rotation = complex_div(One, rotation); rotation.imag = - rotation.imag; } new_diff = complex_div(old_diff, rotation); left_endpoint[i] = complex_plus(right_endpoint[i], new_diff); } strand += left_strands; veer_right(&ptet); } } while ( ! same_positioned_tet(&ptet, initial_ptet) || strand != initial_strand); /* * Write the computed translations, and return. */ for (i = 0; i < 2; i++) /* i = ultimate, penultimate */ translation[i] = left_endpoint[i]; }
static void kill_the_incident_generator( Triangulation *manifold, EdgeClass *edge) { PositionedTet ptet, ptet0; int dead_index; Tetrahedron *tet, *nbr_tet; Permutation gluing; FaceIndex face, nbr_face; /* * The EdgeClass edge is incident to a unique generator. * Find it. */ set_left_edge(edge, &ptet0); ptet = ptet0; while (TRUE) { /* * If we've found the active generator, * break out of the while loop. Otherwise . . . */ if (ptet.tet->generator_status[ptet.near_face] != not_a_generator) break; /* * . . . move on to the next Tetrahedron incident to the EdgeClass. */ veer_left(&ptet); /* * If we've come all the way around the EdgeClass without * finding a generator, something has gone terribly wrong. */ if (same_positioned_tet(&ptet, &ptet0)) uFatalError("kill_the_incident_generator", "choose_generators"); } /* * Note the index of the about to be killed generator . . . */ dead_index = ptet.tet->generator_index[ptet.near_face]; /* * . . . then kill it. */ nbr_tet = ptet.tet->neighbor[ptet.near_face]; gluing = ptet.tet->gluing[ptet.near_face]; nbr_face = EVALUATE(gluing, ptet.near_face); ptet.tet->generator_status[ptet.near_face] = not_a_generator; nbr_tet ->generator_status[nbr_face] = not_a_generator; ptet.tet->generator_index[ptet.near_face] = -1; /* garbage value */ nbr_tet ->generator_index[nbr_face] = -1; /* * The EdgeClass no longer represents an active relation. */ edge->active_relation = FALSE; /* * Decrement the num_incident_generators count at each of * the incident EdgeClasses. */ ptet.tet->edge_class[edge_between_faces[ptet.near_face][ptet.left_face] ]->num_incident_generators--; ptet.tet->edge_class[edge_between_faces[ptet.near_face][ptet.right_face] ]->num_incident_generators--; ptet.tet->edge_class[edge_between_faces[ptet.near_face][ptet.bottom_face]]->num_incident_generators--; /* * Decrement *number_of_generators. */ manifold->num_generators--; /* * If dead_index was not the highest numbered generator, then removing * it will have left a gap in the numbering scheme. Renumber the highest * numbered generator to keep the numbering contiguous. */ if (dead_index != manifold->num_generators) { for (tet = manifold->tet_list_begin.next; tet != &manifold->tet_list_end; tet = tet->next) for (face = 0; face < 4; face++) if (tet->generator_index[face] == manifold->num_generators) { if (tet->generator_status[face] == not_a_generator) uFatalError("kill_the_incident_generator", "choose_generators"); nbr_tet = tet->neighbor[face]; gluing = tet->gluing[face]; nbr_face = EVALUATE(gluing, face); tet ->generator_index[face] = dead_index; nbr_tet->generator_index[nbr_face] = dead_index; /* * Rather than worrying about breaking out of a * double loop, let's just return from here. */ return; } /* * The program should return from within the above double loop. */ uFatalError("kill_the_incident_generator", "choose_generators"); } else /* dead_index == manifold->num_generators, so nothing else to do */ return; }