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
|
/*
* cusp_shapes.c
*
* This file provides the function
*
* void compute_cusp_shapes(Triangulation *manifold,
* FillingStatus which_structure);
*
* which computes the shape of each unfilled cusp. The shape of an
* orientable cusp is defined to be the ratio of the complex numbers
* representing the longitudinal and meridional translations
* (i.e. longitude/meridian). The shape of a nonorientable cusp is
* defined to be the shape of the cusp's orientation double cover;
* it is always pure imaginary. (Another reasonable definition for
* the shape of a nonorientable cusp would be to divide the shape
* of the orientation double cover by two, to correspond more closely
* to the geometry of the nonorientable cusp's fundamental domain,
* but I decided not to do this.)
*
* compute_cusp_shapes() computes the cusp shapes using the which_structure
* (initial or current) hyperbolic structure, and stores each computed cusp
* shape in the cusp_shape[which_structure] field of the Cusp data structure.
*
* To estimate the computed cusp shape's precision, compute_cusp_shapes()
* computes the cusp shape using both the ultimate and penultimate values of
* the Tetrahedron shapes. The number of digits to which the answers agree
* is stored in the shape_precision[which_structure] field of the Cusp data
* structure.
*
* do_Dehn_filling() calls compute_cusp_shapes() to maintain correct values
* in the cusp_shape[current] field of each unfilled cusp whenever a
* hyperbolic structure is present. find_complete_hyperbolic_structure()
* takes responsibility for copying the original shape of each cusp into
* the cusp_shape[initial] field.
*
* Cusp shapes are computed iff manifold->solution_type[which_structure]
* is geometric_solution, nongeometric_solution, flat_solution, or
* (tentatively) other_solution. For other solution types (degenerate_solution,
* etc.) all cusp shapes are set to zero.
*
* Technical note: with the usual orientation conventions for the
* longitude and meridian, we must view the cusp from the fat part
* of the manifold looking out towards infinity in order to have the
* imaginary part of the cusp shape be positive. The code
* in compute_translation() views the cusp from infinity looking in
* towards the fat part of the manifold (as usual), and then
* compute_one_cusp_shape() takes the complex conjugate of the shape
* at the very end.
*
* The function shortest_cusp_basis() in shortest_cusp_basis.c converts
* the (meridian, longitude) basis to the (shortest, second shortest)
* basis. cusp_modulus() uses the latter to compute the cusp modulus
* as (second shortest translation)/(shortest translation).
*
* 96/9/27 Added the which_structure parameter to compute_cusp_shapes()
* so it can compute the cusp shapes for either the initial (complete)
* or the current (filled) hyperbolic structure. Previously it used only
* the current structure.
*/
#include "kernel.h"
static void compute_the_cusp_shapes(Triangulation *manifold, FillingStatus which_structure);
static void set_all_shapes_to_zero(Triangulation *manifold, FillingStatus which_structure);
static void compute_one_cusp_shape(Triangulation *manifold, Cusp *cusp, FillingStatus which_structure);
static void compute_translation(PositionedTet *initial_ptet,
PeripheralCurve which_curve, TraceDirection which_direction,
Complex translation[2], FillingStatus which_structure);
static PositionedTet find_start(Triangulation *manifold, Cusp *cusp);
void compute_cusp_shapes(
Triangulation *manifold,
FillingStatus which_structure)
{
/*
* If we have some reasonable (or semi-reasonable) hyperbolic
* structure, then compute the cusp shapes. Otherwise, fill in
* all shapes with zeros.
*/
switch (manifold->solution_type[which_structure])
{
case geometric_solution:
case nongeometric_solution:
case flat_solution:
case other_solution: /* we'll give the cusps of other_solution a try */
/* other_solution can be moved below if its */
/* cusp shapes can't be computed meaningfully */
compute_the_cusp_shapes(manifold, which_structure);
break;
case not_attempted:
case degenerate_solution:
case no_solution:
set_all_shapes_to_zero(manifold, which_structure);
break;
}
}
static void compute_the_cusp_shapes(
Triangulation *manifold,
FillingStatus which_structure)
{
Cusp *cusp;
/*
* Call compute_one_cusp_shape() for each unfilled cusp.
*/
for (cusp = manifold->cusp_list_begin.next;
cusp != &manifold->cusp_list_end;
cusp = cusp->next)
if ( which_structure == initial
|| (which_structure == current && cusp->is_complete))
compute_one_cusp_shape(manifold, cusp, which_structure);
else
{
cusp->cusp_shape[which_structure] = Zero;
cusp->shape_precision[which_structure] = 0;
}
}
static void set_all_shapes_to_zero(
Triangulation *manifold,
FillingStatus which_structure)
{
Cusp *cusp;
for (cusp = manifold->cusp_list_begin.next;
cusp != &manifold->cusp_list_end;
cusp = cusp->next)
{
cusp->cusp_shape[which_structure] = Zero;
cusp->shape_precision[which_structure] = 0;
}
}
static void compute_one_cusp_shape(
Triangulation *manifold,
Cusp *cusp,
FillingStatus which_structure)
{
PositionedTet initial_ptet;
TraceDirection direction[2]; /* direction[M/L] */
Complex translation[2][2], /* translation[M/L][ultimate/penultimate] */
shape[2]; /* shape[ultimate/penultimate] */
int i;
/*
* Compute the longitudinal and meridional translations, and
* divide them to get the cusp shape.
*
* Do parallel computations for the ultimate and penultimate shapes,
* to estimate the accuracy of the final answer.
*/
/*
* Find and position a tetrahedron so that the near edge of the top
* vertex intersects both the meridian and the longitude.
*/
initial_ptet = find_start(manifold, cusp);
for (i = 0; i < 2; i++) /* which curve */
{
/*
* Decide whether the meridian and longitude cross the near edge of the
* top vertex in a forwards or backwards direction.
*/
direction[i] =
(initial_ptet.tet->curve[i][initial_ptet.orientation] [initial_ptet.bottom_face] [initial_ptet.near_face] > 0) ?
trace_forwards:
trace_backwards;
/*
* Compute the translation.
*/
compute_translation(&initial_ptet, i, direction[i], translation[i], which_structure);
}
/*
* Compute the cusp shape.
*/
for (i = 0; i < 2; i++) /* i = ultimate, penultimate */
shape[i] = complex_div(translation[L][i], translation[M][i]); /* will handle division by Zero correctly */
/*
* Record the cusp shape and its accuracy.
*/
cusp->cusp_shape[which_structure] = shape[ultimate];
cusp->shape_precision[which_structure] = complex_decimal_places_of_accuracy(shape[ultimate], shape[penultimate]);
/*
* Adjust for the fact that the meridian and/or the longitude may have
* been traced backwards.
*/
if (direction[M] != direction[L])
{
cusp->cusp_shape[which_structure].real = - cusp->cusp_shape[which_structure].real;
cusp->cusp_shape[which_structure].imag = - cusp->cusp_shape[which_structure].imag;
}
/*
* As explained at the top of this file, the usual convention for the
* cusp shape requires viewing the cusp from the fat part of
* the manifold looking out, rather than from the cusp looking in, as
* in done in the rest of SnapPea. For this reason, we must take the
* complex conjugate of the final cusp shape.
*/
cusp->cusp_shape[which_structure].imag = - cusp->cusp_shape[which_structure].imag;
}
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 PositionedTet find_start(
Triangulation *manifold,
Cusp *cusp)
{
Tetrahedron *tet;
VertexIndex vertex;
Orientation orientation;
FaceIndex side;
PositionedTet ptet;
for (tet = manifold->tet_list_begin.next;
tet != &manifold->tet_list_end;
tet = tet->next)
for (vertex = 0; vertex < 4; vertex++)
{
if (tet->cusp[vertex] != cusp)
continue;
for (orientation = right_handed; orientation <= left_handed; orientation++)
for (side = 0; side < 4; side++)
if (side != vertex
&& tet->curve[M][orientation][vertex][side] != 0
&& tet->curve[L][orientation][vertex][side] != 0)
{
/*
* Record the current position of the Tetrahedron in
* a PositionedTet structure . . .
*/
ptet.tet = tet;
ptet.bottom_face = vertex;
ptet.near_face = side;
if (orientation == right_handed)
{
ptet.left_face = remaining_face[ptet.bottom_face][ptet.near_face];
ptet.right_face = remaining_face[ptet.near_face][ptet.bottom_face];
}
else
{
ptet.left_face = remaining_face[ptet.near_face][ptet.bottom_face];
ptet.right_face = remaining_face[ptet.bottom_face][ptet.near_face];
}
ptet.orientation = orientation;
/*
* . . . and return it.
*/
return ptet;
}
}
/*
* The program should never get to this point.
*/
uFatalError("find_start", "cusp_shapes");
/*
* The C++ compiler would like a return value, even though
* we never return from the uFatalError() call.
*/
return ptet;
}
|