Exemplo n.º 1
0
    def push_vector_forward(self,tangent_vector):
        r"""Applies the mapping to the provided vector."""
        ring = tangent_vector.bundle().base_ring()
        if tangent_vector.polygon_label() == self._p:
            point=tangent_vector.point()
            vertex1=self._domain.polygon(self._p).vertex(self._v1)
            vertex2=self._domain.polygon(self._p).vertex(self._v2)

            wp = wedge_product(vertex2-vertex1,point-vertex1)

            if wp > 0:
                # in new polygon 1
                return self.codomain().tangent_vector( \
                    self._p, \
                    self._tp(tangent_vector.point()), \
                    tangent_vector.vector(), \
                    ring = ring)
            if wp < 0:
                # in new polygon 2
                return self.codomain().tangent_vector( \
                    self._new_label, \
                    self._tnew_label(tangent_vector.point()), \
                    tangent_vector.vector(), \
                    ring = ring)

            # Otherwise wp==0
            w = tangent_vector.vector()
            wp = wedge_product(vertex2-vertex1,w)
            if wp > 0:
                # in new polygon 1
                return self.codomain().tangent_vector( \
                    self._p, \
                    self._tp(tangent_vector.point()), \
                    tangent_vector.vector(), \
                    ring = ring)
            # in new polygon 2
            return self.codomain().tangent_vector( \
                self._new_label, \
                self._tnew_label(tangent_vector.point()), \
                tangent_vector.vector(), \
                ring = ring)
        else:
            # Not in a polygon that was changed. Just copy the data.
            return self._codomain.tangent_vector( \
                tangent_vector.polygon_label(), \
                tangent_vector.point(), \
                tangent_vector.vector(), \
                ring = ring)
Exemplo n.º 2
0
    def push_vector_forward(self, tangent_vector):
        r"""Applies the mapping to the provided vector."""
        ring = tangent_vector.bundle().base_ring()
        if tangent_vector.polygon_label() == self._p:
            point = tangent_vector.point()
            vertex1 = self._domain.polygon(self._p).vertex(self._v1)
            vertex2 = self._domain.polygon(self._p).vertex(self._v2)

            wp = wedge_product(vertex2 - vertex1, point - vertex1)

            if wp > 0:
                # in new polygon 1
                return self.codomain().tangent_vector( \
                    self._p, \
                    self._tp(tangent_vector.point()), \
                    tangent_vector.vector(), \
                    ring = ring)
            if wp < 0:
                # in new polygon 2
                return self.codomain().tangent_vector( \
                    self._new_label, \
                    self._tnew_label(tangent_vector.point()), \
                    tangent_vector.vector(), \
                    ring = ring)

            # Otherwise wp==0
            w = tangent_vector.vector()
            wp = wedge_product(vertex2 - vertex1, w)
            if wp > 0:
                # in new polygon 1
                return self.codomain().tangent_vector( \
                    self._p, \
                    self._tp(tangent_vector.point()), \
                    tangent_vector.vector(), \
                    ring = ring)
            # in new polygon 2
            return self.codomain().tangent_vector( \
                self._new_label, \
                self._tnew_label(tangent_vector.point()), \
                tangent_vector.vector(), \
                ring = ring)
        else:
            # Not in a polygon that was changed. Just copy the data.
            return self._codomain.tangent_vector( \
                tangent_vector.polygon_label(), \
                tangent_vector.point(), \
                tangent_vector.vector(), \
                ring = ring)
Exemplo n.º 3
0
 def pull_vector_back(self, tangent_vector):
     r"""
     Applies the inverse of the mapping to the provided vector.
     """
     ring = tangent_vector.bundle().base_ring()
     if tangent_vector.polygon_label() == self._saved_label:
         p = tangent_vector.point()
         v = self._domain.polygon(self._saved_label).vertex(
             self._glued_edge)
         e = self._domain.polygon(self._saved_label).edge(self._glued_edge)
         from flatsurf.geometry.polygon import wedge_product
         wp = wedge_product(p - v, e)
         if wp > 0:
             # in polygon with the removed label
             return self.domain().tangent_vector( \
                 self._removed_label, \
                 (~ self._remove_map)(tangent_vector.point()), \
                 (~ self._remove_map_derivative)*tangent_vector.vector(), \
                 ring = ring)
         if wp < 0:
             # in polygon with the removed label
             return self.domain().tangent_vector( \
                 self._saved_label, \
                 tangent_vector.point(), \
                 tangent_vector.vector(), \
                 ring = ring)
         # Otherwise wp==0
         w = tangent_vector.vector()
         wp = wedge_product(w, e)
         if wp > 0:
             # in polygon with the removed label
             return self.domain().tangent_vector( \
                 self._removed_label, \
                 (~ self._remove_map)(tangent_vector.point()), \
                 (~ self._remove_map_derivative)*tangent_vector.vector(), \
                 ring = ring)
         return self.domain().tangent_vector( \
             self._saved_label, \
             tangent_vector.point(), \
             tangent_vector.vector(), \
             ring = ring)
     else:
         return self._domain.tangent_vector( \
             tangent_vector.polygon_label(), \
             tangent_vector.point(), \
             tangent_vector.vector(), \
             ring = ring)
Exemplo n.º 4
0
def edge_needs_flip_Linfinity(s, p1, e1):
    r"""
    Check whether the provided edge which bouds two triangles should be flipped
    to get closer to the L-infinity Delaunay decomposition.

    EXAMPLES::

        sage: from flatsurf import *
        sage: t1 = polygons(vertices=[(0,0), (1,0), (1,1)])
        sage: t2 = polygons(vertices=[(0,0), (1,1), (0,1)])
        sage: m = matrix(2, [2,1,1,1])
        sage: t1 = m*t1
        sage: t2 = m*t2
        sage: s = similarity_surfaces([t1,t2], {(0,0):(1,1), (0,1):(1,2), (0,2):(1,0)})

        sage: from flatsurf.geometry.mappings import edge_needs_flip_Linfinity
        sage: edge_needs_flip_Linfinity(s, 0, 0)
        False
        sage: edge_needs_flip_Linfinity(s, 0, 1)
        False
        sage: edge_needs_flip_Linfinity(s, 0, 2)
        True
        sage: edge_needs_flip_Linfinity(s, 1, 0)
        True
        sage: edge_needs_flip_Linfinity(s, 1, 1)
        False
        sage: edge_needs_flip_Linfinity(s, 1, 2)
        False
    """
    p2,e2 = s.opposite_edge(p1,e1)
    poly1 = s.polygon(p1)
    poly2 = s.polygon(p2)
    assert poly1.num_edges() == 3
    assert poly2.num_edges() == 3

    # convexity check of the quadrilateral
    if wedge_product(poly2.edge(e2-1), poly1.edge(e1+1)) <= 0 or \
       wedge_product(poly1.edge(e1-1), poly2.edge(e2+1)) <=0:
        return False

    # compare the norms
    edge1 = poly1.edge(e1)
    edge = poly2.edge(e2-1) + poly1.edge(e1+1)
    n1 = max(abs(edge1[0]), abs(edge1[1]))
    n = max(abs(edge[0]), abs(edge[1]))
    return n < n1
Exemplo n.º 5
0
 def pull_vector_back(self,tangent_vector):
     r"""
     Applies the inverse of the mapping to the provided vector.
     """
     ring = tangent_vector.bundle().base_ring()
     if tangent_vector.polygon_label() == self._saved_label:
         p=tangent_vector.point()
         v=self._domain.polygon(self._saved_label).vertex(self._glued_edge)
         e=self._domain.polygon(self._saved_label).edge(self._glued_edge)
         from flatsurf.geometry.polygon import wedge_product
         wp = wedge_product(p-v,e)
         if wp > 0:
             # in polygon with the removed label
             return self.domain().tangent_vector( \
                 self._removed_label, \
                 (~ self._remove_map)(tangent_vector.point()), \
                 (~ self._remove_map_derivative)*tangent_vector.vector(), \
                 ring = ring)
         if wp < 0:
             # in polygon with the removed label
             return self.domain().tangent_vector( \
                 self._saved_label, \
                 tangent_vector.point(), \
                 tangent_vector.vector(), \
                 ring = ring)
         # Otherwise wp==0
         w = tangent_vector.vector()
         wp = wedge_product(w,e)
         if wp > 0:
             # in polygon with the removed label
             return self.domain().tangent_vector( \
                 self._removed_label, \
                 (~ self._remove_map)(tangent_vector.point()), \
                 (~ self._remove_map_derivative)*tangent_vector.vector(), \
                 ring = ring)
         return self.domain().tangent_vector( \
             self._saved_label, \
             tangent_vector.point(), \
             tangent_vector.vector(), \
             ring = ring)
     else:
         return self._domain.tangent_vector( \
             tangent_vector.polygon_label(), \
             tangent_vector.point(), \
             tangent_vector.vector(), \
             ring = ring)
Exemplo n.º 6
0
def subdivide_a_polygon(s):
    r"""
    Return a SurfaceMapping which cuts one polygon along a diagonal or None if the surface is triangulated.
    """
    from flatsurf.geometry.polygon import wedge_product
    for l,poly in s.label_iterator(polygons=True):
        n = poly.num_edges() 
        if n>3:
            for i in xrange(n):
                e1=poly.edge(i)
                e2=poly.edge((i+1)%n)
                if wedge_product(e1,e2) != 0:
                    return SplitPolygonsMapping(s,l,i, (i+2)%n)
            raise ValueError("Unable to triangulate polygon with label "+str(l)+\
                ": "+str(poly))
    return None
Exemplo n.º 7
0
def subdivide_a_polygon(s):
    r"""
    Return a SurfaceMapping which cuts one polygon along a diagonal or None if the surface is triangulated.
    """
    from flatsurf.geometry.polygon import wedge_product
    for l, poly in s.label_iterator(polygons=True):
        n = poly.num_edges()
        if n > 3:
            for i in range(n):
                e1 = poly.edge(i)
                e2 = poly.edge((i + 1) % n)
                if wedge_product(e1, e2) != 0:
                    return SplitPolygonsMapping(s, l, i, (i + 2) % n)
            raise ValueError("Unable to triangulate polygon with label "+str(l)+\
                ": "+str(poly))
    return None
Exemplo n.º 8
0
    def triangulate(self, in_place=False):
        r"""
        Return a triangulated version of this surface.

        EXAMPLES::

            sage: from flatsurf import *
            sage: s=translation_surfaces.mcmullen_L(1,1,1,1)
            sage: ss=s.triangulate()
            sage: gs=ss.graphical_surface()
            sage: gs.make_all_visible()
            sage: print(gs)
            Graphical version of Similarity Surface TranslationSurface built from 6 polygons
        """
        if in_place:
            s=self
        else:
            s=self.mutable_copy()
        from flatsurf.geometry.polygon import wedge_product
        loop=True
        while loop:
            loop=False
            for l,poly in s.label_iterator(polygons=True):
                n = poly.num_edges() 
                if n>3:
                    for i in xrange(n):
                        e1=poly.edge(i)
                        e2=poly.edge((i+1)%n)
                        if wedge_product(e1,e2) != 0:
                            s.subdivide_polygon(l,i,(i+2)%n)
                            loop=True
                            break
                    if loop:
                        break
                    else:
                        # This should never happen:
                        raise ValueError("Unable to triangulate polygon with label "+ \
                            str(l)+": "+str(poly))
        return s
Exemplo n.º 9
0
    def triangle_flip(self, l1, e1, in_place=False, test=False):
        r"""
        Flips the diagonal of the quadrilateral formed by two triangles
        glued together along the provided edge (l1,e1). This can be broken 
        into two steps: join along the edge to form a convex quadilateral,
        then cut along the other diagonal. Raises a ValueError if this 
        quadrilateral would be non-convex.
        
        Parameters
        ----------
        l1
            label of polygon
        e1 : integer
            edge of the polygon
        in_place : boolean
            if True do the flip to the current surface which must be mutable. 
            In this case the updated surface will be returned.
            Otherwise a mutable copy is made and then an edge is flipped, which is then returned.
        test : boolean
            if True we don't actually flip, and we return True or False depending
            on whether or not the flip would be successful.
        
        EXAMPLES::

            sage: from flatsurf import *
            sage: s=similarity_surfaces.right_angle_triangle(ZZ(1),ZZ(1))
            sage: print(s.polygon(0))
            Polygon: (0, 0), (1, 0), (0, 1)
            sage: print s.triangle_flip(0, 0, test=True)
            False
            sage: print s.triangle_flip(0, 1, test=True)
            True
            sage: print s.triangle_flip(0, 2, test=True)
            False

            sage: from flatsurf import *
            sage: s=similarity_surfaces.right_angle_triangle(ZZ(1),ZZ(1))
            sage: from flatsurf.geometry.surface import Surface_fast
            sage: s=s.__class__(Surface_fast(s, mutable=True))
            sage: try:
            ....:     s.triangle_flip(0,0,in_place=True)
            ....: except ValueError as e:
            ....:     print e
            Gluing triangles along this edge yields a non-convex quadrilateral.
            sage: print s.triangle_flip(0,1,in_place=True)
            ConeSurface built from 2 polygons
            sage: print s.polygon(0)
            Polygon: (0, 0), (1, 1), (0, 1)
            sage: print s.polygon(1)
            Polygon: (0, 0), (-1, -1), (0, -1)
            sage: for p in s.edge_iterator(gluings=True):
            ....:     print p
            ((0, 0), (1, 0))
            ((0, 1), (0, 2))
            ((0, 2), (0, 1))
            ((1, 0), (0, 0))
            ((1, 1), (1, 2))
            ((1, 2), (1, 1))
            sage: try:
            ....:     s.triangle_flip(0,2,in_place=True)
            ....: except ValueError as e:
            ....:     print e
            ....: 
            Gluing triangles along this edge yields a non-convex quadrilateral.

            sage: from flatsurf import *
            sage: p=polygons((2,0),(-1,3),(-1,-3))
            sage: s=similarity_surfaces.self_glued_polygon(p)
            sage: from flatsurf.geometry.surface import Surface_fast
            sage: s=s.__class__(Surface_fast(s,mutable=True))
            sage: s.triangle_flip(0,1,in_place=True)
            HalfTranslationSurface built from 1 polygon
            sage: for x in s.label_iterator(polygons=True):
            ....:     print x
            (0, Polygon: (0, 0), (3, 3), (1, 3))
            sage: for x in s.edge_iterator(gluings=True):
            ....:     print x
            ((0, 0), (0, 0))
            ((0, 1), (0, 1))
            ((0, 2), (0, 2))
            sage: TestSuite(s).run()
        """
        if test:
            # Just test if the flip would be successful
            p1=self.polygon(l1)
            if not p1.num_edges()==3:
                return false
            l2,e2 = self.opposite_edge(l1,e1)
            p2 = self.polygon(l2)
            if not p2.num_edges()==3:
                return false
            sim = self.edge_transformation(l2,e2)
            hol = sim( p2.vertex( (e2+2)%3 ) - p1.vertex((e1+2)%3) )
            from flatsurf.geometry.polygon import wedge_product
            return wedge_product(p1.edge((e1+2)%3), hol) > 0 and \
                wedge_product(p1.edge((e1+1)%3), hol) > 0
        if in_place:
            s=self.underlying_surface()
        else:
            from flatsurf.geometry.surface import Surface_fast
            s=Surface_fast(surface=self.underlying_surface(), \
                mutable=True, dictionary=True)
        p1=self.polygon(l1)
        if not p1.num_edges()==3:
            raise ValueError("The polygon with the provided label is not a triangle.")
        l2,e2 = self.opposite_edge(l1,e1)
        p2 = self.polygon(l2)
        if not p2.num_edges()==3:
            raise ValueError("The polygon opposite the provided edge is not a triangle.")
        sim = self.edge_transformation(l2,e2)
        m = sim.derivative()
        hol = sim( p2.vertex( (e2+2)%3 ) ) - p1.vertex((e1+2)%3)

        from flatsurf import polygons

        try:
            np1 = polygons(edges=[hol, m * p2.edge((e2+2)%3), p1.edge((e1+1)%3)])
            np2 = polygons(edges=[-hol, p1.edge((e1+2)%3), m * p2.edge((e2+1)%3)])
        except (ValueError, TypeError):
            raise ValueError("Gluing triangles along this edge yields a non-convex quadrilateral.")
        # Old gluings:
        pairs = [self.opposite_edge(l2,(e2+2)%3), \
            self.opposite_edge(l1,(e1+1)%3), \
            self.opposite_edge(l1,(e1+2)%3), \
            self.opposite_edge(l2,(e2+1)%3)]
        for i, (l,e) in enumerate(pairs):
            if l==l1:
                if e==(e1+1)%3:
                    pairs[i]=(l1,2)
                elif e==(e1+2)%3:
                    pairs[i]=(l2,1)
                else:
                    raise ValueError("Surfaced passed has errors in polygon gluings.")
            elif l==l2:
                if e==(e2+1)%3:
                    pairs[i]=(l2,2)
                elif e==(e2+2)%3:
                    pairs[i]=(l1,1)
                else:
                    raise ValueError("Surfaced passed has errors in polygon gluings.")
        if l1==l2:
            s.change_polygon(l1,np1)
            s.change_edge_gluing(l1,0,l1,0)
            s.change_edge_gluing(l1,1,pairs[0][0],pairs[0][1])
            s.change_edge_gluing(l1,2,pairs[1][0],pairs[1][1])
        else:
            s.change_polygon(l1,np1)
            s.change_polygon(l2,np2)
            s.change_edge_gluing(l1,0,l2,0)
            s.change_edge_gluing(l1,1,pairs[0][0],pairs[0][1])
            s.change_edge_gluing(l1,2,pairs[1][0],pairs[1][1])
            s.change_edge_gluing(l2,1,pairs[2][0],pairs[2][1])
            s.change_edge_gluing(l2,2,pairs[3][0],pairs[3][1])
        if in_place:
            return self
        else:
            return self.__class__(s)
Exemplo n.º 10
0
    def _edge_needs_flip_Linfinity(self, p1, e1, p2, e2):
        r"""
        Check whether the provided edge which bounds two triangles should be flipped
        to get closer to the L-infinity Delaunay decomposition.

        TESTS::

            sage: from flatsurf import *
            sage: s = Surface_list(base_ring=QQ)
            sage: t1 = polygons((1,0),(-1,1),(0,-1))
            sage: t2 = polygons((0,1),(-1,0),(1,-1))
            sage: s.add_polygon(polygons(vertices=[(0,0), (1,0), (0,1)]))
            0
            sage: s.add_polygon(polygons(vertices=[(1,1), (0,1), (1,0)]))
            1
            sage: s.change_polygon_gluings(0, [(1,0), (1,1), (1,2)])
            sage: s = TranslationSurface(s)
            sage: [s._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [False, False, False]

            sage: ss = matrix(2, [1,1,0,1]) * s
            sage: [ss._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [False, False, False]
            sage: ss = matrix(2, [1,0,1,1]) * s
            sage: [ss._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [False, False, False]

            sage: ss = matrix(2, [1,2,0,1]) * s
            sage: [ss._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [False, False, True]

            sage: ss = matrix(2, [1,0,2,1]) * s
            sage: [ss._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [True, False, False]
        """
        # safety check for now
        assert self.opposite_edge(p1, e1) == (p2, e2), "not opposite edges"

        # triangles
        poly1 = self.polygon(p1)
        poly2 = self.polygon(p2)
        if poly1.num_edges() != 3 or poly2.num_edges() != 3:
            raise ValueError("edge must be adjacent to two triangles")

        edge1 = poly1.edge(e1)
        edge1L = poly1.edge(e1 - 1)
        edge1R = poly1.edge(e1 + 1)
        edge2 = poly2.edge(e2)
        edge2L = poly2.edge(e2 - 1)
        edge2R = poly2.edge(e2 + 1)

        sim = self.edge_transformation(p2, e2)
        m = sim.derivative()  # matrix carrying p2 to p1
        if not m.is_one():
            edge2 = m * edge2
            edge2L = m * edge2L
            edge2R = m * edge2R

        # convexity check of the quadrilateral
        from flatsurf.geometry.polygon import wedge_product
        if wedge_product(edge2L, edge1R) <= 0 or \
           wedge_product(edge1L, edge2R) <=0:
            return False

        # compare the norms
        new_edge = edge2L + edge1R
        n1 = max(abs(edge1[0]), abs(edge1[1]))
        n = max(abs(new_edge[0]), abs(new_edge[1]))
        return n < n1
Exemplo n.º 11
0
    def __init__(self, s, label0, edges):
        r"""
        Construct a cylinder on the surface `s` from an initial label and a
        sequence of edges crossed.

        Parameters
        ----------
        s: A SimilaritySurface
            the surface conaining the cylinder
        label0: An initial label
            representing a polygon the cylinder passes through.
        edges: a list
            giving the sequence of edges the cylinder crosses until it closes.
        """
        self._s = s
        self._label0 = label0
        self._edges = tuple(edges)
        ss = s.minimal_cover(cover_type="planar")
        SG = SimilarityGroup(s.base_ring())
        labels = [(label0, SG.one())] # labels of polygons on the cover ss.
        for e in edges:
            labels.append(ss.opposite_edge(labels[-1],e)[0])
        if labels[0][0] != labels[-1][0]:
            raise ValueError("Combinatorial path does not close.")
        trans = labels[-1][1]
        if not trans.is_translation():
            raise NotImplemented("Only cylinders with translational monodromy are currently supported")
        m = trans.matrix()
        v = vector(s.base_ring(),(m[0][2],m[1][2])) # translation vector
        from flatsurf.geometry.polygon import wedge_product

        p = ss.polygon(labels[0])
        e = edges[0]
        min_y = wedge_product(v, p.vertex(e))
        max_y = wedge_product(v, p.vertex((e+1)%p.num_edges()))
        if min_y >= max_y:
            raise ValueError("Combinatorial data does not represent a cylinder")

        # Stores the vertices where saddle connections starts:
        min_list = [0]
        max_list = [0]

        for i in xrange(1, len(edges)):
            e = edges[i]
            p = ss.polygon(labels[i])
            y = wedge_product(v, p.vertex(e))
            if y == min_y:
                min_list.append(i)
            elif y > min_y:
                min_list = [i]
                min_y = y
                if min_y >= max_y:
                    raise ValueError("Combinatorial data does not represent a cylinder")
            y = wedge_product(v, p.vertex((e+1)%p.num_edges()))
            if y == max_y:
                max_list.append(i)
            elif y < max_y:
                max_list = [i]
                max_y = y
                if min_y >= max_y:
                    raise ValueError("Combinatorial data does not represent a cylinder")

        # Extract the saddle connections on the right side:
        from flatsurf.geometry.surface_objects import SaddleConnection
        sc_set_right = set()
        vertices = []
        for i in min_list:
            l = labels[i]
            p = ss.polygon(l)
            vertices.append((i,p.vertex(edges[i])))
        i,vert_i = vertices[-1]
        vert_i = vert_i - v
        j,vert_j = vertices[0]
        if vert_i != vert_j:
            li = labels[i]
            li = (li[0], SG(-v)*li[1])
            lio = ss.opposite_edge(li,edges[i])
            lj = labels[j]
            sc = SaddleConnection(s,
                                  (lio[0][0], (lio[1]+1) % ss.polygon(lio[0]).num_edges()),
                                  (~lio[0][1])(vert_j)-(~lio[0][1])(vert_i))
            sc_set_right.add(sc)
        i = j
        vert_i = vert_j
        for j,vert_j in vertices[1:]:
            if vert_i != vert_j:
                li = labels[i]
                li = (li[0], SG(-v)*li[1])
                lio = ss.opposite_edge(li,edges[i])
                lj = labels[j]
                sc = SaddleConnection(s,
                                      (lio[0][0], (lio[1]+1) % ss.polygon(lio[0]).num_edges()),
                                      (~lio[0][1])(vert_j)-(~lio[0][1])(vert_i),
                                      limit = j-i)
                sc_set_right.add(sc)
            i = j
            vert_i =vert_j

        # Extract the saddle connections on the left side:
        sc_set_left = set()
        vertices = []
        for i in max_list:
            l = labels[i]
            p = ss.polygon(l)
            vertices.append((i,p.vertex((edges[i]+1)%p.num_edges())))
        i,vert_i = vertices[-1]
        vert_i = vert_i - v
        j,vert_j = vertices[0]
        if vert_i != vert_j:
            li = labels[i]
            li = (li[0], SG(-v)*li[1])
            lio = ss.opposite_edge(li,edges[i])
            lj = labels[j]
            sc = SaddleConnection(s,
                                  (lj[0], (edges[j]+1) % ss.polygon(lj).num_edges()),
                                  (~lj[1])(vert_i)-(~lj[1])(vert_j))
            sc_set_left.add(sc)
        i = j
        vert_i =vert_j
        for j,vert_j in vertices[1:]:
            if vert_i != vert_j:
                li = labels[i]
                lio = ss.opposite_edge(li,edges[i])
                lj = labels[j]
                sc = SaddleConnection(s,
                                      (lj[0], (edges[j]+1) % ss.polygon(lj).num_edges()),
                                      (~lj[1])(vert_i)-(~lj[1])(vert_j))
                sc_set_left.add(sc)
            i = j
            vert_i =vert_j
        self._boundary1 = frozenset(sc_set_right)
        self._boundary2 = frozenset(sc_set_left)
        self._boundary = frozenset(self._boundary1.union(self._boundary2))

        edge_intersections = []
        i = min_list[0]
        l = labels[i]
        p = ss.polygon(l)
        right_point = p.vertex(edges[i]) # point on the right boundary
        i = max_list[0]
        l = labels[i]
        p = ss.polygon(l)
        left_point = p.vertex((edges[i]+1)%p.num_edges())
        from flatsurf.geometry.polygon import solve
        for i in xrange(len(edges)):
            l = labels[i]
            p = ss.polygon(l)
            e = edges[i]
            v1 = p.vertex(e)
            v2 = p.vertex((e+1)%p.num_edges())
            a,b = solve(left_point, v, v1, v2-v1)
            w1 = (~(l[1]))(v1 + b*(v2-v1))
            a,b = solve(right_point, v, v1, v2-v1)
            w2 = (~(l[1]))(v1 + b*(v2-v1))
            edge_intersections.append((w1,w2))

        polygons = []
        P = Polygons(s.base_ring())
        pair1 = edge_intersections[-1]
        l1 = labels[-2][0]
        e1 = edges[-1]
        for i in xrange(len(edges)):
            l2 = labels[i][0]
            pair2 = edge_intersections[i]
            e2 = edges[i]
            trans = s.edge_transformation(l1,e1)
            pair1p = (trans(pair1[0]), trans(pair1[1]))
            polygon_verts = [pair1p[0], pair1p[1]]
            if pair2[1] != pair1p[1]:
                polygon_verts.append(pair2[1])
            if pair2[0] != pair1p[0]:
                polygon_verts.append(pair2[0])
            polygons.append((l2,P(vertices=polygon_verts)))
            l1 = l2
            pair1 = pair2
            e1 = e2
        self._polygons = tuple(polygons)
Exemplo n.º 12
0
    def __init__(self, surface, start_data, direction,
            end_data=None, end_direction=None,
            holonomy=None, end_holonomy=None,
            check=True, limit=1000):
        r"""
        Construct a saddle connecton on a SimilaritySurface.

        The only necessary parameters are the surface, start_data, and direction
        (to start). If there is missing data that can not be inferred from the surface
        type, then a straight-line trajectory will be computed to confirm that this is
        indeed a saddle connection. The trajectory will pass through at most limit
        polygons before we give up.

        Details of the parameters are provided below.

        Parameters
        ----------
        surface : a SimilaritySurface
            which will contain the saddle connection being constructed.
        start_data : a pair
            consisting of the label of the polygon where the saddle connection starts
            and the starting vertex.
        direction : 2-dimensional vector with entries in the base_ring of the surface
            representing the direction the saddle connection is moving in (in the
            coordinates of the initial polygon).
        end_data : a pair
            consisting of the label of the polygon where the saddle connection terminates
            and the terminating vertex.
        end_direction : 2-dimensional vector with entries in the base_ring of the surface
            representing the direction to move backward from the end point (in the
            coordinates of the terminal polygon). If the surface is a DilationSurface
            or better this will be the negation of the direction vector. If the surface
            is a HalfDilation surface or better, then this will be either the direction
            vector or its negation. In either case the value can be inferred from the
            end_data.
        holonomy : 2-dimensional vector with entries in the base_ring of the surface
            the holonomy of the saddle connection measured from the start. To compute this
            you develop the saddle connection into the plane starting from the starting
            polygon.
        end_holonomy : 2-dimensional vector with entries in the base_ring of the surface
            the holonomy of the saddle connection measured from the end (with the opposite
            orientation). To compute this you develop the saddle connection into the plane
            starting from the terminating polygon. For a translation surface, this will be
            the negation of holonomy, and for a HalfTranslation surface it will be either
            equal to holonomy or equal to its negation. In both these cases the end_holonomy
            can be inferred and does not need to be passed to the constructor.
        check : boolean
            If all data above is provided or can be inferred, then when check=False this
            geometric data is not verified. With check=True the data is always verified
            by straight-line flow. Erroroneous data will result in a ValueError being thrown.
            Defaults to true.
        limit :
            The combinatorial limit (in terms of number of polygons crossed) to flow forward
            to check the saddle connection geometry.
        """
        from .similarity_surface import SimilaritySurface
        assert isinstance(surface,SimilaritySurface)
        self._s=surface

        # Sanitize the direction vector:
        V=self._s.vector_space()
        self._direction=V(direction)
        if self._direction==V.zero():
            raise ValueError("Direction must be nonzero.")
        # To canonicalize the direction vector we ensure its endpoint lies in the boundary of the unit square.
        xabs=self._direction[0].abs()
        yabs=self._direction[1].abs()
        if xabs>yabs:
            self._direction=self._direction/xabs
        else:
            self._direction=self._direction/yabs

        # Fix end_direction if not standard.
        if end_direction is not None:
            xabs=end_direction[0].abs()
            yabs=end_direction[1].abs()
            if xabs>yabs:
                end_direction=end_direction/xabs
            else:
                end_direction=end_direction/yabs

        self._start_data=tuple(start_data)

        if end_direction is None:
            from .half_dilation_surface import HalfDilationSurface
            from .dilation_surface import DilationSurface
            # Attempt to infer the end_direction.
            if isinstance(self._s,DilationSurface):
                end_direction=-self._direction
            elif isinstance(self._s,HalfDilationSurface) and end_data is not None:
                p=self._s.polygon(end_data[0])
                if wedge_product(p.edge(end_data[1]), self._direction)>=0 and \
                   wedge_product(p.edge( (p.num_edges()+end_data[1]-1)%p.num_edges() ), self._direction)>0:
                    end_direction=self._direction
                else:
                    end_direction=-self._direction

        if end_holonomy is None and holonomy is not None:
            # Attempt to infer the end_holonomy:
            from .half_translation_surface import HalfTranslationSurface
            from .translation_surface import TranslationSurface
            if isinstance(self._s,TranslationSurface):
                end_holonomy=-holonomy
            if isinstance(self._s,HalfTranslationSurface):
                if direction==end_direction:
                    end_holonomy=holonomy
                else:
                    end_holonomy=-holonomy

        if  end_data is None or end_direction is None or holonomy is None or end_holonomy is None or check:
            v=self.start_tangent_vector()
            traj=v.straight_line_trajectory()
            traj.flow(limit)
            if not traj.is_saddle_connection():
                raise ValueError("Did not obtain saddle connection by flowing forward. Limit="+str(limit))
            tv=traj.terminal_tangent_vector()
            self._end_data=(tv.polygon_label(), tv.vertex())
            if end_data is not None:
                if end_data!=self._end_data:
                    raise ValueError("Provided or inferred end_data="+str(end_data)+" does not match actual end_data="+str(self._end_data))
            self._end_direction=tv.vector()
            # Canonicalize again.
            xabs=self._end_direction[0].abs()
            yabs=self._end_direction[1].abs()
            if xabs>yabs:
                self._end_direction = self._end_direction / xabs
            else:
                self._end_direction = self._end_direction / yabs
            if end_direction is not None:
                if end_direction!=self._end_direction:
                    raise ValueError("Provided or inferred end_direction="+str(end_direction)+" does not match actual end_direction="+str(self._end_direction))

            if traj.segments()[0].is_edge():
                # Special case (The below method causes error if the trajectory is just an edge.)
                self._holonomy = self._s.polygon(start_data[0]).edge(start_data[1])
                self._end_holonomy = self._s.polygon(self._end_data[0]).edge(self._end_data[1])
            else:
                from .similarity import SimilarityGroup
                sim=SimilarityGroup(self._s.base_ring()).one()
                itersegs = iter(traj.segments())
                next(itersegs)
                for seg in itersegs:
                    sim = sim * self._s.edge_transformation(seg.start().polygon_label(),
                                                            seg.start().position().get_edge())
                self._holonomy = sim(traj.segments()[-1].end().point())- \
                    traj.initial_tangent_vector().point()
                self._end_holonomy = -( (~sim.derivative())*self._holonomy )

            if holonomy is not None:
                if holonomy!=self._holonomy:
                    print("Combinatorial length: "+str(traj.combinatorial_length()))
                    print("Start: "+str(traj.initial_tangent_vector().point()))
                    print("End: "+str(traj.terminal_tangent_vector().point()))
                    print("Start data:"+str(start_data))
                    print("End data:"+str(end_data))
                    raise ValueError("Provided holonomy "+str(holonomy)+
                                     " does not match computed holonomy of "+str(self._holonomy))
            if end_holonomy is not None:
                if end_holonomy!=self._end_holonomy:
                    raise ValueError("Provided or inferred end_holonomy "+str(end_holonomy)+
                                     " does not match computed end_holonomy of "+str(self._end_holonomy))
        else:
            self._end_data=tuple(end_data)
            self._end_direction=end_direction
            self._holonomy=holonomy
            self._end_holonomy=end_holonomy

        # Make vectors immutable
        self._direction.set_immutable()
        self._end_direction.set_immutable()
        self._holonomy.set_immutable()
        self._end_holonomy.set_immutable()
Exemplo n.º 13
0
    def rel_deformation(self, deformation, local=False, limit=100):
        r"""
        Perform a rel deformation of the surface and return the result.

        This algorithm currently assumes that all polygons affected by this deformation are
        triangles. That should be fixable in the future.

        INPUT:

        - ``deformation`` (dictionary) - A dictionary mapping singularities of
          the surface to deformation vectors (in some 2-dimensional vector
          space). The rel deformation being done will move the singularities
          (relative to each other) linearly to the provided vector for each
          vertex. If a singularity is not included in the dictionary then the
          vector will be treated as zero.

        - ``local`` - (boolean) - If true, the algorithm attempts to deform all
          the triangles making up the surface without destroying any of them.
          So, the area of the triangle must be positive along the full interval
          of time of the deformation.  If false, then the deformation must have
          a particular form: all vectors for the deformation must be paralell.
          In this case we achieve the deformation with the help of the SL(2,R)
          action and Delaunay triangulations.

        - ``limit`` (integer) - Restricts the length of the size of SL(2,R)
          deformations considered. The algorithm should be roughly worst time
          linear in limit.

        TODO:

        - Support arbitrary rel deformations.
        - Remove the requirement that triangles be used.

        EXAMPLES::

            sage: from flatsurf import *
            sage: s = translation_surfaces.arnoux_yoccoz(4)
            sage: field = s.base_ring()
            sage: a = field.gen()
            sage: V = VectorSpace(field,2)
            sage: deformation1 = {s.singularity(0,0):V((1,0))}
            sage: s1 = s.rel_deformation(deformation1).canonicalize()
            sage: deformation2 = {s.singularity(0,0):V((a,0))}
            sage: s2 = s.rel_deformation(deformation2).canonicalize()
            sage: m = Matrix([[a,0],[0,~a]])
            sage: s2.cmp((m*s1).canonicalize())
            0
        """
        s = self
        # Find a common field
        field = s.base_ring()
        for singularity, v in iteritems(deformation):
            if v.parent().base_field() != field:
                from sage.structure.element import get_coercion_model
                cm = get_coercion_model()
                field = cm.common_parent(field, v.parent().base_field())
        from sage.modules.free_module import VectorSpace
        vector_space = VectorSpace(field, 2)

        from collections import defaultdict
        vertex_deformation = defaultdict(
            vector_space.zero)  # dictionary associating the vertices.
        deformed_labels = set()  # list of polygon labels being deformed.

        for singularity, vect in iteritems(deformation):
            # assert s==singularity.surface()
            for label, v in singularity.vertex_set():
                vertex_deformation[(label, v)] = vect
                deformed_labels.add(label)
                assert s.polygon(label).num_edges() == 3

        from flatsurf.geometry.polygon import wedge_product, ConvexPolygons

        if local:

            ss = s.copy(mutable=True, new_field=field)
            us = ss.underlying_surface()

            P = ConvexPolygons(field)
            for label in deformed_labels:
                polygon = s.polygon(label)
                a0 = vector_space(polygon.vertex(1))
                b0 = vector_space(polygon.vertex(2))
                v0 = vector_space(vertex_deformation[(label, 0)])
                v1 = vector_space(vertex_deformation[(label, 1)])
                v2 = vector_space(vertex_deformation[(label, 2)])
                a1 = v1 - v0
                b1 = v2 - v0
                # We deform by changing the triangle so that its vertices 1 and 2 have the form
                # a0+t*a1 and b0+t*b1
                # respectively. We are deforming from t=0 to t=1.
                # We worry that the triangle degenerates along the way.
                # The area of the deforming triangle has the form
                # A0 + A1*t + A2*t^2.
                A0 = wedge_product(a0, b0)
                A1 = wedge_product(a0, b1) + wedge_product(a1, b0)
                A2 = wedge_product(a1, b1)
                if A2 != field.zero():
                    # Critical point of area function
                    c = A1 / (-2 * A2)
                    if field.zero() < c and c < field.one():
                        assert A0 + A1 * c + A2 * c**2 > field.zero(
                        ), "Triangle with label %r degenerates at critical point before endpoint" % label
                assert A0 + A1 + A2 > field.zero(
                ), "Triangle with label %r degenerates at or before endpoint" % label
                # Triangle does not degenerate.
                us.change_polygon(
                    label, P(vertices=[vector_space.zero(), a0 + a1, b0 + b1]))
            return ss

        else:  # Non local deformation
            # We can only do this deformation if all the rel vector are parallel.
            # Check for this.
            nonzero = None
            for singularity, vect in iteritems(deformation):
                vvect = vector_space(vect)
                if vvect != vector_space.zero():
                    if nonzero is None:
                        nonzero = vvect
                    else:
                        assert wedge_product(nonzero,vvect)==0, \
                            "In non-local deformation all deformation vectos must be parallel"
            assert nonzero is not None, "Deformation appears to be trivial."
            from sage.matrix.constructor import Matrix
            m = Matrix([[nonzero[0], -nonzero[1]], [nonzero[1], nonzero[0]]])
            mi = ~m
            g = Matrix([[1, 0], [0, 2]], ring=field)
            prod = m * g * mi
            ss = None
            k = 0
            while True:
                if ss is None:
                    ss = s.copy(mutable=True, new_field=field)
                else:
                    # In place matrix deformation
                    ss.apply_matrix(prod)
                ss.delaunay_triangulation(direction=nonzero, in_place=True)
                deformation2 = {}
                for singularity, vect in iteritems(deformation):
                    found_start = None
                    for label, v in singularity.vertex_set():
                        if wedge_product(s.polygon(label).edge(v),nonzero) >= 0 and \
                        wedge_product(nonzero,-s.polygon(label).edge((v+2)%3)) > 0:
                            found_start = (label, v)
                            found = None
                            for vv in range(3):
                                if wedge_product(ss.polygon(label).edge(vv),nonzero) >= 0 and \
                                wedge_product(nonzero,-ss.polygon(label).edge((vv+2)%3)) > 0:
                                    found = vv
                                    deformation2[ss.singularity(label,
                                                                vv)] = vect
                                    break
                            assert found is not None
                            break
                    assert found_start is not None
                try:
                    sss = ss.rel_deformation(deformation2, local=True)
                    sss.apply_matrix(mi * g**(-k) * m)
                    sss.delaunay_triangulation(direction=nonzero,
                                               in_place=True)
                    return sss
                except AssertionError as e:
                    pass
                k = k + 1
                if limit is not None and k >= limit:
                    assert False, "Exeeded limit iterations"
Exemplo n.º 14
0
    def _edge_needs_flip_Linfinity(self, p1, e1, p2, e2):
        r"""
        Check whether the provided edge which bounds two triangles should be flipped
        to get closer to the L-infinity Delaunay decomposition.

        TESTS::

            sage: from flatsurf import *
            sage: s = Surface_list(base_ring=QQ)
            sage: t1 = polygons((1,0),(-1,1),(0,-1))
            sage: t2 = polygons((0,1),(-1,0),(1,-1))
            sage: s.add_polygon(polygons(vertices=[(0,0), (1,0), (0,1)]))
            0
            sage: s.add_polygon(polygons(vertices=[(1,1), (0,1), (1,0)]))
            1
            sage: s.change_polygon_gluings(0, [(1,0), (1,1), (1,2)])
            sage: s = TranslationSurface(s)
            sage: [s._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [False, False, False]

            sage: ss = matrix(2, [1,1,0,1]) * s
            sage: [ss._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [False, False, False]
            sage: ss = matrix(2, [1,0,1,1]) * s
            sage: [ss._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [False, False, False]

            sage: ss = matrix(2, [1,2,0,1]) * s
            sage: [ss._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [False, False, True]

            sage: ss = matrix(2, [1,0,2,1]) * s
            sage: [ss._edge_needs_flip_Linfinity(0, i, 1, i) for i in range(3)]
            [True, False, False]
        """
        # safety check for now
        assert self.opposite_edge(p1, e1) == (p2, e2), "not opposite edges"

        # triangles
        poly1 = self.polygon(p1)
        poly2 = self.polygon(p2)
        if poly1.num_edges() != 3 or poly2.num_edges() != 3:
            raise ValueError("edge must be adjacent to two triangles")

        edge1 = poly1.edge(e1)
        edge1L = poly1.edge(e1 - 1)
        edge1R = poly1.edge(e1 + 1)
        edge2 = poly2.edge(e2)
        edge2L = poly2.edge(e2 - 1)
        edge2R = poly2.edge(e2 + 1)

        sim = self.edge_transformation(p2, e2)
        m = sim.derivative()   # matrix carrying p2 to p1
        if not m.is_one():
            edge2 = m * edge2
            edge2L = m * edge2L
            edge2R = m * edge2R

        # convexity check of the quadrilateral
        from flatsurf.geometry.polygon import wedge_product
        if wedge_product(edge2L, edge1R) <= 0 or \
           wedge_product(edge1L, edge2R) <=0:
            return False

        # compare the norms
        new_edge = edge2L + edge1R
        n1 = max(abs(edge1[0]), abs(edge1[1]))
        n = max(abs(new_edge[0]), abs(new_edge[1]))
        return n < n1
Exemplo n.º 15
0
    def __init__(self, s, label0, edges):
        r"""
        Construct a cylinder on the surface `s` from an initial label and a
        sequence of edges crossed.

        Parameters
        ----------
        s: A SimilaritySurface
            the surface conaining the cylinder
        label0: An initial label
            representing a polygon the cylinder passes through.
        edges: a list
            giving the sequence of edges the cylinder crosses until it closes.
        """
        self._s = s
        self._label0 = label0
        self._edges = tuple(edges)
        ss = s.minimal_cover(cover_type="planar")
        SG = SimilarityGroup(s.base_ring())
        labels = [(label0, SG.one())]  # labels of polygons on the cover ss.
        for e in edges:
            labels.append(ss.opposite_edge(labels[-1], e)[0])
        if labels[0][0] != labels[-1][0]:
            raise ValueError("Combinatorial path does not close.")
        trans = labels[-1][1]
        if not trans.is_translation():
            raise NotImplemented(
                "Only cylinders with translational monodromy are currently supported"
            )
        m = trans.matrix()
        v = vector(s.base_ring(), (m[0][2], m[1][2]))  # translation vector
        from flatsurf.geometry.polygon import wedge_product

        p = ss.polygon(labels[0])
        e = edges[0]
        min_y = wedge_product(v, p.vertex(e))
        max_y = wedge_product(v, p.vertex((e + 1) % p.num_edges()))
        if min_y >= max_y:
            raise ValueError(
                "Combinatorial data does not represent a cylinder")

        # Stores the vertices where saddle connections starts:
        min_list = [0]
        max_list = [0]

        for i in range(1, len(edges)):
            e = edges[i]
            p = ss.polygon(labels[i])
            y = wedge_product(v, p.vertex(e))
            if y == min_y:
                min_list.append(i)
            elif y > min_y:
                min_list = [i]
                min_y = y
                if min_y >= max_y:
                    raise ValueError(
                        "Combinatorial data does not represent a cylinder")
            y = wedge_product(v, p.vertex((e + 1) % p.num_edges()))
            if y == max_y:
                max_list.append(i)
            elif y < max_y:
                max_list = [i]
                max_y = y
                if min_y >= max_y:
                    raise ValueError(
                        "Combinatorial data does not represent a cylinder")

        # Extract the saddle connections on the right side:
        from flatsurf.geometry.surface_objects import SaddleConnection
        sc_set_right = set()
        vertices = []
        for i in min_list:
            l = labels[i]
            p = ss.polygon(l)
            vertices.append((i, p.vertex(edges[i])))
        i, vert_i = vertices[-1]
        vert_i = vert_i - v
        j, vert_j = vertices[0]
        if vert_i != vert_j:
            li = labels[i]
            li = (li[0], SG(-v) * li[1])
            lio = ss.opposite_edge(li, edges[i])
            lj = labels[j]
            sc = SaddleConnection(
                s, (lio[0][0], (lio[1] + 1) % ss.polygon(lio[0]).num_edges()),
                (~lio[0][1])(vert_j) - (~lio[0][1])(vert_i))
            sc_set_right.add(sc)
        i = j
        vert_i = vert_j
        for j, vert_j in vertices[1:]:
            if vert_i != vert_j:
                li = labels[i]
                li = (li[0], SG(-v) * li[1])
                lio = ss.opposite_edge(li, edges[i])
                lj = labels[j]
                sc = SaddleConnection(
                    s,
                    (lio[0][0], (lio[1] + 1) % ss.polygon(lio[0]).num_edges()),
                    (~lio[0][1])(vert_j) - (~lio[0][1])(vert_i),
                    limit=j - i)
                sc_set_right.add(sc)
            i = j
            vert_i = vert_j

        # Extract the saddle connections on the left side:
        sc_set_left = set()
        vertices = []
        for i in max_list:
            l = labels[i]
            p = ss.polygon(l)
            vertices.append((i, p.vertex((edges[i] + 1) % p.num_edges())))
        i, vert_i = vertices[-1]
        vert_i = vert_i - v
        j, vert_j = vertices[0]
        if vert_i != vert_j:
            li = labels[i]
            li = (li[0], SG(-v) * li[1])
            lio = ss.opposite_edge(li, edges[i])
            lj = labels[j]
            sc = SaddleConnection(
                s, (lj[0], (edges[j] + 1) % ss.polygon(lj).num_edges()),
                (~lj[1])(vert_i) - (~lj[1])(vert_j))
            sc_set_left.add(sc)
        i = j
        vert_i = vert_j
        for j, vert_j in vertices[1:]:
            if vert_i != vert_j:
                li = labels[i]
                lio = ss.opposite_edge(li, edges[i])
                lj = labels[j]
                sc = SaddleConnection(
                    s, (lj[0], (edges[j] + 1) % ss.polygon(lj).num_edges()),
                    (~lj[1])(vert_i) - (~lj[1])(vert_j))
                sc_set_left.add(sc)
            i = j
            vert_i = vert_j
        self._boundary1 = frozenset(sc_set_right)
        self._boundary2 = frozenset(sc_set_left)
        self._boundary = frozenset(self._boundary1.union(self._boundary2))

        edge_intersections = []
        i = min_list[0]
        l = labels[i]
        p = ss.polygon(l)
        right_point = p.vertex(edges[i])  # point on the right boundary
        i = max_list[0]
        l = labels[i]
        p = ss.polygon(l)
        left_point = p.vertex((edges[i] + 1) % p.num_edges())
        from flatsurf.geometry.polygon import solve
        for i in range(len(edges)):
            l = labels[i]
            p = ss.polygon(l)
            e = edges[i]
            v1 = p.vertex(e)
            v2 = p.vertex((e + 1) % p.num_edges())
            a, b = solve(left_point, v, v1, v2 - v1)
            w1 = (~(l[1]))(v1 + b * (v2 - v1))
            a, b = solve(right_point, v, v1, v2 - v1)
            w2 = (~(l[1]))(v1 + b * (v2 - v1))
            edge_intersections.append((w1, w2))

        polygons = []
        P = ConvexPolygons(s.base_ring())
        pair1 = edge_intersections[-1]
        l1 = labels[-2][0]
        e1 = edges[-1]
        for i in range(len(edges)):
            l2 = labels[i][0]
            pair2 = edge_intersections[i]
            e2 = edges[i]
            trans = s.edge_transformation(l1, e1)
            pair1p = (trans(pair1[0]), trans(pair1[1]))
            polygon_verts = [pair1p[0], pair1p[1]]
            if pair2[1] != pair1p[1]:
                polygon_verts.append(pair2[1])
            if pair2[0] != pair1p[0]:
                polygon_verts.append(pair2[0])
            polygons.append((l2, P(vertices=polygon_verts)))
            l1 = l2
            pair1 = pair2
            e1 = e2
        self._polygons = tuple(polygons)
Exemplo n.º 16
0
    def __init__(self,
                 surface,
                 start_data,
                 direction,
                 end_data=None,
                 end_direction=None,
                 holonomy=None,
                 end_holonomy=None,
                 check=True,
                 limit=1000):
        r"""
        Construct a saddle connecton on a SimilaritySurface.

        The only necessary parameters are the surface, start_data, and direction
        (to start). If there is missing data that can not be inferred from the surface
        type, then a straight-line trajectory will be computed to confirm that this is
        indeed a saddle connection. The trajectory will pass through at most limit
        polygons before we give up.

        Details of the parameters are provided below.

        Parameters
        ----------
        surface : a SimilaritySurface
            which will contain the saddle connection being constructed.
        start_data : a pair
            consisting of the label of the polygon where the saddle connection starts
            and the starting vertex.
        direction : 2-dimensional vector with entries in the base_ring of the surface
            representing the direction the saddle connection is moving in (in the
            coordinates of the initial polygon).
        end_data : a pair
            consisting of the label of the polygon where the saddle connection terminates
            and the terminating vertex.
        end_direction : 2-dimensional vector with entries in the base_ring of the surface
            representing the direction to move backward from the end point (in the
            coordinates of the terminal polygon). If the surface is a DilationSurface
            or better this will be the negation of the direction vector. If the surface
            is a HalfDilation surface or better, then this will be either the direction
            vector or its negation. In either case the value can be inferred from the
            end_data.
        holonomy : 2-dimensional vector with entries in the base_ring of the surface
            the holonomy of the saddle connection measured from the start. To compute this
            you develop the saddle connection into the plane starting from the starting
            polygon.
        end_holonomy : 2-dimensional vector with entries in the base_ring of the surface
            the holonomy of the saddle connection measured from the end (with the opposite
            orientation). To compute this you develop the saddle connection into the plane
            starting from the terminating polygon. For a translation surface, this will be
            the negation of holonomy, and for a HalfTranslation surface it will be either
            equal to holonomy or equal to its negation. In both these cases the end_holonomy
            can be inferred and does not need to be passed to the constructor.
        check : boolean
            If all data above is provided or can be inferred, then when check=False this
            geometric data is not verified. With check=True the data is always verified
            by straight-line flow. Erroroneous data will result in a ValueError being thrown.
            Defaults to true.
        limit :
            The combinatorial limit (in terms of number of polygons crossed) to flow forward
            to check the saddle connection geometry.
        """
        from .similarity_surface import SimilaritySurface
        assert isinstance(surface, SimilaritySurface)
        self._s = surface

        # Sanitize the direction vector:
        V = self._s.vector_space()
        self._direction = V(direction)
        if self._direction == V.zero():
            raise ValueError("Direction must be nonzero.")
        # To canonicalize the direction vector we ensure its endpoint lies in the boundary of the unit square.
        xabs = self._direction[0].abs()
        yabs = self._direction[1].abs()
        if xabs > yabs:
            self._direction = self._direction / xabs
        else:
            self._direction = self._direction / yabs

        # Fix end_direction if not standard.
        if end_direction is not None:
            xabs = end_direction[0].abs()
            yabs = end_direction[1].abs()
            if xabs > yabs:
                end_direction = end_direction / xabs
            else:
                end_direction = end_direction / yabs

        self._start_data = tuple(start_data)

        if end_direction is None:
            from .half_dilation_surface import HalfDilationSurface
            from .dilation_surface import DilationSurface
            # Attempt to infer the end_direction.
            if isinstance(self._s, DilationSurface):
                end_direction = -self._direction
            elif isinstance(self._s,
                            HalfDilationSurface) and end_data is not None:
                p = self._s.polygon(end_data[0])
                if wedge_product(p.edge(end_data[1]), self._direction)>=0 and \
                   wedge_product(p.edge( (p.num_edges()+end_data[1]-1)%p.num_edges() ), self._direction)>0:
                    end_direction = self._direction
                else:
                    end_direction = -self._direction

        if end_holonomy is None and holonomy is not None:
            # Attempt to infer the end_holonomy:
            from .half_translation_surface import HalfTranslationSurface
            from .translation_surface import TranslationSurface
            if isinstance(self._s, TranslationSurface):
                end_holonomy = -holonomy
            if isinstance(self._s, HalfTranslationSurface):
                if direction == end_direction:
                    end_holonomy = holonomy
                else:
                    end_holonomy = -holonomy

        if end_data is None or end_direction is None or holonomy is None or end_holonomy is None or check:
            v = self.start_tangent_vector()
            traj = v.straight_line_trajectory()
            traj.flow(limit)
            if not traj.is_saddle_connection():
                raise ValueError(
                    "Did not obtain saddle connection by flowing forward. Limit="
                    + str(limit))
            tv = traj.terminal_tangent_vector()
            self._end_data = (tv.polygon_label(), tv.vertex())
            if end_data is not None:
                if end_data != self._end_data:
                    raise ValueError("Provided or inferred end_data=" +
                                     str(end_data) +
                                     " does not match actual end_data=" +
                                     str(self._end_data))
            self._end_direction = tv.vector()
            # Canonicalize again.
            xabs = self._end_direction[0].abs()
            yabs = self._end_direction[1].abs()
            if xabs > yabs:
                self._end_direction = self._end_direction / xabs
            else:
                self._end_direction = self._end_direction / yabs
            if end_direction is not None:
                if end_direction != self._end_direction:
                    raise ValueError("Provided or inferred end_direction=" +
                                     str(end_direction) +
                                     " does not match actual end_direction=" +
                                     str(self._end_direction))

            if traj.segments()[0].is_edge():
                # Special case (The below method causes error if the trajectory is just an edge.)
                self._holonomy = self._s.polygon(start_data[0]).edge(
                    start_data[1])
                self._end_holonomy = self._s.polygon(self._end_data[0]).edge(
                    self._end_data[1])
            else:
                from .similarity import SimilarityGroup
                sim = SimilarityGroup(self._s.base_ring()).one()
                itersegs = iter(traj.segments())
                next(itersegs)
                for seg in itersegs:
                    sim = sim * self._s.edge_transformation(
                        seg.start().polygon_label(),
                        seg.start().position().get_edge())
                self._holonomy = sim(traj.segments()[-1].end().point())- \
                    traj.initial_tangent_vector().point()
                self._end_holonomy = -((~sim.derivative()) * self._holonomy)

            if holonomy is not None:
                if holonomy != self._holonomy:
                    print("Combinatorial length: " +
                          str(traj.combinatorial_length()))
                    print("Start: " +
                          str(traj.initial_tangent_vector().point()))
                    print("End: " +
                          str(traj.terminal_tangent_vector().point()))
                    print("Start data:" + str(start_data))
                    print("End data:" + str(end_data))
                    raise ValueError("Provided holonomy " + str(holonomy) +
                                     " does not match computed holonomy of " +
                                     str(self._holonomy))
            if end_holonomy is not None:
                if end_holonomy != self._end_holonomy:
                    raise ValueError(
                        "Provided or inferred end_holonomy " +
                        str(end_holonomy) +
                        " does not match computed end_holonomy of " +
                        str(self._end_holonomy))
        else:
            self._end_data = tuple(end_data)
            self._end_direction = end_direction
            self._holonomy = holonomy
            self._end_holonomy = end_holonomy

        # Make vectors immutable
        self._direction.set_immutable()
        self._end_direction.set_immutable()
        self._holonomy.set_immutable()
        self._end_holonomy.set_immutable()