''' A module for representing triangulations of surface bundles over the circle.
Provides one class: Bundle. '''
import flipper
[docs]class Bundle:
''' This represents a triangulation of a surface bundle over the circle.
It is specified by a triangulation of the surface, a triangulation of the
bundle and an immersion map. Mapping classes can build their bundles and
this is the standard way users are expected to create these. '''
def __init__(self, triangulation, triangulation3, immersion):
assert isinstance(triangulation, flipper.kernel.Triangulation)
assert isinstance(triangulation3, flipper.kernel.Triangulation3)
assert isinstance(immersion, dict)
assert all(triangle in immersion for triangle in triangulation)
assert all(isinstance(immersion[triangle], (list, tuple)) for triangle in triangulation)
assert all(len(immersion[triangle]) == 2 for triangle in triangulation)
assert all(isinstance(immersion[triangle][0], flipper.kernel.Tetrahedron) for triangle in triangulation)
assert all(isinstance(immersion[triangle][1], flipper.kernel.Permutation) for triangle in triangulation)
self.triangulation = triangulation
self.triangulation3 = triangulation3
self.immersion = immersion
# This is a dict mapping:
# triangle |---> (tetrahedra, perm).
assert self.triangulation3.is_closed()
def __repr__(self):
return str(self)
def __str__(self):
return str(self.triangulation3)
[docs] def snappy_string(self, name='flipper_triangulation', filled=True):
''' Return the SnapPy string describing this triangulation.
If filled=True then the fake cusps are filled along their fibre slope. '''
fillings = [slope if filled_cusp else (0, 0) for filled_cusp, slope in zip(self.cusp_types(), self.fibre_slopes())] if filled else None
return self.triangulation3.snappy_string(name, fillings)
def __snappy__(self):
return self.snappy_string()
[docs] def cusp_types(self):
''' Return the list of the type of each cusp. '''
cusp_types = [None] * self.triangulation3.num_cusps
for corner_class in self.triangulation.corner_classes:
vertex = corner_class[0].vertex
filled = vertex.filled
for corner in corner_class:
tetrahedron, permutation = self.immersion[corner.triangle]
index = tetrahedron.cusp_indices[permutation(corner.side)]
if cusp_types[index] is None:
cusp_types[index] = filled
else:
assert cusp_types[index] == filled
return cusp_types
[docs] def fibre_slopes(self):
''' Return the list of fibre slopes on each cusp. '''
LONGITUDES, MERIDIANS = flipper.kernel.triangulation3.LONGITUDES, flipper.kernel.triangulation3.MERIDIANS
slopes = [None] * self.triangulation3.num_cusps
for index in range(self.triangulation3.num_cusps):
meridian_intersection, longitude_intersection = 0, 0
for corner_class in self.triangulation.corner_classes:
corner = corner_class[0]
tetra, perm = self.immersion[corner.triangle]
if tetra.cusp_indices[perm(corner.side)] == index:
for corner in corner_class:
tetra, perm = self.immersion[corner.triangle]
side, other = perm(corner.side), perm(3)
meridian_intersection += tetra.peripheral_curves[MERIDIANS][side][other]
longitude_intersection += tetra.peripheral_curves[LONGITUDES][side][other]
slopes[index] = (longitude_intersection, -meridian_intersection)
break
else:
raise RuntimeError('No vertex was mapped to this cusp.')
return slopes
[docs] def degeneracy_slopes(self):
''' Return the list of degeneracy slopes on each cusp.
This triangulation is must be veering. '''
assert self.triangulation3.is_veering()
VEERING_LEFT, VEERING_RIGHT = flipper.kernel.triangulation3.VEERING_LEFT, flipper.kernel.triangulation3.VEERING_RIGHT
TEMPS = flipper.kernel.triangulation3.TEMPS
VERTICES_MEETING = flipper.kernel.triangulation3.VERTICES_MEETING
EXIT_CUSP_LEFT, EXIT_CUSP_RIGHT = flipper.kernel.triangulation3.EXIT_CUSP_LEFT, flipper.kernel.triangulation3.EXIT_CUSP_RIGHT
slopes = [None] * self.triangulation3.num_cusps
cusp_pairing = self.triangulation3.cusp_identification_map()
for index, cusp in enumerate(self.triangulation3.cusps):
self.triangulation3.clear_temp_peripheral_structure()
# Set the degeneracy curve into the TEMPS peripheral structure.
# First find a good starting point:
start_tetrahedron, start_side = cusp[0]
edge_labels = [start_tetrahedron.get_edge_label(start_side, other) for other in VERTICES_MEETING[start_side]]
for i in range(3):
if edge_labels[(i+1) % 3] == VEERING_RIGHT and edge_labels[(i+2) % 3] == VEERING_LEFT:
start_other = VERTICES_MEETING[start_side][i]
break
# Then walk around, never crossing through an edge where both ends veer the same way.
current_tetrahedron, current_side, current_other = start_tetrahedron, start_side, start_other
while True:
current_tetrahedron.peripheral_curves[TEMPS][current_side][current_other] += 1
if start_tetrahedron.get_edge_label(current_side, current_other) == VEERING_LEFT:
leave = EXIT_CUSP_LEFT[(current_side, current_other)]
else:
leave = EXIT_CUSP_RIGHT[(current_side, current_other)]
current_tetrahedron.peripheral_curves[TEMPS][current_side][leave] -= 1
current_tetrahedron, current_side, current_other = cusp_pairing[(current_tetrahedron, current_side, leave)]
if (current_tetrahedron, current_side, current_other) == (start_tetrahedron, start_side, start_other):
break
slopes[index] = self.triangulation3.slope()
return slopes