static Boolean logs_sum_to_zero( Complex summand0, Orientation eo0, Complex summand1, Orientation eo1) { Complex sum; if (eo0 != eo1) summand1.real = - summand1.real; sum = complex_plus(summand0, summand1); normalize_angle(&sum.imag); return (complex_modulus(sum) < CANCELLATION_EPSILON); }
static void add_complex_with_log( ComplexWithLog *cwl0, Orientation eo0, ComplexWithLog *cwl1, Orientation eo1, ComplexWithLog *cwl2, Orientation eo2) { /* * First compute the sum of the logs, then recover * the rectangular form. * * We do all computations relative to the Orientation * of the EdgeClass. So if a particular edge is seen * as left_handed by the EdgeClass, we must negate the * real part of the log of its complex angle. (Recall * that all all TetShapes are stored relative to the * right_handed Orientation of the Tetrahedron.) */ Complex summand0, summand1, sum; summand0 = cwl0->log; if (eo0 == left_handed) summand0.real = - summand0.real; summand1 = cwl1->log; if (eo1 == left_handed) summand1.real = - summand1.real; sum = complex_plus(summand0, summand1); if (eo2 == left_handed) sum.real = - sum.real; normalize_angle(&sum.imag); cwl2->log = sum; cwl2->rect = complex_exp(sum); }
FuncResult change_peripheral_curves( Triangulation *manifold, CONST MatrixInt22 change_matrices[]) { int i, v, f, old_m, old_l; double old_m_coef, /* changed from int to double, JRW 2000/01/18 */ old_l_coef; Tetrahedron *tet; Cusp *cusp; Complex old_Hm, old_Hl; /* * First make sure all the change_matrices have determinant +1. */ for (i = 0; i < manifold->num_cusps; i++) if (DET2(change_matrices[i]) != +1) return func_bad_input; /* * The change_matrices for Klein bottle cusps must have zeros in the * off-diagonal entries. (Nothing else makes sense topologically.) */ for (cusp = manifold->cusp_list_begin.next; cusp != &manifold->cusp_list_end; cusp = cusp->next) if (cusp->topology == Klein_cusp) for (i = 0; i < 2; i++) if (change_matrices[cusp->index][i][!i] != 0) uFatalError("change_peripheral_curves", "change_peripheral_curves"); /* * Change the peripheral curves according to the change_matrices. * As stated at the top of this file, the transformation rule is * * | new m | | | | old m | * | | = | change_matrices[i] | | | * | new l | | | | old l | */ for (tet = manifold->tet_list_begin.next; tet != &manifold->tet_list_end; tet = tet->next) for (i = 0; i < 2; i++) /* which orientation */ for (v = 0; v < 4; v++) /* which vertex */ for (f = 0; f < 4; f++) /* which side */ { old_m = tet->curve[M][i][v][f]; old_l = tet->curve[L][i][v][f]; tet->curve[M][i][v][f] = change_matrices[tet->cusp[v]->index][0][0] * old_m + change_matrices[tet->cusp[v]->index][0][1] * old_l; tet->curve[L][i][v][f] = change_matrices[tet->cusp[v]->index][1][0] * old_m + change_matrices[tet->cusp[v]->index][1][1] * old_l; } /* * Change the Dehn filling coefficients to reflect the new * peripheral curves. That is, we want the Dehn filling curves * to be topologically the same as before, even though the * coefficients will be different because we changed the basis. * * To keep our thinking straight, let's imagine all peripheral * curves -- old and new -- in terms of some arbitrary but * fixed basis for pi_1(T^2) = Z + Z. We never actually compute * such a basis, but it helps keep our thinking straight. * Relative to this fixed basis, we have * * old m = (old m [0], old m [1]) * old l = (old l [0], old l [1]) * new m = (new m [0], new m [1]) * new l = (new l [0], new l [1]) * * Note that these m's and l's are curves, not coefficients! * They are elements of pi_1(T^2) = Z + Z. * * We can then rewrite the above transformation rule, with * each peripheral curve (old m, old l, new m and new l) * appearing as a row in a 2 x 2 matrix: * * | <--new m--> | | | | <--old m--> | * | | = | change_matrices[i] | | | * | <--new l--> | | | | <--old l--> | * * We can invert the change_matrix to solve for the old curves * in terms of the new ones: * -1 * | <--old m--> | | | | <--new m--> | * | | = | change_matrices[i] | | | * | <--old l--> | | | | <--new l--> | * * The Dehn filling curve is * * old_m_coef * old_m + old_l_coef * old_l * * = old_m_coef * [ change_matrices[i][1][1] * new_m * - change_matrices[i][0][1] * new_l] * + old_l_coef * [- change_matrices[i][1][0] * new_m * + change_matrices[i][0][0] * new_l] * * = new_m * [ old_m_coef * change_matrices[i][1][1] * - old_l_coef * change_matrices[i][1][0] ] * + new_l * [- old_m_coef * change_matrices[i][0][1] * + old_l_coef * change_matrices[i][0][0] ] * * Therefore * * new_m_coef = old_m_coef * change_matrices[i][1][1] * - old_l_coef * change_matrices[i][1][0] * new_l_coef = - old_m_coef * change_matrices[i][0][1] * + old_l_coef * change_matrices[i][0][0] */ for (cusp = manifold->cusp_list_begin.next; cusp != &manifold->cusp_list_end; cusp = cusp->next) if (cusp->is_complete == FALSE) { old_m_coef = cusp->m; old_l_coef = cusp->l; cusp->m = old_m_coef * change_matrices[cusp->index][1][1] - old_l_coef * change_matrices[cusp->index][1][0]; cusp->l = - old_m_coef * change_matrices[cusp->index][0][1] + old_l_coef * change_matrices[cusp->index][0][0]; } /* * Update the holonomies according to the rule * * | new H(m) | | | | old H(m) | * | | = | change_matrices[i] | | | * | new H(l) | | | | old H(l) | * * (These are actually logs of holonomies, so it's correct to * add -- not multiply -- them.) * * For complete Cusps the holonomies should be zero, but that's OK. */ for (cusp = manifold->cusp_list_begin.next; cusp != &manifold->cusp_list_end; cusp = cusp->next) if ( cusp->topology == torus_cusp || cusp->topology == Klein_cusp ) /* DJH */ for (i = 0; i < 2; i++) /* i = ultimate, penultimate */ { old_Hm = cusp->holonomy[i][M]; old_Hl = cusp->holonomy[i][L]; cusp->holonomy[i][M] = complex_plus( complex_real_mult( change_matrices[cusp->index][0][0], old_Hm ), complex_real_mult( change_matrices[cusp->index][0][1], old_Hl ) ); cusp->holonomy[i][L] = complex_plus( complex_real_mult( change_matrices[cusp->index][1][0], old_Hm ), complex_real_mult( change_matrices[cusp->index][1][1], old_Hl ) ); } /* * Update the cusp_shapes. */ for (cusp = manifold->cusp_list_begin.next; cusp != &manifold->cusp_list_end; cusp = cusp->next) if ( cusp->topology == torus_cusp || cusp->topology == Klein_cusp ) /* DJH */ { cusp->cusp_shape[initial] = transformed_cusp_shape( cusp->cusp_shape[initial], change_matrices[cusp->index]); if (cusp->is_complete == TRUE) cusp->cusp_shape[current] = transformed_cusp_shape( cusp->cusp_shape[current], change_matrices[cusp->index]); /* * else cusp->cusp_shape[current] == Zero, and needn't be changed. */ } return func_OK; }
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 compute_rhs( Triangulation *manifold) { EdgeClass *edge; Cusp *cusp; Complex desired_holonomy, current_holonomy, rhs; /* * The right hand side of each equation will be the desired value * of the edge angle sum or the holonomy (depending on whether it's * an edge equation or a cusp equation) minus the current value. * Thus, when the equations are solved and the Shapes of the * Tetrahedra are updated, the edge angle sums and the holonomies * will take on their desired values, to the accuracy of the * linear approximation. */ /* * Set the right hand side (rhs) of each edge equation. */ for ( edge = manifold->edge_list_begin.next; edge != &manifold->edge_list_end; edge = edge->next) { /* * The desired value of the sum of the logs of the complex * edge parameters is 2 pi i. The current value is * edge->edge_angle_sum. */ rhs = complex_minus(TwoPiI_on[ (int) edge->singular_order], edge->edge_angle_sum); /* DJH */ if (manifold->orientability == oriented_manifold || manifold->orientability == oriented_orbifold ) edge->complex_edge_equation[manifold->num_tetrahedra] = rhs; else { edge->real_edge_equation_re[2 * manifold->num_tetrahedra] = rhs.real; edge->real_edge_equation_im[2 * manifold->num_tetrahedra] = rhs.imag; } } /* * Set the right hand side (rhs) of each cusp equation. */ for (cusp = manifold->cusp_list_begin.next; cusp != &manifold->cusp_list_end; cusp = cusp->next) /* DJH */ if ( cusp->topology == torus_cusp || cusp->topology == Klein_cusp ) { /* * For complete cusps we want the log of the holonomy of the * meridian to be zero. For Dehn filled cusps we want the * log of the holonomy of the Dehn filling curve to be 2 pi i. */ if (cusp->is_complete) { desired_holonomy = Zero; current_holonomy = cusp->holonomy[ultimate][M]; } else { desired_holonomy = TwoPiI; current_holonomy = complex_plus( complex_real_mult(cusp->m, cusp->holonomy[ultimate][M]), complex_real_mult(cusp->l, cusp->holonomy[ultimate][L]) ); } rhs = complex_minus(desired_holonomy, current_holonomy); if (manifold->orientability == oriented_manifold || manifold->orientability == oriented_orbifold ) /* DJH */ cusp->complex_cusp_equation[manifold->num_tetrahedra] = rhs; else { cusp->real_cusp_equation_re[2 * manifold->num_tetrahedra] = rhs.real; cusp->real_cusp_equation_im[2 * manifold->num_tetrahedra] = rhs.imag; } } }
static void compute_derivative( Triangulation *manifold) { Tetrahedron *tet; Complex z[3], d[3], *eqn_coef = NULL, dz[2]; EdgeIndex e; VertexIndex v; FaceIndex initial_side, terminal_side; int init[2][2], term[2][2]; double m, l, a, b, *eqn_coef_00 = NULL, *eqn_coef_01 = NULL, *eqn_coef_10 = NULL, *eqn_coef_11 = NULL; int i, j; for (tet = manifold->tet_list_begin.next; tet != &manifold->tet_list_end; tet = tet->next) { /* * Note the three edge parameters. */ for (i = 0; i < 3; i++) z[i] = tet->shape[filled]->cwl[ultimate][i].rect; /* * Set the derivatives of log(z0), log(z1) and log(z2) * with respect to the given coordinate system, as * indicated by the above table. */ switch (tet->coordinate_system) { case 0: d[0] = One; d[1] = complex_div(MinusOne, z[2]); d[2] = complex_minus(Zero, z[1]); break; case 1: d[0] = complex_minus(Zero, z[2]); d[1] = One; d[2] = complex_div(MinusOne, z[0]); break; case 2: d[0] = complex_div(MinusOne, z[1]); d[1] = complex_minus(Zero, z[0]); d[2] = One; break; } /* * Record this tetrahedron's contribution to the edge equations. */ for (e = 0; e < 6; e++) /* Look at each of the six edges. */ { /* * Find the matrix entry(ies) corresponding to the * derivative of the edge equation with respect to this * tetrahedron. If the manifold is oriented it will be * a single entry in the complex matrix. If the manifold * is unoriented it will be a 2 x 2 block in the real matrix. */ if (manifold->orientability == oriented_manifold || manifold->orientability == oriented_orbifold ) /* DJH */ eqn_coef = &tet->edge_class[e]->complex_edge_equation[tet->index]; else { eqn_coef_00 = &tet->edge_class[e]->real_edge_equation_re[2 * tet->index]; eqn_coef_01 = &tet->edge_class[e]->real_edge_equation_re[2 * tet->index + 1]; eqn_coef_10 = &tet->edge_class[e]->real_edge_equation_im[2 * tet->index]; eqn_coef_11 = &tet->edge_class[e]->real_edge_equation_im[2 * tet->index + 1]; } /* * Add in the derivative of the log of the edge parameter * with respect to the chosen coordinate system. Please * see the comment preceding this function for details. */ if (manifold->orientability == oriented_manifold || manifold->orientability == oriented_orbifold ) /* DJH */ *eqn_coef = complex_plus(*eqn_coef, d[edge3[e]]); else { /* * These are the same a and b as in the comment * preceding this function. */ a = d[edge3[e]].real; b = d[edge3[e]].imag; if (tet->edge_orientation[e] == right_handed) { *eqn_coef_00 += a; *eqn_coef_01 -= b; *eqn_coef_10 += b; *eqn_coef_11 += a; } else { *eqn_coef_00 -= a; *eqn_coef_01 += b; *eqn_coef_10 += b; *eqn_coef_11 += a; } } } /* * Record this tetrahedron's contribution to the cusp equations. */ for (v = 0; v < 4; v++) /* Look at each ideal vertex. */ { /* * Note the Dehn filling coefficients on this cusp. * If the cusp is complete, use m = 1.0 and l = 0.0. */ if (tet->cusp[v]->is_complete) /* DJH : not sure ? */ { m = 1.0; l = 0.0; } else { m = tet->cusp[v]->m; l = tet->cusp[v]->l; } /* * Find the matrix entry(ies) corresponding to the * derivative of the cusp equation with respect to this * tetrahedron. If the manifold is oriented it will be * a single entry in the complex matrix. If the manifold * is unoriented it will be a 2 x 2 block in the real matrix. */ /* DJH */ if ( (manifold->orientability == oriented_manifold || manifold->orientability == oriented_orbifold ) && ( tet->cusp[v]->topology == torus_cusp || tet->cusp[v]->topology == Klein_cusp ) ) eqn_coef = &tet->cusp[v]->complex_cusp_equation[tet->index]; else { eqn_coef_00 = &tet->cusp[v]->real_cusp_equation_re[2 * tet->index]; eqn_coef_01 = &tet->cusp[v]->real_cusp_equation_re[2 * tet->index + 1]; eqn_coef_10 = &tet->cusp[v]->real_cusp_equation_im[2 * tet->index]; eqn_coef_11 = &tet->cusp[v]->real_cusp_equation_im[2 * tet->index + 1]; } /* * Each ideal vertex contains two triangular cross sections, * one right_handed and the other left_handed. We want to * compute the contribution of each angle of each triangle * to the holonomy. We begin by considering the right_handed * triangle, looking at each of its three angles. A directed * angle is specified by its initial and terminal sides. * We find the number of strands of the Dehn filling curve * passing from the initial side to the terminal side; * it is m * (number of strands of meridian) * + l * (number of strands of longitude), where (m,l) are * the Dehn filling coefficients (in practice, m and l need * not be integers, but it's simpler to imagine them to be * integers as you try to understand the following code). * The number of strands of the Dehn filling curves passing * from the initial to the terminal side is multiplied by * the derivative of the log of the complex angle, to yield * the contribution to the derivative matrix. If the manifold * is oriented, that complex number is added directly to * the relevant matrix entry. If the manifold is unoriented, * we convert the complex number to a 2 x 2 real matrix * (cf. the comments preceding this function) and add it to * the appropriate 2 x 2 block of the real derivative matrix. * The 2 x 2 matrix for the left_handed triangle is modified * to account for the fact that although the real part of the * derivative of the log (i.e. the compression/expansion * factor) is the same, the imaginary part (i.e. the rotation) * is negated. [Note that in computing the edge equations * the real part was negated, while for the cusp equations * the imaginary part is negated. I will leave an explanation * of the difference as an exercise for the reader.] * * Note that we cannot possibly handle curves on the * left_handed sheet of the orientation double cover of * a cusp of an oriented manifold. The reason is that the * log of the holonomy of the Dehn filling curve is not * a complex analytic function of the shape of the tetrahedron * (it's the complex conjugate of such a function). I.e. * it doesn't have a derivative in the complex sense. This * is why we make the convention that all peripheral curves * in oriented manifolds lie on the right_handed sheet of * the double cover. */ for (initial_side = 0; initial_side < 4; initial_side++) { if (initial_side == v) continue; terminal_side = remaining_face[v][initial_side]; /* * Note the intersection numbers of the meridian and * longitude with the initial and terminal sides. */ for (i = 0; i < 2; i++) { /* which curve */ for (j = 0; j < 2; j++) { /* which sheet */ init[i][j] = tet->curve[i][j][v][initial_side]; term[i][j] = tet->curve[i][j][v][terminal_side]; } } /* * For each triangle (right_handed and left_handed), * multiply the number of strands of the Dehn filling * curve running from initial_side to terminal_side * by the derivative of the log of the edge parameter. */ for (i = 0; i < 2; i++) /* which sheet */ dz[i] = complex_real_mult( m * FLOW(init[M][i],term[M][i]) + l * FLOW(init[L][i],term[L][i]), d[ edge3_between_faces[initial_side][terminal_side] ] ); /* * If the manifold is oriented, the Dehn filling curve * must lie of the right_handed sheet of the orientation * double cover (cf. above). Add its contributation to * the cusp equation. */ /* DJH */ if (manifold->orientability == oriented_manifold || manifold->orientability == oriented_orbifold ) *eqn_coef = complex_plus(*eqn_coef, dz[right_handed]); /* "else" follows below */ /* * If the manifold is unoriented, treat the right_ and * left_handed sheets separately. Add in the contribution * of the right_handed sheet normally. For the left_handed * sheet, we must account for the fact that even though * the modulus of the derivative (i.e. the expansion/ * contraction factor) is correct, its argument (i.e. the * angle of rotation) is the negative of what it should be. */ else { a = dz[right_handed].real; b = dz[right_handed].imag; *eqn_coef_00 += a; *eqn_coef_01 -= b; *eqn_coef_10 += b; *eqn_coef_11 += a; a = dz[left_handed].real; b = dz[left_handed].imag; *eqn_coef_00 += a; *eqn_coef_01 -= b; *eqn_coef_10 -= b; *eqn_coef_11 -= a; } } } } }
void compute_core_geodesic( Cusp *cusp, int *singularity_index, Complex length[2]) { int i; long int positive_d, negative_c; Real pi_over_n; /* * If the Cusp is unfilled or the Dehn filling coefficients aren't * integers, then just write in some zeros (as explained at the top * of this file) and return. */ if (cusp->is_complete == TRUE || Dehn_coefficients_are_integers(cusp) == FALSE) { *singularity_index = 0; length[ultimate] = Zero; length[penultimate] = Zero; return; } /* * The euclidean_algorithm() will give the singularity index * directly (as the g.c.d.), and the coefficients lead to the * complex length (cf. the explanation at the top of this file). */ *singularity_index = euclidean_algorithm( (long int) cusp->m, (long int) cusp->l, &positive_d, &negative_c); for (i = 0; i < 2; i++) /* i = ultimate, penultimate */ { /* * length[i] = c H(m) + d H(l) * * (The holonomies are already in logarithmic form.) */ length[i] = complex_plus( complex_real_mult( (Real) (double)(- negative_c), cusp->holonomy[i][M] ), complex_real_mult( (Real) (double) positive_d, cusp->holonomy[i][L] ) ); /* * Make sure the length is positive. */ if (length[i].real < 0.0) length[i] = complex_negate(length[i]); /* * We want to normalize the torsion to the range * [-pi/n + epsilon, pi/n + epsilon], where n is * the order of the singular locus. */ pi_over_n = PI / *singularity_index; while (length[i].imag < - pi_over_n + TORSION_EPSILON) length[i].imag += 2.0 * pi_over_n; while (length[i].imag > pi_over_n + TORSION_EPSILON) length[i].imag -= 2.0 * pi_over_n; /* * In the case of a Klein bottle cusp, H(m) will be purely * rotational and H(l) will be purely translational * (cf. the documentation at the top of holonomy.c). * But the longitude used in practice is actually the * double cover of the true longitude, so we have to * divide the core_length by two to compensate. */ if (cusp->topology == Klein_cusp) length[i].real /= 2.0; } }
void compute_fourth_corner( Complex corner[4], VertexIndex missing_corner, Orientation orientation, ComplexWithLog cwl[3]) { int i; VertexIndex v[4]; Complex z[4], cross_ratio, diff20, diff21, numerator, denominator; /* * Given the locations on the sphere at infinity in * the upper half space model of three of a Tetrahedron's * four ideal vertices, compute_fourth_corner() computes * the location of the remaining corner. * * corner[4] is the array which contains the three known * corners, and into which the fourth will be * written. * * missing_corner is the index of the unknown corner. * * orientation is the Orientation with which the Tetrahedron * is currently being viewed. * * cwl[3] describes the shape of the Tetrahedron. */ /* * Set up an indexing scheme v[] for the vertices. * * If some vertex (!= missing_corner) is positioned at infinity, let its * index be v0. Otherwise choose v0 arbitrarily. Then choose * v2 and v3 so that the Tetrahedron looks right_handed relative * to the v[]. */ v[3] = missing_corner; v[0] = ! missing_corner; for (i = 0; i < 4; i++) if (i != missing_corner && complex_infinite(corner[i])) v[0] = i; if (orientation == right_handed) { v[1] = remaining_face[v[3]][v[0]]; v[2] = remaining_face[v[0]][v[3]]; } else { v[1] = remaining_face[v[0]][v[3]]; v[2] = remaining_face[v[3]][v[0]]; } /* * Let z[i] be the location of v[i]. * The z[i] are known for i < 3, unknown for i == 3. */ for (i = 0; i < 3; i++) z[i] = corner[v[i]]; /* * Note the cross_ratio at the edge connecting v0 to v1. */ cross_ratio = cwl[edge3_between_faces[v[0]][v[1]]].rect; if (orientation == left_handed) cross_ratio = complex_conjugate(complex_div(One, cross_ratio)); /* * The cross ratio is defined as * * (z3 - z1) (z2 - z0) * cross_ratio = ----------------------- * (z2 - z1) (z3 - z0) * * Solve for z3. * * z1*(z2 - z0) - cross_ratio*z0*(z2 - z1) * z3 = ----------------------------------------- * (z2 - z0) - cross_ratio*(z2 - z1) * * If z0 is infinite, this reduces to * * z3 = z1 + cross_ratio * (z2 - z1) * * which makes sense geometrically. */ if (complex_infinite(z[0]) == TRUE) z[3] = complex_plus( z[1], complex_mult( cross_ratio, complex_minus(z[2], z[1]) ) ); else { diff20 = complex_minus(z[2], z[0]); diff21 = complex_minus(z[2], z[1]); numerator = complex_minus( complex_mult(z[1], diff20), complex_mult( cross_ratio, complex_mult(z[0], diff21) ) ); denominator = complex_minus( diff20, complex_mult(cross_ratio, diff21) ); z[3] = complex_div(numerator, denominator); /* will handle division by Zero correctly */ } corner[missing_corner] = z[3]; }
static void initial_tetrahedron( Triangulation *manifold, Tetrahedron **initial_tet, Boolean compute_corners, Boolean centroid_at_origin) { VertexIndex v[4]; Complex z, sqrt_z, w[4]; Tetrahedron *tet; EdgeIndex best_edge, edge; /* * Set a default choice of tetrahedron and edge. */ *initial_tet = manifold->tet_list_begin.next; best_edge = 0; /* * 2000/02/11 JRW Can we choose the initial tetrahedron in such * a way that if we happen to have the canonical triangulation * of a 2-bridge knot or link complement, the basepoint falls * at a center of D2 symmetry? That is, can we find a Tetrahedron * that looks like the "top of the tower" in the canonical * triangulation of a 2-bridge knot or link complement? */ for (tet = manifold->tet_list_begin.next; tet != &manifold->tet_list_end; tet = tet->next) for (edge = 0; edge < 6; edge++) if (tet->neighbor[one_face_at_edge [edge]] == tet->neighbor[other_face_at_edge[edge]]) { *initial_tet = tet; best_edge = edge; } if (compute_corners) { if (centroid_at_origin == TRUE) { /* * Proposition. For any value of w, positioning the corners at * * corner[0] = w * corner[1] = w^-1 * corner[2] = -w^-1 * corner[3] = -w * * defines a tetrahedron with its centroid at the "origin" and * the common perpendiculars between pairs of opposite edges * coincident with the "coordinate axes". [In the Klein model, * the tetrahedron is inscribed in a rectangular box whose faces * are parallel to the coordinate axes.] * * Proof: Use the observation that the line from a0 to a1 will * intersect the line from b0 to b1 iff the cross ratio * * (b0 - a0) (b1 - a1) * ------------------- * (b1 - a0) (b0 - a1) * * of the tetrahedron they span is real, and they will be * orthogonal iff the cross ratio is -1. * * [-w, w] is orthogonal to [0, infinity] because * * (0 - -w) (infinity - w) * ----------------------- = -1 * (infinity - -w) (0 - w) * * and similarly for [-w^-1, w^-1] and [0, infinity]. * * [w^-1, w] is orthogonal to [-1, 1] because * * (-1 - w^-1) (1 - w) * ------------------- = -1 * (1 - w^-1) (-1 - w) * * and similarly for [-w^-1, -w] and [-1, 1]. * * [-w^-1, w] is orthogonal to [-i, i] because * * (-i - -w^-1) (i - w) * -------------------- = -1 * (i - -w^-1) (-i - w) * * and similarly for [w^-1, -w] and [-i, i]. * * Q.E.D. * * * The tetrahedron will have the correct cross ratio z iff * * (w - -w^-1) (w^-1 - -w ) (w + w^-1)^2 * z = -------------------------- = -------------- * (w - -w ) (w^-1 - -w^-1) 4 * * Solving for w in terms of z gives the four possibilities * * w = +- (sqrt(z) +- sqrt(z - 1)) * * Note that sqrt(z) + sqrt(z - 1) and sqrt(z) - sqrt(z - 1) are * inverses of one another. We can choose any of the four solutions * to be "w", and the other three will automatically become w^-1, * -w, and -w^-1. * * Comment: This position for the initial corners brings out * nice numerical properties in the O(3,1) matrices for manifolds * composed of regular ideal tetrahedra (cf. the proofs in the * directory "Tilings of H^3", which aren't part of SnapPea, but * I could give you a copy). */ z = (*initial_tet)->shape[filled]->cwl[ultimate][0].rect; w[0] = complex_plus( complex_sqrt(z), complex_sqrt(complex_minus(z, One)) ); w[1] = complex_div(One, w[0]); w[2] = complex_negate(w[1]); w[3] = complex_negate(w[0]); (*initial_tet)->corner[0] = w[0]; (*initial_tet)->corner[1] = w[1]; (*initial_tet)->corner[2] = w[2]; (*initial_tet)->corner[3] = w[3]; } else { /* * Originally this code positioned the Tetrahedron's vertices * at {0, 1, z, infinity}. As of 2000/02/04 I modified it * to put the vertices at {0, 1/sqrt(z), sqrt(z), infinity} instead, * so that the basepoint (0,0,1) falls at the midpoint * of the edge extending from 0 to infinity, and the * tetrahedron's symmetry axis lies parallel to the x-axis. * To convince yourself that the tetrahedron's axis of * symmetry does indeed pass through that point, note * that a half turn around the axis of symmetry factors * as a reflection in the plane |z| = 1 followed by * a reflection in the vertical plane sitting over x-axis. */ /* * Order the vertices so that the tetrahedron is positively * oriented, and the selected edge is between vertices * v[0] and v[1]. */ v[0] = one_vertex_at_edge[best_edge]; v[1] = other_vertex_at_edge[best_edge]; v[2] = remaining_face[v[1]][v[0]]; v[3] = remaining_face[v[0]][v[1]]; /* * Set the coordinates of the corners. */ z = (*initial_tet)->shape[filled]->cwl[ultimate][edge3[best_edge]].rect; sqrt_z = complex_sqrt(z); (*initial_tet)->corner[v[0]] = Infinity; (*initial_tet)->corner[v[1]] = Zero; (*initial_tet)->corner[v[2]] = complex_div(One, sqrt_z); (*initial_tet)->corner[v[3]] = sqrt_z; } } }
// expect wavelength in m int SetReflectivity(struct ElementType *ep, double wavelength) { double f1, f2, a, rho, energy, nt, delta, beta, ac, sinag, cosag, Rs, Rp; int z, myreturn; COMPLEX cn, cn2, cwu, crs, cts, crp, ctp, c1, c2, c3, csinag; char *material; struct ReflecType *rp; material= ep->MDat.material; rp= (struct ReflecType *)&ep->reflec; #ifdef DEBUG printf("debug: SetReflectivity called, material= >%s<, file= %s\n", material, __FILE__); #endif if (!(wavelength > 0.0)) { fprintf(stderr, "error SetReflectivity: wavelength not defined (%f)- return"); return 0; } energy= 1240e-9/ wavelength; myreturn= ReadMaterial(material, &z, &a, &rho); myreturn &= ReadHenke(material, energy, &f1, &f2); nt= 1e6* rho * NA / a; // Teilchendichte (1/m^3), rho is in (g/cm^3) delta= RE * pow(wavelength, 2) * nt * f1 / (2.0 * PI); beta = RE * pow(wavelength, 2) * nt * f2 / (2.0 * PI); ac = acos(1.0 - delta); // critical (grazing) angle in rad complex_in(&cn, (1.0- delta), beta); // complex index of refraction sinag= ep->geo.cosa; // sin(grazing angle) grazing angle in rad cosag= ep->geo.sina; // sin <-> cos change for grazing angle // we calculate the compex reflectivity and transmission coefficients // transmission coefficients not used so far complex_x (&cn, &cn, &cn2); // n^2 complex_in (&c1, pow(cosag, 2.0), 0.0); // cos(theta))^2 saved in c1 complex_minus(&cn2, &c1, &c2); // c2= n2- c1 complex_pow (&c2, 0.5, &cwu); // wu= sqrt(c2) complex_in (&csinag, sinag, 0.0); // sin(theta) saved in csinag complex_minus(&csinag, &cwu, &c1); // zehler in c1 complex_plus (&csinag, &cwu, &c2); // nenner in c2 complex_div (&c1, &c2, &crs); // calc crs complex_in (&c1, (2* sinag), 0.0); // zehler in c1 complex_div(&c1, &c2, &cts); // calc cts complex_x (&cn2, &csinag, &c3); // c3 complex_minus(&c3, &cwu, &c1); // zehler in c1 complex_plus (&c3, &cwu, &c2); // nenner in c2 complex_div (&c1, &c2, &crp); // calc crp complex_in (&c1, 2.0, 0.0); // 2.0 in c1 complex_x (&c1, &cn, &c3); // 2n in c3 complex_x (&c3, &csinag, &c1); // zaehler in c1 complex_div(&c1, &c2, &ctp); // calc ctp Rs= pow(crs.re, 2)+ pow(crs.im, 2); // abs()^2 Rp= pow(crp.re, 2)+ pow(crp.im, 2); // abs()^2; rp->runpol= 0.5 * (Rs + Rp); // fill double ryamp, rypha, rzamp, rzpha, runpol; switch (ep->GDat.azimut) /* vertikal 0; nach links 1; nach unten 2 ; nach rechts 3 */ { case 0: case 2: rp->ryamp= sqrt(pow(crp.re, 2)+ pow(crp.im, 2)); rp->rypha= atan2(crp.im, crp.re); rp->rzamp= sqrt(pow(crs.re, 2)+ pow(crs.im, 2)); rp->rzpha= atan2(crs.im, crs.re); break; case 1: case 3: rp->rzamp= sqrt(pow(crp.re, 2)+ pow(crp.im, 2)); rp->rzpha= atan2(crp.im, crp.re); rp->ryamp= sqrt(pow(crs.re, 2)+ pow(crs.im, 2)); rp->rypha= atan2(crs.im, crs.re); break; default: fprintf(stderr, "error in file %s- azimut >>%d<<out of range\n", __FILE__, ep->GDat.azimut); exit(-1); } return myreturn; } // SetReflectivity