def parallel_components( self ) -> dict[Lamination[Edge], tuple[int, bigger.Side[Edge], bool]]: """Return a dictionary mapping component to (multiplicity, side, is_arc) for each component of self that is parallel to an edge.""" components = dict() sides = set(side for edge in self.support() for side in self.triangulation.star(bigger.Side(edge))) for side in sides: if self(side) > 0: continue if side.orientation: # Don't double count. multiplicity = -self(side) if multiplicity > 0: components[self.triangulation.side_arc(side)] = ( multiplicity, side, True) around, twisting = float("inf"), float("inf") for index, (sidey, nxt) in enumerate( bigger.utilities.lookahead( self.triangulation.walk_vertex(side), 1)): value = self.left(sidey) around = min(around, value) # Always shrink around. if nxt == ~side: if 0 <= around < twisting < float("inf") and self.left( side) == self.right(side) == around: assert not isinstance(twisting, float) assert not isinstance(around, float) multiplicity = twisting - around components[self.triangulation.side_curve(side)] = ( multiplicity, side, False) break if index: # Only shrink twisting when it's not the first (or last) value. twisting = min(twisting, value) if around < 0 or twisting <= 0: # Terminate early. break return components
def meeting(self, edge: Edge) -> Lamination[Edge]: """Return the sublamination of self meeting the given edge. Note: self does not need to be finitely supported but the sublamination must be. Unfortunately we have no way of knowing this in advance.""" num_intersections = self(edge) start_side = bigger.Side(edge) intersections = set(range(num_intersections)) hits: Dict[Edge, int] = defaultdict(int) while intersections: start_intersection = next(iter(intersections)) last = None for side, intersection in self.trace(start_side, start_intersection): last = (side, intersection) hits[side.edge] += 1 if side == start_side: intersections.remove(intersection) elif side == ~start_side: intersections.remove(num_intersections - 1 - intersection) if last != (start_side, start_intersection): # We terminated into a vertex, so we must explore the other direction too. hits[start_side.edge] += 1 intersections.remove(start_intersection) for side, intersection in self.trace( ~start_side, num_intersections - 1 - start_intersection): hits[side.edge] += 1 if side == start_side: intersections.remove(intersection) elif side == ~start_side: intersections.remove(num_intersections - 1 - intersection) return self.triangulation(hits)
def support(triangulation: bigger.Triangulation[Edge], edge: Edge) -> tuple[Triangle[Edge], Triangle[Edge]]: """Return the two triangles that support and edge.""" side = bigger.Side(edge) return triangulation.triangle(side), triangulation.triangle(~side)
def supporting_sides(self) -> Iterable[bigger.Side[Edge]]: """Return the sides supporting this lamination.""" for edge in self.support(): for orientation in [True, False]: yield bigger.Side(edge, orientation)
def test_flip(self): s = bigger.Side(0, True) h = self.T.encode([{s}, {s}]) self.assertEqualSquares(h.target.link(s), h.source.link(s))