def _compute_margulis_tube_ends(tet, vertex): trig = tet.horotriangles[vertex] CF = tet.ShapeParameters[t3m.E01].parent() if tet.Class[vertex].is_complete: return [(0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0)] tet_vertices = [ tet.complex_vertices[v] for v in t3m.ZeroSubsimplices if v != vertex ] cusp_vertices = [ trig.vertex_positions[vertex | v] for v in t3m.ZeroSubsimplices if v != vertex ] std_to_tet = _matrix_taking_0_1_inf_to_given_points(*tet_vertices) cusp_to_std = _adjoint( _matrix_taking_0_1_inf_to_given_points(*cusp_vertices)) cusp_to_tet = std_to_tet * cusp_to_std cusp_to_tet_O13 = GL2C_to_O13(cusp_to_tet) return [cusp_to_tet_O13 * vector([1.0, x, 0.0, 0.0]) for x in [-1.0, 1.0]]
def _compute_margulis_tube_ends(tet, vertex): if tet.Class[vertex].is_complete: return [(0.0, 0.0, 0.0, 0.0), (0.0, 0.0, 0.0, 0.0)] return [ tet.cusp_to_tet_matrices[vertex] * vector([1.0, x, 0.0, 0.0]) for x in [-1.0, 1.0] ]
def _compute_edge_ends(self): cs = [vector(self.RF, [1, 1, 0, 0]), vector(self.RF, [1, -1, 0, 0])] def _compute_edge_ends(tet, perm): m = tet.permutahedron_matrices[perm] return [GL2C_to_O13(_adjoint(m)) * c for c in cs] for tet in self.mcomplex.Tetrahedra: tet.R13_edge_ends = { t3m.E01: _compute_edge_ends(tet, (0, 1, 2, 3)), t3m.E02: _compute_edge_ends(tet, (0, 2, 1, 3)), t3m.E12: _compute_edge_ends(tet, (2, 1, 0, 3)), t3m.E03: _compute_edge_ends(tet, (0, 3, 1, 2)), t3m.E13: _compute_edge_ends(tet, (1, 3, 0, 2)), t3m.E23: _compute_edge_ends(tet, (2, 3, 0, 1)) }
def _check_consistency(self): for tet in self.mcomplex.Tetrahedra: for F in t3m.TwoSubsimplices: for V in t3m.ZeroSubsimplices: if V & F: v0 = tet.O13_matrices[F] * vector(tet.R13_vertices[V]) v1 = tet.Neighbor[F].R13_vertices[tet.Gluing[F].image(V)] err = R13_dot(v0, v1) if err > 1e-10 or err < -1e-10: print("PROBLEM")
def edge_involution(u, v): b0 = vector(R13_normalise(u + v)) b1 = u - v b1 = vector(R13_normalise(b1 + b0 * R13_dot(b0, b1))) candidates = [ [ 0, 1, 0, 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ] penalties_and_candidates = [ (abs(R13_dot(b1, c)), vector(c)) for c in candidates ] penalties_and_candidates.sort() b2 = penalties_and_candidates[0][1] b3 = penalties_and_candidates[1][1] b2 = vector(R13_normalise(b2 + b0 * R13_dot(b0, b2) - b1 * R13_dot(b1, b2))) b3 = vector(R13_normalise(b3 + b0 * R13_dot(b0, b3) - b1 * R13_dot(b1, b3) - b2 * R13_dot(b2, b3))) bs = [ b0, b1, b2, b3 ] m = [ matrix([[x * y for x in _change_first_sign(b)] for y in b]) for b in bs ] return - m[0] + m[1] - m[2] - m[3]
def _check_consistency(self): for tet in self.mcomplex.Tetrahedra: for F in t3m.TwoSubsimplices: for V in t3m.ZeroSubsimplices: if V & F: v0 = tet.O13_matrices[F] * vector(tet.R13_vertices[V]) v1 = tet.Neighbor[F].R13_vertices[tet.Gluing[F].image( V)] if abs(R13_dot(v0, v1) - (-1.0)) > 1e-6: print("Inconsistency ", tet.Index, F) print(v0) print(v1)
def _compute_tet_vertices(self): c = vector(self.RF, [1, 0, 0, 0]) def _compute_vertex(tet, perm): m = tet.permutahedron_matrices[perm] return GL2C_to_O13(_adjoint(m)) * c for tet in self.mcomplex.Tetrahedra: tet.R13_vertices = { t3m.V0: _compute_vertex(tet, (0, 1, 3, 2)), t3m.V1: _compute_vertex(tet, (1, 0, 2, 3)), t3m.V2: _compute_vertex(tet, (2, 0, 3, 1)), t3m.V3: _compute_vertex(tet, (3, 0, 1, 2)) }
def _compute_planes(self): c = vector(self.RF, [0.0, 0.0, 0.0, -1.0]) def _compute_plane(tet, perm): m = tet.permutahedron_matrices[perm] v = c * GL2C_to_O13(m) return vector([-v[0], v[1], v[2], v[3]]) for tet in self.mcomplex.Tetrahedra: tet.R13_planes = { t3m.F0: _compute_plane(tet, (2, 3, 1, 0)), t3m.F1: _compute_plane(tet, (0, 3, 2, 1)), t3m.F2: _compute_plane(tet, (0, 1, 3, 2)), t3m.F3: _compute_plane(tet, (0, 2, 1, 3)) }
def complex_to_pair(z): return vector([z.real(), z.imag()])
def compute_translation_and_inverse_from_pick_point( self, size, frag_coord, depth): # Depth value emitted by shader is tanh(distance from camera) # Limit the maximal depth for orbiting to avoid numeric issues depth = min(depth, _max_depth_for_orbiting) fov = self.ui_uniform_dict['fov'][1] isIdeal = self.ui_parameter_dict['perspectiveType'][1] # Reimplement functionality from fragment shader # See get_ray_eye_space fov_scale = 2.0 * math.tan(fov / 360.0 * math.pi) # Reimplement computation of xy from fragment coordinate x = (frag_coord[0] - 0.5 * size[0]) / size[0] y = (frag_coord[1] - 0.5 * size[1]) / size[0] # Reimplement get_ray_eye_space to determine end point of # ray. The end point is encoded as pair distance to origin # direction to origin. if isIdeal: scaled_x = 0.5 * fov_scale * x scaled_y = 0.5 * fov_scale * y # Use "parabolic transformation magic by Saul" # to determine the start point and direction of ray. # Then compute end point using depth value. foo = 0.5 * (scaled_x * scaled_x + scaled_y * scaled_y) rayEnd = R13_normalise( vector([ RF((foo + 1.0) + depth * foo), RF(scaled_x + depth * scaled_x), RF(scaled_y + depth * scaled_y), RF(foo + depth * (foo - 1.0)) ])) # Distance of rayEnd from origin dist = math.acosh(rayEnd[0]) # Direction from origin to rayEnd dir = vector([rayEnd[1], rayEnd[2], rayEnd[3]]) else: scaled_x = fov_scale * x scaled_y = fov_scale * y # Camera is assumed to be at origin. dist = math.atanh(depth) # Reimplemented from get_ray_eye_space dir = vector([RF(scaled_x), RF(scaled_y), RF(-1.0)]) # Normalize direction dir = dir.normalized() # Compute the circumference of a circle of radius dist # # Do this by using a concentric circle in the Poincare disk # model poincare_dist = math.tanh(dist / 2.0) hyp_circumference_up_to_constant = ( poincare_dist / (1.0 - poincare_dist * poincare_dist)) speed = min( _max_orbit_speed, _max_linear_camera_speed / max(1e-10, hyp_circumference_up_to_constant)) # Compute translation along direction by distance. # And inverse. return (unit_3_vector_and_distance_to_O13_hyperbolic_translation( dir, dist), unit_3_vector_and_distance_to_O13_hyperbolic_translation( dir, -dist), speed)
def _compute_plane(tet, perm): m = tet.permutahedron_matrices[perm] v = c * GL2C_to_O13(m) return vector([-v[0], v[1], v[2], v[3]])