def plot_fan_stereographically(rays, walls, northsign=1, north=vector((-1,-1,-1)), right=vector((1,0,0)), colors=None, thickness=None):
    from sage.plot.graphics import Graphics
    from sage.plot.point import point
    from sage.misc.flatten import flatten
    from sage.plot.line import line
    from sage.misc.functional import norm
    
    if colors == None:
        colors = dict([('walls','black'),('rays','red')])

    if thickness == None:
        thickness = dict([('walls',0.5),('rays',20)])


    G = Graphics()
    
    for (u,v) in walls:
        G += _stereo_arc(vector(u),vector(v),vector(u+v),north=northsign*north,right=right,color=colors['walls'],thickness=thickness['walls'],zorder=len(G))
   
    for v in rays: 
        G += point(_stereo_coordinates(vector(v),north=northsign*north,right=right),color=colors['rays'],zorder=len(G),size=thickness['rays'])
    
    G.set_aspect_ratio(1)
    G._show_axes = False
    return G
    def plot2d(self, depth=None):
        # FIXME: refactor this before publishing
        from sage.plot.line import line
        from sage.plot.graphics import Graphics
        if self._n != 2:
            raise ValueError("Can only 2d plot fans.")
        if depth == None:
            depth = self._depth
        if not self.is_finite() and depth == infinity:
            raise ValueError(
                "For infinite algebras you must specify the depth.")

        colors = dict([(0, 'red'), (1, 'green')])
        G = Graphics()
        for i in range(2):
            orbit = self.ith_orbit(i, depth=depth)
            for j in orbit:
                G += line([(0, 0), vector(orbit[j])],
                          color=colors[i],
                          thickness=0.5,
                          zorder=2 * j + 1)

        G.set_aspect_ratio(1)
        G._show_axes = False
        return G
Exemple #3
0
def bar_chart(datalist, **options):
    """
    A bar chart of (currently) one list of numerical data.
    Support for more data lists in progress.

    EXAMPLES:

    A bar_chart with blue bars::

        sage: bar_chart([1,2,3,4])
        Graphics object consisting of 1 graphics primitive

    A bar_chart with thinner bars::

        sage: bar_chart([x^2 for x in range(1,20)], width=0.2)
        Graphics object consisting of 1 graphics primitive

    A bar_chart with negative values and red bars::

        sage: bar_chart([-3,5,-6,11], rgbcolor=(1,0,0))
        Graphics object consisting of 1 graphics primitive

    A bar chart with a legend (it's possible, not necessarily useful)::

        sage: bar_chart([-1,1,-1,1], legend_label='wave')
        Graphics object consisting of 1 graphics primitive

    Extra options will get passed on to show(), as long as they are valid::

        sage: bar_chart([-2,8,-7,3], rgbcolor=(1,0,0), axes=False)
        Graphics object consisting of 1 graphics primitive
        sage: bar_chart([-2,8,-7,3], rgbcolor=(1,0,0)).show(axes=False) # These are equivalent
    """
    dl = len(datalist)
    #if dl > 1:
    #    print "WARNING, currently only 1 data set allowed"
    #    datalist = datalist[0]
    if dl == 3:
        datalist = datalist + [0]
    #bardata = []
    #cnt = 1
    #for pnts in datalist:
    #ind = [i+cnt/dl for i in range(len(pnts))]
    #bardata.append([ind, pnts, xrange, yrange])
    #cnt += 1

    g = Graphics()
    g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
    #TODO: improve below for multiple data sets!
    #cnt = 1
    #for ind, pnts, xrange, yrange in bardata:
    #options={'rgbcolor':hue(cnt/dl),'width':0.5/dl}
    #    g._bar_chart(ind, pnts, xrange, yrange, options=options)
    #    cnt += 1
    #else:
    ind = list(range(len(datalist)))
    g.add_primitive(BarChart(ind, datalist, options=options))
    if options['legend_label']:
        g.legend(True)
    return g
Exemple #4
0
def legend_3d(hyperplane_arrangement, hyperplane_colors, length):
    r"""
    Create plot of a 3d legend for an arrangement of planes in 3-space.  The
    ``length`` parameter determines whether short or long labels are used in
    the legend.

    INPUT:

    - ``hyperplane_arrangement`` -- a hyperplane arrangement
    
    - ``hyperplane_colors`` -- list of colors

    - ``length`` -- either ``'short'`` or ``'long'``

    OUTPUT:

    - A graphics object.

    EXAMPLES::

        sage: a = hyperplane_arrangements.semiorder(3)
        sage: from sage.geometry.hyperplane_arrangement.plot import legend_3d
        sage: legend_3d(a, list(colors.values())[:6],length='long')
        Graphics object consisting of 6 graphics primitives

        sage: b = hyperplane_arrangements.semiorder(4)
        sage: c = b.essentialization()
        sage: legend_3d(c, list(colors.values())[:12], length='long')
        Graphics object consisting of 12 graphics primitives

        sage: legend_3d(c, list(colors.values())[:12], length='short')
        Graphics object consisting of 12 graphics primitives

        sage: p = legend_3d(c, list(colors.values())[:12], length='short')
        sage: p.set_legend_options(ncol=4)
        sage: type(p)
        <class 'sage.plot.graphics.Graphics'>
    """
    if hyperplane_arrangement.dimension() != 3:
        raise ValueError('arrangements must be in 3-space')
    hyps = hyperplane_arrangement.hyperplanes()
    N = len(hyperplane_arrangement)
    if length == 'short':
        labels = ['  ' + str(i) for i in range(N)]
    else:
        labels = ['  ' + hyps[i]._repr_linear(include_zero=False) for i in
                  range(N)]
    p = Graphics()
    for i in range(N):
        p += line([(0,0),(0,0)], color=hyperplane_colors[i], thickness=8,
                legend_label=labels[i], axes=False)
    p.set_legend_options(title='Hyperplanes', loc='center', labelspacing=0.4, 
            fancybox=True, font_size='x-large', ncol=2)
    p.legend(True)
    return p
Exemple #5
0
    def bezier_path(self):
        """
        Return ``self`` as a Bezier path.

        This is needed to concatenate arcs, in order to
        create hyperbolic polygons.

        EXAMPLES::

            sage: from sage.plot.arc import Arc
            sage: op = {'alpha':1,'thickness':1,'rgbcolor':'blue','zorder':0,
            ....:     'linestyle':'--'}
            sage: Arc(2,3,2.2,2.2,0,2,3,op).bezier_path()
            Graphics object consisting of 1 graphics primitive

            sage: a = arc((0,0),2,1,0,(pi/5,pi/2+pi/12), linestyle="--", color="red")
            sage: b = a[0].bezier_path()
            sage: b[0]
            Bezier path from (1.133..., 0.8237...) to (-0.2655..., 0.9911...)
        """
        from sage.plot.bezier_path import BezierPath
        from sage.plot.graphics import Graphics
        from matplotlib.path import Path
        import numpy as np
        ma = self._matplotlib_arc()

        def theta_stretch(theta, scale):
            theta = np.deg2rad(theta)
            x = np.cos(theta)
            y = np.sin(theta)
            return np.rad2deg(np.arctan2(scale * y, x))

        theta1 = theta_stretch(ma.theta1, ma.width / ma.height)
        theta2 = theta_stretch(ma.theta2, ma.width / ma.height)

        pa = ma
        pa._path = Path.arc(theta1, theta2)
        transform = pa.get_transform().get_matrix()
        cA, cC, cE = transform[0]
        cB, cD, cF = transform[1]
        points = []
        for u in pa._path.vertices:
            x, y = list(u)
            points += [(cA * x + cC * y + cE, cB * x + cD * y + cF)]
        cutlist = [points[0:4]]
        N = 4
        while N < len(points):
            cutlist += [points[N:N + 3]]
            N += 3
        g = Graphics()
        opt = self.options()
        opt['fill'] = False
        g.add_primitive(BezierPath(cutlist, opt))
        return g
Exemple #6
0
    def bezier_path(self):
        """
        Return ``self`` as a Bezier path.

        This is needed to concatenate arcs, in order to
        create hyperbolic polygons.

        EXAMPLES::

            sage: from sage.plot.arc import Arc
            sage: op = {'alpha':1,'thickness':1,'rgbcolor':'blue','zorder':0,
            ....:     'linestyle':'--'}
            sage: Arc(2,3,2.2,2.2,0,2,3,op).bezier_path()
            Graphics object consisting of 1 graphics primitive

            sage: a = arc((0,0),2,1,0,(pi/5,pi/2+pi/12), linestyle="--", color="red")
            sage: b = a[0].bezier_path()
            sage: b[0]
            Bezier path from (1.133..., 0.8237...) to (-0.2655..., 0.9911...)
        """
        from sage.plot.bezier_path import BezierPath
        from sage.plot.graphics import Graphics
        from matplotlib.path import Path
        import numpy as np
        ma = self._matplotlib_arc()
        def theta_stretch(theta, scale):
            theta = np.deg2rad(theta)
            x = np.cos(theta)
            y = np.sin(theta)
            return np.rad2deg(np.arctan2(scale * y, x))
        theta1 = theta_stretch(ma.theta1, ma.width / ma.height)
        theta2 = theta_stretch(ma.theta2, ma.width / ma.height)

        pa = ma
        pa._path = Path.arc(theta1, theta2)
        transform = pa.get_transform().get_matrix()
        cA, cC, cE = transform[0]
        cB, cD, cF = transform[1]
        points = []
        for u in pa._path.vertices:
            x, y = list(u)
            points += [(cA * x + cC * y + cE, cB * x + cD * y + cF)]
        cutlist = [points[0: 4]]
        N = 4
        while N < len(points):
            cutlist += [points[N: N + 3]]
            N += 3
        g = Graphics()
        opt = self.options()
        opt['fill'] = False
        g.add_primitive(BezierPath(cutlist, opt))
        return g
    def plot3d(self, depth=None):
        # FIXME: refactor this before publishing
        from sage.plot.graphics import Graphics
        from sage.plot.point import point
        from sage.misc.flatten import flatten
        from sage.plot.plot3d.shapes2 import sphere
        if self._n != 3:
            raise ValueError("Can only 3d plot fans.")
        if depth == None:
            depth = self._depth
        if not self.is_finite() and depth == infinity:
            raise ValueError(
                "For infinite algebras you must specify the depth.")

        colors = dict([(0, 'red'), (1, 'green'), (2, 'blue'), (3, 'cyan')])
        G = Graphics()

        roots = self.d_vectors(depth=depth)
        compatible = []
        while roots:
            x = roots.pop()
            for y in roots:
                if self.compatibility_degree(x, y) == 0:
                    compatible.append((x, y))
        for (u, v) in compatible:
            G += _arc3d((_normalize(vector(u)), _normalize(vector(v))),
                        thickness=0.5,
                        color='black')

        for i in range(3):
            orbit = self.ith_orbit(i, depth=depth)
            for j in orbit:
                G += point(_normalize(vector(orbit[j])),
                           color=colors[i],
                           size=10,
                           zorder=len(G.all))

        if self.is_affine():
            tube_vectors = map(vector, flatten(self.affine_tubes()))
            tube_vectors = map(_normalize, tube_vectors)
            for v in tube_vectors:
                G += point(v, color=colors[3], size=10, zorder=len(G.all))
            G += _arc3d((tube_vectors[0], tube_vectors[1]),
                        thickness=5,
                        color='gray',
                        zorder=0)

        G += sphere((0, 0, 0), opacity=0.1, zorder=0)
        G._extra_kwds['frame'] = False
        G._extra_kwds['aspect_ratio'] = 1
        return G
Exemple #8
0
def bar_chart(datalist, **options):
    """
    A bar chart of (currently) one list of numerical data.
    Support for more data lists in progress.

    EXAMPLES:

    A bar_chart with blue bars::

        sage: bar_chart([1,2,3,4])
        Graphics object consisting of 1 graphics primitive

    A bar_chart with thinner bars::

        sage: bar_chart([x^2 for x in range(1,20)], width=0.2)
        Graphics object consisting of 1 graphics primitive

    A bar_chart with negative values and red bars::

        sage: bar_chart([-3,5,-6,11], rgbcolor=(1,0,0))
        Graphics object consisting of 1 graphics primitive

    A bar chart with a legend (it's possible, not necessarily useful)::

        sage: bar_chart([-1,1,-1,1], legend_label='wave')
        Graphics object consisting of 1 graphics primitive

    Extra options will get passed on to show(), as long as they are valid::

        sage: bar_chart([-2,8,-7,3], rgbcolor=(1,0,0), axes=False)
        Graphics object consisting of 1 graphics primitive
        sage: bar_chart([-2,8,-7,3], rgbcolor=(1,0,0)).show(axes=False) # These are equivalent
    """
    dl = len(datalist)
    #if dl > 1:
    #    print "WARNING, currently only 1 data set allowed"
    #    datalist = datalist[0]
    if dl == 3:
        datalist = datalist+[0]
    #bardata = []
    #cnt = 1
    #for pnts in datalist:
        #ind = [i+cnt/dl for i in range(len(pnts))]
        #bardata.append([ind, pnts, xrange, yrange])
        #cnt += 1

    g = Graphics()
    g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
    #TODO: improve below for multiple data sets!
    #cnt = 1
    #for ind, pnts, xrange, yrange in bardata:
        #options={'rgbcolor':hue(cnt/dl),'width':0.5/dl}
    #    g._bar_chart(ind, pnts, xrange, yrange, options=options)
    #    cnt += 1
    #else:
    ind = list(range(len(datalist)))
    g.add_primitive(BarChart(ind, datalist, options=options))
    if options['legend_label']:
        g.legend(True)
    return g
Exemple #9
0
    def plot(self, **options):
        r"""
        Plot this cylinder in coordinates used by a graphical surface. This
        plots this cylinder as a union of subpolygons. Only the intersections
        with polygons visible in the graphical surface are shown.

        Parameters other than `graphical_surface` are passed to `polygon2d`
        which is called to render the polygons.

        Parameters
        ----------
        graphical_surface : a GraphicalSurface
            If not provided or `None`, the plot method uses the default graphical
            surface for the surface.
        """
        if "graphical_surface" in options and options[
                "graphical_surface"] is not None:
            gs = options["graphical_surface"]
            assert gs.get_surface(
            ) == self._s, "Graphical surface for the wrong surface."
            del options["graphical_surface"]
        else:
            gs = self._s.graphical_surface()
        plt = Graphics()
        for l, p in self.polygons():
            if gs.is_visible(l):
                gp = gs.graphical_polygon(l)
                t = gp.transformation()
                pp = t(p)
                poly = polygon2d(pp.vertices(), **options)
                plt += poly.plot()
        return plt
Exemple #10
0
def circle_image(A, B):
    G = Graphics()
    G += circle((0, 0), 1, color='grey')
    from collections import defaultdict
    tmp = defaultdict(int)
    for a in A:
        for j in range(a):
            if gcd(j, a) == 1:
                rational = Rational(j) / Rational(a)
                tmp[(rational.numerator(), rational.denominator())] += 1

    for b in B:
        for j in range(b):
            if gcd(j, b) == 1:
                rational = Rational(j) / Rational(b)
                tmp[(rational.numerator(), rational.denominator())] -= 1
    C = ComplexField()
    for val in tmp:
        if tmp[val] > 0:
            G += text(str(tmp[val]),
                      exp(C(-.2 + 2 * 3.14159 * I * val[0] / val[1])),
                      fontsize=30,
                      axes=False,
                      color="green")
        if tmp[val] < 0:
            G += text(str(abs(tmp[val])),
                      exp(C(.2 + 2 * 3.14159 * I * val[0] / val[1])),
                      fontsize=30,
                      axes=False,
                      color="blue")
    return G
Exemple #11
0
def plot_error_function(model_filename, N, x0, Tfrac=0.8):
    """
    Plot the estimated error of the linearized ODE as a function of time.

    INPUT:

    - ``model_filename`` -- string containing the model in text format

    - ``N`` -- truncation order

    - ``x0`` -- initial point, a list

    - ``Tfrac`` -- (optional, default: `0.8`): fraction of the convergence radius,
      to specify the plotting range in the time axis

    NOTE:

    This function calls ``error_function`` for the error computations.
    """
    from sage.plot.graphics import Graphics
    from sage.plot.plot import plot
    from sage.plot.line import line

    [Ts, eps] = error_function(model_filename, N, x0)
    P = Graphics()
    P = plot(eps, 0, Ts * Tfrac, axes_labels=["$t$", r"$\mathcal{E}(t)$"])
    P += line([[Ts, 0], [Ts, eps(t=Ts * Tfrac)]],
              linestyle='dotted',
              color='black')

    return P
Exemple #12
0
    def plot_n_cylinders(self, n, labels=True):
        r"""
        EXAMPLES::

            sage: from slabbe.markov_transformation import markov_transformations
            sage: T = markov_transformations.Selmer()
            sage: G = T.plot_n_cylinders(3)

        TESTS::

            sage: G = T.plot_n_cylinders(0)
        """
        from sage.plot.graphics import Graphics
        from sage.plot.polygon import polygon
        from sage.plot.text import text
        M3to2 = projection_matrix(3, 2)
        G = Graphics()
        for w, cyl in self.n_cylinders_iterator(n):
            columns = cyl.columns()
            G += polygon((M3to2 * col / col.norm(1) for col in columns),
                         fill=False)
            if labels:
                sum_cols = sum(columns)
                G += text("{}".format(w), M3to2 * sum_cols / sum_cols.norm(1))
        return G
    def plot(self, **kwargs):
        """
        Plot this Newton polygon.

        .. NOTE::

            All usual rendering options (color, thickness, etc.) are available.

        EXAMPLES:

            sage: from sage.geometry.newton_polygon import NewtonPolygon
            sage: NP = NewtonPolygon([ (0,0), (1,1), (2,6) ])
            sage: polygon = NP.plot()
        """
        vertices = self.vertices()
        if len(vertices) == 0:
            from sage.plot.graphics import Graphics
            return Graphics()
        else:
            from sage.plot.line import line
            (xstart, ystart) = vertices[0]
            (xend, yend) = vertices[-1]
            if self.last_slope() is Infinity:
                return line([(xstart, ystart+1), (xstart,ystart+0.5)], linestyle="--", **kwargs) \
                     + line([(xstart, ystart+0.5)] + vertices + [(xend, yend+0.5)], **kwargs) \
                     + line([(xend, yend+0.5), (xend, yend+1)], linestyle="--", **kwargs)
            else:
                return line([(xstart, ystart+1), (xstart,ystart+0.5)], linestyle="--", **kwargs) \
                     + line([(xstart, ystart+0.5)] + vertices + [(xend+0.5, yend + 0.5*self.last_slope())], **kwargs) \
                     + line([(xend+0.5, yend + 0.5*self.last_slope()), (xend+1, yend+self.last_slope())], linestyle="--", **kwargs)
Exemple #14
0
    def plot_n_matrices_eigenvectors(self, n, side='right', color_index=0, draw_line=False):
        r"""
        INPUT:

        - ``n`` -- integer, length
        - ``side`` -- ``'left'`` or ``'right'``, drawing left or right
          eigenvectors
        - ``color_index`` -- 0 for first letter, -1 for last letter
        - ``draw_line`` -- boolean

        EXAMPLES::

            sage: from slabbe.matrix_cocycle import cocycles
            sage: ARP = cocycles.ARP()
            sage: G = ARP.plot_n_matrices_eigenvectors(2)
        """
        from sage.plot.graphics import Graphics
        from sage.plot.point import point
        from sage.plot.line import line
        from sage.plot.text import text
        from sage.plot.colors import hue
        from sage.modules.free_module_element import vector
        from .matrices import M3to2
        R = self.n_matrices_eigenvectors(n)
        L = [(w, M3to2*(a/sum(a)), M3to2*(b/sum(b))) for (w,a,b) in R]
        G = Graphics()
        alphabet = self._language._alphabet
        color_ = dict( (letter, hue(i/float(len(alphabet)))) for i,letter in
                enumerate(alphabet))
        for letter in alphabet:
            L_filtered = [(w,p1,p2) for (w,p1,p2) in L if w[color_index] == letter]
            words,rights,lefts = zip(*L_filtered)
            if side == 'right':
                G += point(rights, color=color_[letter], legend_label=letter)
            elif side == 'left':
                G += point(lefts,  color=color_[letter], legend_label=letter)
            else:
                raise ValueError("side(=%s) should be left or right" % side)

        if draw_line:
            for (a,b) in L:
                G += line([a,b], color='black', linestyle=":")
        G += line([M3to2*vector(a) for a in [(1,0,0), (0,1,0), (0,0,1), (1,0,0)]]) 
        title = "%s eigenvectors, colored by letter w[%s] of cylinder w" % (side, color_index)
        G += text(title, (0.5, 1.05), axis_coords=True)
        G.axes(False)
        return G
Exemple #15
0
    def plot_n_matrices_eigenvectors(self, n, side='right', color_index=0, draw_line=False):
        r"""
        INPUT:

        - ``n`` -- integer, length
        - ``side`` -- ``'left'`` or ``'right'``, drawing left or right
          eigenvectors
        - ``color_index`` -- 0 for first letter, -1 for last letter
        - ``draw_line`` -- boolean

        EXAMPLES::

            sage: from slabbe.matrix_cocycle import cocycles
            sage: ARP = cocycles.ARP()
            sage: G = ARP.plot_n_matrices_eigenvectors(2)
        """
        from sage.plot.graphics import Graphics
        from sage.plot.point import point
        from sage.plot.line import line
        from sage.plot.text import text
        from sage.plot.colors import hue
        from sage.modules.free_module_element import vector
        from matrices import M3to2
        R = self.n_matrices_eigenvectors(n)
        L = [(w, M3to2*(a/sum(a)), M3to2*(b/sum(b))) for (w,a,b) in R]
        G = Graphics()
        alphabet = self._language._alphabet
        color_ = dict( (letter, hue(i/float(len(alphabet)))) for i,letter in
                enumerate(alphabet))
        for letter in alphabet:
            L_filtered = [(w,p1,p2) for (w,p1,p2) in L if w[color_index] == letter]
            words,rights,lefts = zip(*L_filtered)
            if side == 'right':
                G += point(rights, color=color_[letter], legend_label=letter)
            elif side == 'left':
                G += point(lefts,  color=color_[letter], legend_label=letter)
            else:
                raise ValueError("side(=%s) should be left or right" % side)

        if draw_line:
            for (a,b) in L:
                G += line([a,b], color='black', linestyle=":")
        G += line([M3to2*vector(a) for a in [(1,0,0), (0,1,0), (0,0,1), (1,0,0)]]) 
        title = "%s eigenvectors, colored by letter w[%s] of cylinder w" % (side, color_index)
        G += text(title, (0.5, 1.05), axis_coords=True)
        G.axes(False)
        return G
Exemple #16
0
def piecewise_constant_image(A, B):
    # Jumps up and down going around circle, not used
    v = circle_drops(A, B)
    G = Graphics()
    w = ((Rational(i) / len(v), j) for i, j in enumerate(v))
    for p0, p1 in w:
        G += line([(p0, p1), (p0 + Rational(1) / len(w), p1)])
    return G
Exemple #17
0
def piecewise_linear_image(A,B):
    # Jumps up and down going around circle, not used
    v = circle_drops(A,B)
    G = Graphics()
    w = [(Rational(i)/len(v), j) for i,j in enumerate(v)]
    for pt in w:
        G += line([(pt[0],pt[1]),(pt[0]+Rational(1)/len(w),pt[1])])
    return G
Exemple #18
0
    def plot(self, geosub, color=None):
        r"""
        EXAMPLES::

            sage: from EkEkstar import kFace, kPatch, GeoSub
            sage: sub = {1:[1,2], 2:[1,3], 3:[1]}
            sage: geosub = GeoSub(sub,2, dual=True)
            sage: P = kPatch([kFace((0,0,0),(1,2),dual=True),
            ....:             kFace((0,0,1),(1,3),dual=True),
            ....:             kFace((0,1,0),(2,1),dual=True),
            ....:             kFace((0,0,0),(3,1),dual=True)])
            sage: _ = P.plot(geosub)
        """
        G = Graphics()
        for face, m in self:
            G += face._plot(geosub, color)
        G.set_aspect_ratio(1)
        return G
    def plot3d(self,depth=None):
        # FIXME: refactor this before publishing
        from sage.plot.graphics import Graphics
        from sage.plot.point import point
        from sage.misc.flatten import flatten
        from sage.plot.plot3d.shapes2 import sphere
        if self._n !=3:
            raise ValueError("Can only 3d plot fans.")
        if depth == None:
            depth = self._depth
        if not self.is_finite() and depth==infinity:
            raise ValueError("For infinite algebras you must specify the depth.")

        colors = dict([(0,'red'),(1,'green'),(2,'blue'),(3,'cyan')])
        G = Graphics()

        roots = self.d_vectors(depth=depth)
        compatible = []
        while roots:
            x = roots.pop()
            for y in roots:
                if self.compatibility_degree(x,y) == 0:
                    compatible.append((x,y))
        for (u,v) in compatible:
            G += _arc3d((_normalize(vector(u)),_normalize(vector(v))),thickness=0.5,color='black')

        for i in range(3):
            orbit = self.ith_orbit(i,depth=depth)
            for j in orbit:
                G += point(_normalize(vector(orbit[j])),color=colors[i],size=10,zorder=len(G.all))

        if self.is_affine():
            tube_vectors=map(vector,flatten(self.affine_tubes()))
            tube_vectors=map(_normalize,tube_vectors)
            for v in tube_vectors:
                G += point(v,color=colors[3],size=10,zorder=len(G.all))
            G += _arc3d((tube_vectors[0],tube_vectors[1]),thickness=5,color='gray',zorder=0)
        
        G += sphere((0,0,0),opacity=0.1,zorder=0)
        G._extra_kwds['frame']=False
        G._extra_kwds['aspect_ratio']=1 
        return G
Exemple #20
0
 def plot(self, pointsize=20):
     from sage.plot.graphics import Graphics
     G = Graphics()
     m = len(self._scales)
     for i, (name, scale) in enumerate(zip(self._scale_names,
                                           self._scales)):
         G += list_plot(scale,
                        color=hue(1. * i / m),
                        legend_label=name,
                        pointsize=pointsize)
     return G
Exemple #21
0
    def plot(self, m, pointsize=100, thickness=3, axes=False):
        r"""
        Return 2d graphics object contained in the primal box [-m,m]^d.

        INPUT:

        - ``pointsize``, integer (default:``100``),
        - ``thickness``, integer (default:``3``),
        - ``axes``, bool (default:``False``),

        EXAMPLES::

            sage: from slabbe import BondPercolationSample
            sage: S = BondPercolationSample(0.5,2)
            sage: S.plot(2)           # optional long

        It works in 3d!!::

            sage: S = BondPercolationSample(0.5,3)
            sage: S.plot(3, pointsize=10, thickness=1)     # optional long
            Graphics3d Object

        """
        s = ""
        s += "\\begin{tikzpicture}\n"
        s += "[inner sep=0pt,thick,\n"
        s += "reddot/.style={fill=red,draw=red,circle,minimum size=5pt}]\n"
        s += "\\clip %s rectangle %s;\n" % ((-m - .4, -m - .4),
                                            (m + .4, m + .4))
        G = Graphics()
        for u in self.cluster_in_box(m + 1):
            G += point(u, color='blue', size=pointsize)
        for (u, v) in self.edges_in_box(m + 1):
            G += line((u, v), thickness=thickness, alpha=0.8)
        G += text("p=%.3f" % self._p, (0.5, 1.03),
                  axis_coords=True,
                  color='black')
        G += circle((0, 0), 0.5, color='red', thickness=thickness)
        if self._dimension == 2:
            G.axes(axes)
        return G
    def plot2d(self,depth=None):
        # FIXME: refactor this before publishing
        from sage.plot.line import line
        from sage.plot.graphics import Graphics
        if self._n !=2:
            raise ValueError("Can only 2d plot fans.")
        if depth == None:
            depth = self._depth
        if not self.is_finite() and depth==infinity:
            raise ValueError("For infinite algebras you must specify the depth.")

        colors = dict([(0,'red'),(1,'green')])
        G = Graphics()
        for i in range(2):
            orbit = self.ith_orbit(i,depth=depth)
            for j in orbit:
                G += line([(0,0),vector(orbit[j])],color=colors[i],thickness=0.5, zorder=2*j+1)
    
        G.set_aspect_ratio(1)
        G._show_axes = False
        return G
Exemple #23
0
    def bezier_path(self):
        """
        Return ``self`` as a Bezier path.

        This is needed to concatenate arcs, in order to
        create hyperbolic polygons.

        EXAMPLES::

            sage: from sage.plot.arc import Arc
            sage: op = {'alpha':1,'thickness':1,'rgbcolor':'blue','zorder':0,
            ....:     'linestyle':'--'}
            sage: Arc(2,3,2.2,2.2,0,2,3,op).bezier_path()
            Graphics object consisting of 1 graphics primitive

            sage: a = arc((0,0),2,1,0,(pi/5,pi/2+pi/12), linestyle="--", color="red")
            sage: b = a[0].bezier_path()
            sage: b[0]
            Bezier path from (1.618..., 0.5877...) to (-0.5176..., 0.9659...)
        """
        from sage.plot.bezier_path import BezierPath
        from sage.plot.graphics import Graphics
        ma = self._matplotlib_arc()
        transform = ma.get_transform().get_matrix()
        cA, cC, cE = transform[0]
        cB, cD, cF = transform[1]
        points = []
        for u in ma._path.vertices:
            x, y = list(u)
            points += [(cA * x + cC * y + cE, cB * x + cD * y + cF)]
        cutlist = [points[0: 4]]
        N = 4
        while N < len(points):
            cutlist += [points[N: N + 3]]
            N += 3
        g = Graphics()
        opt = self.options()
        opt['fill'] = False
        g.add_primitive(BezierPath(cutlist, opt))
        return g
Exemple #24
0
    def bezier_path(self):
        """
        Return ``self`` as a Bezier path.

        This is needed to concatenate arcs, in order to
        create hyperbolic polygons.

        EXAMPLES::

            sage: from sage.plot.arc import Arc
            sage: op = {'alpha':1,'thickness':1,'rgbcolor':'blue','zorder':0,
            ....:     'linestyle':'--'}
            sage: Arc(2,3,2.2,2.2,0,2,3,op).bezier_path()
            Graphics object consisting of 1 graphics primitive

            sage: a = arc((0,0),2,1,0,(pi/5,pi/2+pi/12), linestyle="--", color="red")
            sage: b = a[0].bezier_path()
            sage: b[0]
            Bezier path from (1.618..., 0.5877...) to (-0.5176..., 0.9659...)
        """
        from sage.plot.bezier_path import BezierPath
        from sage.plot.graphics import Graphics
        ma = self._matplotlib_arc()
        transform = ma.get_transform().get_matrix()
        cA, cC, cE = transform[0]
        cB, cD, cF = transform[1]
        points = []
        for u in ma._path.vertices:
            x, y = list(u)
            points += [(cA * x + cC * y + cE, cB * x + cD * y + cF)]
        cutlist = [points[0:4]]
        N = 4
        while N < len(points):
            cutlist += [points[N:N + 3]]
            N += 3
        g = Graphics()
        opt = self.options()
        opt['fill'] = False
        g.add_primitive(BezierPath(cutlist, opt))
        return g
def legend_3d(hyperplane_arrangement, hyperplane_colors, length):
    r"""
    Create plot of a 3d legend for an arrangement of planes in 3-space.  The
    ``length`` parameter determines whether short or long labels are used in
    the legend.

    INPUT:

    - ``hyperplane_arrangement`` -- a hyperplane arrangement
    
    - ``hyperplane_colors`` -- list of colors

    - ``length`` -- either ``'short'`` or ``'long'``

    OUTPUT:

    - A graphics object.

    EXAMPLES::

        sage: a = hyperplane_arrangements.semiorder(3)
        sage: from sage.geometry.hyperplane_arrangement.plot import legend_3d
        sage: legend_3d(a, colors.values()[:6],length='long')

        sage: b = hyperplane_arrangements.semiorder(4)
        sage: c = b.essentialization()
        sage: legend_3d(c, colors.values()[:12], length='long')

        sage: legend_3d(c, colors.values()[:12], length='short')

        sage: p = legend_3d(c, colors.values()[:12], length='short')
        sage: p.set_legend_options(ncol=4)
        sage: type(p)
        <class 'sage.plot.graphics.Graphics'>
    """
    if hyperplane_arrangement.dimension() != 3:
        raise ValueError('arrangements must be in 3-space')
    hyps = hyperplane_arrangement.hyperplanes()
    N = len(hyperplane_arrangement)
    if length == 'short':
        labels = ['  ' + str(i) for i in range(N)]
    else:
        labels = [
            '  ' + hyps[i]._repr_linear(include_zero=False) for i in range(N)
        ]
    p = Graphics()
    for i in range(N):
        p += line([(0, 0), (0, 0)],
                  color=hyperplane_colors[i],
                  thickness=8,
                  legend_label=labels[i],
                  axes=False)
    p.set_legend_options(title='Hyperplanes',
                         loc='center',
                         labelspacing=0.4,
                         fancybox=True,
                         font_size='x-large',
                         ncol=2)
    p.legend(True)
    return p
Exemple #26
0
    def plot(self, m, pointsize=100, thickness=3, axes=False):
        r"""
        Return 2d graphics object contained in the primal box [-m,m]^d.

        INPUT:

        - ``pointsize``, integer (default:``100``),
        - ``thickness``, integer (default:``3``),
        - ``axes``, bool (default:``False``),

        EXAMPLES::

            sage: from slabbe import BondPercolationSample
            sage: S = BondPercolationSample(0.5,2)
            sage: S.plot(2)           # optional long

        It works in 3d!!::

            sage: S = BondPercolationSample(0.5,3)
            sage: S.plot(3, pointsize=10, thickness=1)     # optional long
            Graphics3d Object

        """
        s = ""
        s += "\\begin{tikzpicture}\n"
        s += "[inner sep=0pt,thick,\n"
        s += "reddot/.style={fill=red,draw=red,circle,minimum size=5pt}]\n"
        s += "\\clip %s rectangle %s;\n" % ((-m-.4,-m-.4), (m+.4,m+.4))
        G = Graphics()
        for u in self.cluster_in_box(m+1):
            G += point(u, color='blue', size=pointsize)
        for (u,v) in self.edges_in_box(m+1):
            G += line((u,v), thickness=thickness, alpha=0.8)
        G += text("p=%.3f" % self._p, (0.5,1.03), axis_coords=True, color='black')
        G += circle((0,0), 0.5, color='red', thickness=thickness)
        if self._dimension == 2:
            G.axes(axes)
        return G
    def plot_cluster_fan_stereographically(self, northsign=1, north=None, right=None, colors=None):
        from sage.plot.graphics import Graphics
        from sage.plot.point import point
        from sage.misc.flatten import flatten
        from sage.plot.line import line
        from sage.misc.functional import norm

        if self.rk !=3:
            raise ValueError("Can only stereographically project fans in 3d.")
        if not self.is_finite() and self._depth == infinity:
            raise ValueError("For infinite algebras you must specify the depth.")

        if north == None:
            if self.is_affine():
                north = vector(self.delta())
            else:
                north = vector( (-1,-1,-1) )
        if right == None:
            if self.is_affine():
                right = vector(self.gamma())
            else:
                right = vector( (1,0,0) )
        if colors == None:
            colors = dict([(0,'red'),(1,'green'),(2,'blue'),(3,'cyan'),(4,'yellow')])
        G = Graphics()

        roots = list(self.g_vectors())
        compatible = []
        while roots:
            x = roots.pop()
            for y in roots:
                if self.compatibility_degree(x,y) == 0:
                    compatible.append((x,y))
        for (u,v) in compatible:
            G += _stereo_arc(vector(u),vector(v),vector(u+v),north=northsign*north,right=right,thickness=0.5,color='black')

        for i in range(3):
            orbit = self.ith_orbit(i)
            for j in orbit:
                G += point(_stereo_coordinates(vector(orbit[j]),north=northsign*north,right=right),color=colors[i],zorder=len(G))

        if self.is_affine():
            tube_vectors = map(vector,flatten(self.affine_tubes()))
            for v in tube_vectors:
                G += point(_stereo_coordinates(v,north=northsign*north,right=right),color=colors[3],zorder=len(G))
            if north != vector(self.delta()):
                G += _stereo_arc(tube_vectors[0],tube_vectors[1],vector(self.delta()),north=northsign*north,right=right,thickness=2,color=colors[4],zorder=0)
            else:
                # FIXME: refactor this before publishing
                tube_projections = [
                        _stereo_coordinates(v,north=northsign*north,right=right)
                        for v in tube_vectors ]
                t=min((G.get_minmax_data()['xmax'],G.get_minmax_data()['ymax']))
                G += line([tube_projections[0],tube_projections[0]+t*(_normalize(tube_projections[0]-tube_projections[1]))],thickness=2,color=colors[4],zorder=0)
                G += line([tube_projections[1],tube_projections[1]+t*(_normalize(tube_projections[1]-tube_projections[0]))],thickness=2,color=colors[4],zorder=0)
        G.set_aspect_ratio(1)
        G._show_axes = False
        return G
Exemple #28
0
 def make_chart(self, region=None):
     G = Graphics()
     smin, smax, nmin, nmax = [], [], [], []
     for d in self.chartgens(region):
         x, y = self.get_coords(d)
         G += point2d((x, y), zorder=10)
         smin.append(y)
         smax.append(y)
         nmin.append(x)
         nmax.append(x)
         smin = [
             min(smin),
         ]
         smax = [
             max(smax),
         ]
         nmin = [
             min(nmin),
         ]
         nmax = [
             max(nmax),
         ]
     for l in self.chartlines():
         try:
             pts, lineargs = self.lineinfo[l["line"]]
             x1, y1 = self.get_coords(self.geninfo(int(l["src"])))
             x2, y2 = self.get_coords(self.geninfo(int(l["tar"])))
             G += line2d([(x1, y1), (x2, y2)], **lineargs)
         except:
             print("line not understood:", l)
     off = 0.5
     G.ymin(smin[0] - off)
     G.ymax(smax[0] + off)
     G.xmin(nmin[0] - off)
     G.xmax(nmax[0] + off)
     return G
Exemple #29
0
    def plot_n_cylinders(self, n, labels=True):
        r"""
        EXAMPLES::

            sage: from slabbe.matrix_cocycle import cocycles
            sage: C = cocycles.Sorted_ARP()
            sage: G = C.plot_n_cylinders(3)
        """
        from sage.plot.graphics import Graphics
        from sage.plot.polygon import polygon
        from sage.plot.text import text
        from matrices import M3to2
        G = Graphics()
        for w,cyl in self.n_cylinders_iterator(n):
            columns = cyl.columns()
            G += polygon((M3to2*col/col.norm(1) for col in columns), fill=False) 
            if labels:
                sum_cols = sum(columns)
                G += text("{}".format(w), M3to2*sum_cols/sum_cols.norm(1))
        return G
Exemple #30
0
def render_2d(projection, point_opts={}, line_opts={}, polygon_opts={}):
    """
    Return 2d rendering of the projection of a polyhedron into
    2-dimensional ambient space.

    EXAMPLES::

        sage: p1 = Polyhedron(vertices=[[1,1]], rays=[[1,1]])
        sage: q1 = p1.projection()
        sage: p2 = Polyhedron(vertices=[[1,0], [0,1], [0,0]])
        sage: q2 = p2.projection()
        sage: p3 = Polyhedron(vertices=[[1,2]])
        sage: q3 = p3.projection()
        sage: p4 = Polyhedron(vertices=[[2,0]], rays=[[1,-1]], lines=[[1,1]])
        sage: q4 = p4.projection()
        sage: q1.show() + q2.show() + q3.show() + q4.show()
        sage: from sage.geometry.polyhedron.plot import render_2d
        sage: q = render_2d(p1.projection())
        sage: q._objects
        [Point set defined by 1 point(s),
         Arrow from (1.0,1.0) to (2.0,2.0),
         Polygon defined by 3 points]
    """
    if is_Polyhedron(projection):
        projection = Projection(projection)
    from sage.plot.graphics import Graphics
    plt = Graphics()
    if isinstance(point_opts, dict):
        point_opts.setdefault('zorder', 2)
        point_opts.setdefault('pointsize', 10)
        plt += projection.render_points_2d(**point_opts)
    if isinstance(line_opts, dict):
        line_opts.setdefault('zorder', 1)
        plt += projection.render_outline_2d(**line_opts)
    if isinstance(polygon_opts, dict):
        polygon_opts.setdefault('zorder', 0)
        plt += projection.render_fill_2d(**polygon_opts)
    return plt
Exemple #31
0
def circle_image(A, B):
    G = Graphics()
    G += circle((0, 0), 1, color='black', thickness=3)
    G += circle(
        (0, 0), 1.4, color='black', alpha=0
    )  # This adds an invisible framing circle to the plot, which protects the aspect ratio from being skewed.
    from collections import defaultdict
    tmp = defaultdict(int)
    for a in A:
        for j in range(a):
            if gcd(j, a) == 1:
                rational = Rational(j) / Rational(a)
                tmp[(rational.numerator(), rational.denominator())] += 1

    for b in B:
        for j in range(b):
            if gcd(j, b) == 1:
                rational = Rational(j) / Rational(b)
                tmp[(rational.numerator(), rational.denominator())] -= 1
    C = ComplexField()
    color1 = (41 / 255, 95 / 255, 45 / 255)
    color2 = (0 / 255, 0 / 255, 150 / 255)
    for val in tmp:
        if tmp[val] > 0:
            G += text(str(tmp[val]),
                      exp(C(-.2 + 2 * 3.14159 * I * val[0] / val[1])),
                      fontsize=30,
                      axes=False,
                      color=color1)
        if tmp[val] < 0:
            G += text(str(abs(tmp[val])),
                      exp(C(.2 + 2 * 3.14159 * I * val[0] / val[1])),
                      fontsize=30,
                      axes=False,
                      color=color2)
    return G
Exemple #32
0
    def plot(self, edge_labels=False, polygon_labels=False):
        from sage.plot.graphics import Graphics
        surface = self._surface
        polygons = surface.polygons()

        if not polygons.is_finite():
            raise NotImplementedError(
                "no implementation for surface built from infinitely many polygons"
            )

        if self._pos is None:
            self._set_positions()
        if self._edge_labels is None:
            self._set_edge_labels()

        G = Graphics()
        for a in polygons.keys():
            p = polygons[a]
            v = p.vertices(translation=self._pos[a])
            G += p.plot(translation=self._pos[a])

            if edge_labels:
                for i in xrange(p.num_edges()):
                    G += text(str(self._edge_labels[a, i]),
                              (v[i] + v[(i + 1) % p.num_edges()]) / 2,
                              color='black')

        # then we possibly plot the lable inside each polygon
        if len(self._index_set) != 1:
            for a in polygons.keys():
                p = polygons[a]
                if polygon_labels:
                    m = sum(
                        p.vertices(translation=self._pos[a])) / p.num_edges()
                    G += text(str(a), m, color='black')
        return G
Exemple #33
0
    def _graphics(self, plot_curve, ambient_coords, thickness=1,
                  aspect_ratio='automatic', color='red', style='-',
                  label_axes=True):
        r"""
        Plot a 2D or 3D curve in a Cartesian graph with axes labeled by
        the ambient coordinates; it is invoked by the methods
        :meth:`plot` of
        :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve`,
        and its subclasses
        (:class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve`,
        :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve`,
        and
        :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic`).

        TESTS::

            sage: M = Manifold(2, 'R^2')
            sage: X.<x,y> = M.chart()
            sage: R.<t> = RealLine()
            sage: c = M.curve([cos(t), sin(t)], (t, 0, 2*pi), name='c')
            sage: graph = c._graphics([[1,2], [3,4]], [x,y])
            sage: graph._objects[0].xdata == [1,3]
            True
            sage: graph._objects[0].ydata == [2,4]
            True
            sage: graph._objects[0]._options['thickness'] == 1
            True
            sage: graph._extra_kwds['aspect_ratio'] == 'automatic'
            True
            sage: graph._objects[0]._options['rgbcolor'] == 'red'
            True
            sage: graph._objects[0]._options['linestyle'] == '-'
            True
            sage: l = [r'$'+latex(x)+r'$', r'$'+latex(y)+r'$']
            sage: graph._extra_kwds['axes_labels'] == l
            True

        """
        from sage.plot.graphics import Graphics
        from sage.plot.line import line
        from sage.manifolds.utilities import set_axes_labels

        #
        # The plot
        #
        n_pc = len(ambient_coords)
        resu = Graphics()
        resu += line(plot_curve, color=color, linestyle=style,
                     thickness=thickness)
        if n_pc == 2:  # 2D graphic
            resu.set_aspect_ratio(aspect_ratio)
            if label_axes:
                # We update the dictionary _extra_kwds (options to be passed
                # to show()), instead of using the method
                # Graphics.axes_labels() since the latter is not robust w.r.t.
                # graph addition
                resu._extra_kwds['axes_labels'] = [r'$'+latex(pc)+r'$'
                                                   for pc in ambient_coords]
        else: # 3D graphic
            if aspect_ratio == 'automatic':
                aspect_ratio = 1
            resu.aspect_ratio(aspect_ratio)
            if label_axes:
                labels = [str(pc) for pc in ambient_coords]
                resu = set_axes_labels(resu, *labels)
        return resu
Exemple #34
0
    def plot(self, chart=None, ambient_coords=None, mapping=None, prange=None,
             include_end_point=(True, True), end_point_offset=(0.001, 0.001),
             max_value=8, parameters=None, color='red',  style='-',
             thickness=1, plot_points=75, label_axes=True,
             aspect_ratio='automatic'):
        r"""
        Plot the current curve (``self``) in a Cartesian graph based on the
        coordinates of some ambient chart.

        The curve is drawn in terms of two (2D graphics) or three (3D graphics)
        coordinates of a given chart, called hereafter the *ambient chart*.
        The ambient chart's domain must overlap with the curve's codomain or
        with the codomain of the composite curve `\Phi\circ c`, where `c` is
        ``self`` and `\Phi` some manifold differential mapping (argument
        ``mapping`` below).

        INPUT:

        - ``chart`` -- (default: ``None``) the ambient chart (see above);
          if ``None``, the default chart of the codomain of the curve (or of
          the curve composed with `\Phi`) is used
        - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3
          coordinates of the ambient chart in terms of which the plot is
          performed; if ``None``, all the coordinates of the ambient chart are
          considered
        - ``mapping`` -- (default: ``None``) differentiable mapping `\Phi`
          (instance of
          :class:`~sage.geometry.manifolds.diffmapping.DiffMapping`)
          providing the link between ``self`` and the ambient chart ``chart``
          (cf. above); if ``None``, the ambient chart is supposed to be defined
          on the codomain of the curve ``self``.
        - ``prange`` -- (default: ``None``) range of the curve parameter for
          the plot; if ``None``, the entire parameter range declared during the
          curve construction is considered (with -Infinity
          replaced by ``-max_value`` and +Infinity by ``max_value``)
        - ``include_end_point`` -- (default: ``(True, True)``) determines
          whether the end points of ``prange`` are included in the plot
        - ``end_point_offset`` -- (default: ``(0.001, 0.001)``) offsets from
          the end points when they are not included in the plot: if
          ``include_end_point[0] == False``, the minimal value of the curve
          parameter used for the plot is ``prange[0] + end_point_offset[0]``,
          while if ``include_end_point[1] == False``, the maximal value is
          ``prange[1] - end_point_offset[1]``.
        - ``max_value`` -- (default: 8) numerical value substituted to
          +Infinity if the latter is the upper bound of the parameter range;
          similarly ``-max_value`` is the numerical valued substituted for
          -Infinity
        - ``parameters`` -- (default: ``None``) dictionary giving the numerical
          values of the parameters that may appear in the coordinate expression
          of ``self``
        - ``color`` -- (default: 'red') color of the drawn curve
        - ``style`` -- (default: '-') color of the drawn curve; NB: ``style``
          is effective only for 2D plots
        - ``thickness`` -- (default: 1) thickness of the drawn curve
        - ``plot_points`` -- (default: 75) number of points to plot the curve
        - ``label_axes`` -- (default: ``True``) boolean determining whether the
          labels of the coordinate axes of ``chart`` shall be added to the
          graph; can be set to ``False`` if the graph is 3D and must be
          superposed with another graph.
        - ``aspect_ratio`` -- (default: 'automatic') aspect ratio of the plot;
          the default value ('automatic') applies only for 2D plots; for
          3D plots, the default value is ``1`` instead.

        OUTPUT:

        - a graphic object, either an instance of
          :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on
          2 coordinates of ``chart``) or an instance of
          :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e.
          based on 3 coordinates of ``chart``)

        EXAMPLES:

        Plot of the lemniscate of Gerono::

            sage: R2 = Manifold(2, 'R^2')
            sage: X.<x,y> = R2.chart()
            sage: R.<t> = RealLine()
            sage: c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c')
            sage: c.plot()  # 2D plot
            Graphics object consisting of 1 graphics primitive

        Plot for a subinterval of the curve's domain::

            sage: c.plot(prange=(0,pi))
            Graphics object consisting of 1 graphics primitive

        Plot with various options::

            sage: c.plot(color='green', style=':', thickness=3, aspect_ratio=1)
            Graphics object consisting of 1 graphics primitive

        Plot via a mapping to another manifold: loxodrome of a sphere viewed
        in `\RR^3`::

            sage: S2 = Manifold(2, 'S^2')
            sage: U = S2.open_subset('U')
            sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            sage: R3 = Manifold(3, 'R^3')
            sage: X3.<x,y,z> = R3.chart()
            sage: F = S2.diff_mapping(R3, {(XS, X3): [sin(th)*cos(ph),
            ....:                     sin(th)*sin(ph), cos(th)]}, name='F')
            sage: F.display()
            F: S^2 --> R^3
            on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th))
            sage: c = S2.curve([2*atan(exp(-t/10)), t], (t, -oo, +oo), name='c')
            sage: graph_c = c.plot(mapping=F, max_value=40,
            ....:                  plot_points=200, thickness=2, label_axes=False)  # 3D plot
            sage: graph_S2 = XS.plot(X3, mapping=F, nb_values=11, color='black') # plot of the sphere
            sage: show(graph_c + graph_S2) # the loxodrome + the sphere

        Example of use of the argument ``parameters``: we define a curve with
        some symbolic parameters ``a`` and ``b``::

            sage: a, b = var('a b')
            sage: c = R2.curve([a*cos(t) + b, a*sin(t)], (t, 0, 2*pi), name='c')

        To make a plot, we set spectific values for ``a`` and ``b`` by means
        of the Python dictionary ``parameters``::

            sage: c.plot(parameters={a: 2, b: -3}, aspect_ratio=1)
            Graphics object consisting of 1 graphics primitive

        """
        from sage.rings.infinity import Infinity
        from sage.misc.functional import numerical_approx
        from sage.plot.graphics import Graphics
        from sage.plot.line import line
        from sage.geometry.manifolds.chart import Chart
        from sage.geometry.manifolds.utilities import set_axes_labels
        #
        # The "effective" curve to be plotted
        #
        if mapping is None:
            eff_curve = self
        else:
            eff_curve = mapping.restrict(self.codomain()) * self
        #
        # The chart w.r.t. which the curve is plotted
        #
        if chart is None:
            chart = eff_curve._codomain.default_chart()
        elif not isinstance(chart, Chart):
            raise TypeError("{} is not a chart".format(chart))
        #
        # Coordinates of the above chart w.r.t. which the curve is plotted
        #
        if ambient_coords is None:
            ambient_coords = chart[:]  # all chart coordinates are used
        n_pc = len(ambient_coords)
        if n_pc != 2 and n_pc !=3:
            raise ValueError("The number of coordinates involved in the " +
                             "plot must be either 2 or 3, not {}".format(n_pc))
        ind_pc = [chart[:].index(pc) for pc in ambient_coords] # indices of plot
                                                            # coordinates
        #
        # Parameter range for the plot
        #
        if prange is None:
            prange = (self._domain.lower_bound(), self._domain.upper_bound())
        elif not isinstance(prange, (tuple, list)):
            raise TypeError("{} is neither a tuple nor a list".format(prange))
        elif len(prange) != 2:
            raise ValueError("the argument prange must be a tuple/list " +
                             "of 2 elements")
        tmin = prange[0]
        tmax = prange[1]
        if tmin == -Infinity:
            tmin = -max_value
        elif not include_end_point[0]:
            tmin = tmin + end_point_offset[0]
        if tmax == Infinity:
            tmax = max_value
        elif not include_end_point[1]:
            tmax = tmax - end_point_offset[1]
        tmin = numerical_approx(tmin)
        tmax = numerical_approx(tmax)
        #
        # The coordinate expression of the effective curve
        #
        canon_chart = self._domain.canonical_chart()
        transf = None
        for chart_pair in eff_curve._coord_expression:
            if chart_pair == (canon_chart, chart):
                transf = eff_curve._coord_expression[chart_pair]
                break
        else:
            # Search for a subchart
            for chart_pair in eff_curve._coord_expression:
                for schart in chart._subcharts:
                    if chart_pair == (canon_chart, schart):
                        transf = eff_curve._coord_expression[chart_pair]
        if transf is None:
            raise ValueError("No expression has been found for " +
                              "{} in terms of {}".format(self, format))
        #
        # List of points for the plot curve
        #
        plot_curve = []
        dt = (tmax - tmin) / (plot_points - 1)
        t = tmin
        if parameters is None:
            for i in range(plot_points):
                x = transf(t, simplify=False)
                plot_curve.append( [numerical_approx(x[j]) for j in ind_pc] )
                t += dt
        else:
             for i in range(plot_points):
                x = transf(t, simplify=False)
                plot_curve.append(
                               [numerical_approx( x[j].substitute(parameters) )
                                for j in ind_pc] )
                t += dt
        #
        # The plot
        #
        resu = Graphics()
        resu += line(plot_curve, color=color, linestyle=style,
                     thickness=thickness)
        if n_pc==2:  # 2D graphic
            resu.set_aspect_ratio(aspect_ratio)
            if label_axes:
                # We update the dictionary _extra_kwds (options to be passed
                # to show()), instead of using the method
                # Graphics.axes_labels() since the latter is not robust w.r.t.
                # graph addition
                resu._extra_kwds['axes_labels'] = [r'$'+latex(pc)+r'$'
                                                   for pc in ambient_coords]
        else: # 3D graphic
            if aspect_ratio == 'automatic':
                aspect_ratio = 1
            resu.aspect_ratio(aspect_ratio)
            if label_axes:
                labels = [str(pc) for pc in ambient_coords]
                resu = set_axes_labels(resu, *labels)
        return resu
Exemple #35
0
    def plot(self, color='sign'):
        """
        Return a plot of ``self``.

        INPUT:

        - ``color`` -- can be any of the following:

          * ``4`` - use 4 colors: black, red, blue, and green with each
            corresponding to up, right, down, and left respectively
          * ``2`` - use 2 colors: red for horizontal, blue for vertical arrows
          * ``'sign'`` - use red for right and down arrows, blue for left
            and up arrows
          * a list of 4 colors for each direction
          * a function which takes a direction and a boolean corresponding
            to the sign

        EXAMPLES::

            sage: M = SixVertexModel(2, boundary_conditions='ice')
            sage: print(M[0].plot().description())
            Arrow from (-1.0,0.0) to (0.0,0.0)
            Arrow from (-1.0,1.0) to (0.0,1.0)
            Arrow from (0.0,0.0) to (0.0,-1.0)
            Arrow from (0.0,0.0) to (1.0,0.0)
            Arrow from (0.0,1.0) to (0.0,0.0)
            Arrow from (0.0,1.0) to (0.0,2.0)
            Arrow from (1.0,0.0) to (1.0,-1.0)
            Arrow from (1.0,0.0) to (1.0,1.0)
            Arrow from (1.0,1.0) to (0.0,1.0)
            Arrow from (1.0,1.0) to (1.0,2.0)
            Arrow from (2.0,0.0) to (1.0,0.0)
            Arrow from (2.0,1.0) to (1.0,1.0)
        """
        from sage.plot.graphics import Graphics
        from sage.plot.arrow import arrow

        if color == 4:
            color_list = ['black', 'red', 'blue', 'green']
            cfunc = lambda d,pm: color_list[d]
        elif color == 2:
            cfunc = lambda d,pm: 'red' if d % 2 == 0 else 'blue'
        elif color == 1 or color is None:
            cfunc = lambda d,pm: 'black'
        elif color == 'sign':
            cfunc = lambda d,pm: 'red' if pm else 'blue' # RD are True
        elif isinstance(color, (list, tuple)):
            cfunc = lambda d,pm: color[d]
        else:
            cfunc = color

        G = Graphics()
        for j,row in enumerate(reversed(self)):
            for i,entry in enumerate(row):
                if entry == 0: # LR
                    G += arrow((i,j+1), (i,j), color=cfunc(2, True))
                    G += arrow((i,j), (i+1,j), color=cfunc(1, True))
                    if j == 0:
                        G += arrow((i,j-1), (i,j), color=cfunc(0, False))
                    if i == 0:
                        G += arrow((i,j), (i-1,j), color=cfunc(3, False))
                elif entry == 1: # LU
                    G += arrow((i,j), (i,j+1), color=cfunc(0, False))
                    G += arrow((i+1,j), (i,j), color=cfunc(3, False))
                    if j == 0:
                        G += arrow((i,j-1), (i,j), color=cfunc(0, False))
                    if i == 0:
                        G += arrow((i,j), (i-1,j), color=cfunc(3, False))
                elif entry == 2: # LD
                    G += arrow((i,j+1), (i,j), color=cfunc(2, True))
                    G += arrow((i+1,j), (i,j), color=cfunc(3, False))
                    if j == 0:
                        G += arrow((i,j), (i,j-1), color=cfunc(2, True))
                    if i == 0:
                        G += arrow((i,j), (i-1,j), color=cfunc(3, False))
                elif entry == 3: # UD
                    G += arrow((i,j), (i,j+1), color=cfunc(0, False))
                    G += arrow((i+1,j), (i,j), color=cfunc(3, False))
                    if j == 0:
                        G += arrow((i,j), (i,j-1), color=cfunc(2, True))
                    if i == 0:
                        G += arrow((i-1,j), (i,j), color=cfunc(1, True))
                elif entry == 4: # UR
                    G += arrow((i,j), (i,j+1), color=cfunc(0, False))
                    G += arrow((i,j), (i+1,j), color=cfunc(1, True))
                    if j == 0:
                        G += arrow((i,j-1), (i,j), color=cfunc(0, False))
                    if i == 0:
                        G += arrow((i-1,j), (i,j), color=cfunc(1, True))
                elif entry == 5: # RD
                    G += arrow((i,j+1), (i,j), color=cfunc(2, True))
                    G += arrow((i,j), (i+1,j), color=cfunc(1, True))
                    if j == 0:
                        G += arrow((i,j), (i,j-1), color=cfunc(2, True))
                    if i == 0:
                        G += arrow((i-1,j), (i,j), color=cfunc(1, True))
        G.axes(False)
        return G
Exemple #36
0
    def plot(self):
        r"""
        Return a graphical object of the Fully Packed Loop

        EXAMPLES:

        Here is the fully packed loop for

        .. MATH::

            \begin{pmatrix} 0&1&1 \\ 1&-1&1 \\ 0&1&0 \end{pmatrix}:

        .. PLOT::
            :width: 200 px

            A = AlternatingSignMatrix([[0, 1, 0], [1, -1, 1], [0, 1, 0]])
            fpl = FullyPackedLoop(A)
            p = fpl.plot()
            sphinx_plot(p)

        Here is how Sage represents this::

            sage: A = AlternatingSignMatrix([[0, 1, 0], [1, -1, 1], [0, 1, 0]])
            sage: fpl = FullyPackedLoop(A)
            sage: print(fpl.plot().description())
            Line defined by 2 points:       [(-1.0, 1.0), (0.0, 1.0)]
            Line defined by 2 points:       [(0.0, 0.0), (0.0, -1.0)]
            Line defined by 2 points:       [(0.0, 0.0), (1.0, 0.0)]
            Line defined by 2 points:       [(0.0, 2.0), (0.0, 3.0)]
            Line defined by 2 points:       [(0.0, 2.0), (0.0, 3.0)]
            Line defined by 2 points:       [(0.0, 2.0), (1.0, 2.0)]
            Line defined by 2 points:       [(1.0, 1.0), (0.0, 1.0)]
            Line defined by 2 points:       [(1.0, 1.0), (2.0, 1.0)]
            Line defined by 2 points:       [(2.0, 0.0), (1.0, 0.0)]
            Line defined by 2 points:       [(2.0, 0.0), (2.0, -1.0)]
            Line defined by 2 points:       [(2.0, 2.0), (1.0, 2.0)]
            Line defined by 2 points:       [(2.0, 2.0), (2.0, 3.0)]
            Line defined by 2 points:       [(2.0, 2.0), (2.0, 3.0)]
            Line defined by 2 points:       [(3.0, 1.0), (2.0, 1.0)]
            Line defined by 2 points:       [(3.0, 1.0), (2.0, 1.0)]

        Here are the other 3 by 3 Alternating Sign Matrices and their corresponding
        fully packed loops:

        .. MATH::

            A = \begin{pmatrix} 1&0&0 \\ 0&1&0 \\ 0&0&1 \\ \end{pmatrix}

        gives:

        .. PLOT::
            :width: 200 px

            A = AlternatingSignMatrix([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
            fpl = FullyPackedLoop(A)
            p = fpl.plot()
            sphinx_plot(p)

        .. MATH::

            A = \begin{pmatrix} 1&0&0 \\ 0&0&1 \\ 0&1&0 \\ \end{pmatrix}

        gives:

        .. PLOT::
            :width: 200 px

            A = AlternatingSignMatrix([[1, 0, 0], [0, 0, 1], [0, 1, 0]])
            fpl = FullyPackedLoop(A)
            p = fpl.plot()
            sphinx_plot(p)

        .. MATH::

            A = \begin{pmatrix} 0&1&0\\ 1&0&0\\ 0&0&1\\ \end{pmatrix}

        gives:

        .. PLOT::
            :width: 200 px

            A = AlternatingSignMatrix([[0, 1, 0], [1, 0, 0], [0, 0, 1]])
            fpl = FullyPackedLoop(A)
            p = fpl.plot()
            sphinx_plot(p)

        .. MATH::

            A = \begin{pmatrix} 0&1&0\\ 0&0&1\\ 1&0&0\\ \end{pmatrix}

        gives:

        .. PLOT::
            :width: 200 px

            A = AlternatingSignMatrix([[0, 1, 0], [0, 0, 1], [1, 0, 0]])
            fpl = FullyPackedLoop(A)
            p = fpl.plot()
            sphinx_plot(p)

        .. MATH::

            A = \begin{pmatrix} 0&0&1\\ 1&0&0\\ 0&1&0\\ \end{pmatrix}

        gives:

        .. PLOT::
            :width: 200 px

            A = AlternatingSignMatrix([[0, 0, 1], [1, 0, 0], [0, 1, 0]])
            fpl = FullyPackedLoop(A)
            p = fpl.plot()
            sphinx_plot(p)

        .. MATH::

            A = \begin{pmatrix} 0&0&1\\ 0&1&0\\ 1&0&0\\ \end{pmatrix}

        gives:

        .. PLOT::
            :width: 200 px

            A = AlternatingSignMatrix([[0, 0, 1], [0, 1, 0], [1, 0, 0]])
            fpl = FullyPackedLoop(A)
            p = fpl.plot()
            sphinx_plot(p)

        EXAMPLES::

            sage: A = AlternatingSignMatrix([[0, 1, 0, 0], [1, -1, 0, 1], \
            [0, 1, 0, 0],[0, 0, 1, 0]])
            sage: fpl = FullyPackedLoop(A)
            sage: print(fpl.plot().description())
            Line defined by 2 points:       [(-1.0, 0.0), (0.0, 0.0)]
            Line defined by 2 points:       [(-1.0, 2.0), (0.0, 2.0)]
            Line defined by 2 points:       [(0.0, 1.0), (0.0, 0.0)]
            Line defined by 2 points:       [(0.0, 1.0), (1.0, 1.0)]
            Line defined by 2 points:       [(0.0, 3.0), (0.0, 4.0)]
            Line defined by 2 points:       [(0.0, 3.0), (0.0, 4.0)]
            Line defined by 2 points:       [(0.0, 3.0), (1.0, 3.0)]
            Line defined by 2 points:       [(1.0, 0.0), (1.0, -1.0)]
            Line defined by 2 points:       [(1.0, 0.0), (2.0, 0.0)]
            Line defined by 2 points:       [(1.0, 2.0), (0.0, 2.0)]
            Line defined by 2 points:       [(1.0, 2.0), (2.0, 2.0)]
            Line defined by 2 points:       [(2.0, 1.0), (1.0, 1.0)]
            Line defined by 2 points:       [(2.0, 1.0), (2.0, 2.0)]
            Line defined by 2 points:       [(2.0, 3.0), (1.0, 3.0)]
            Line defined by 2 points:       [(2.0, 3.0), (2.0, 4.0)]
            Line defined by 2 points:       [(2.0, 3.0), (2.0, 4.0)]
            Line defined by 2 points:       [(3.0, 0.0), (2.0, 0.0)]
            Line defined by 2 points:       [(3.0, 0.0), (3.0, -1.0)]
            Line defined by 2 points:       [(3.0, 2.0), (3.0, 1.0)]
            Line defined by 2 points:       [(3.0, 2.0), (3.0, 3.0)]
            Line defined by 2 points:       [(4.0, 1.0), (3.0, 1.0)]
            Line defined by 2 points:       [(4.0, 1.0), (3.0, 1.0)]
            Line defined by 2 points:       [(4.0, 3.0), (3.0, 3.0)]
            Line defined by 2 points:       [(4.0, 3.0), (3.0, 3.0)]

        Here is the plot:

        .. PLOT::
            :width: 300 px

            A = AlternatingSignMatrix([[0, 1, 0, 0], [1, -1, 0, 1], [0, 1, 0, 0],[0, 0, 1, 0]])
            fpl = FullyPackedLoop(A)
            p = fpl.plot()
            sphinx_plot(p)

        """
        G = Graphics()
        n=len(self._six_vertex_model)-1
        for j,row in enumerate(reversed(self._six_vertex_model)):
            for i,entry in enumerate(row):
                if i == 0 and (i+j+n+1) % 2 ==0:
                    G+= line([(i-1,j),(i,j)])
                if i == n and (i+j+n+1) % 2 ==0:
                    G+= line([(i+1,j),(i,j)])
                if j == 0 and (i+j+n) % 2 ==0:
                    G+= line([(i,j),(i,j-1)])
                if j == n and (i+j+n) % 2 ==0:
                    G+= line([(i,j),(i,j+1)])
                if entry == 0: # LR
                    if (i+j+n) % 2==0:
                        G += line([(i,j), (i+1,j)])
                    else:
                        G += line([(i,j),(i,j+1)])
                elif entry == 1: # LU
                    if (i+j+n) % 2 ==0:
                        G += line([(i,j), (i,j+1)])
                    else:
                        G += line([(i+1,j), (i,j)])
                elif entry == 2: # LD
                    if (i+j+n) % 2 == 0:
                        pass
                    else:
                        G += line([(i,j+1), (i,j)])
                        G += line([(i+1,j), (i,j)])
                elif entry == 3: # UD
                    if (i+j+n) % 2 == 0:
                        G += line([(i,j), (i,j+1)])
                    else:
                        G += line([(i+1,j), (i,j)])
                elif entry == 4: # UR
                    if (i+j+n) % 2 ==0:
                        G += line([(i,j), (i,j+1)])
                        G += line([(i,j), (i+1,j)])
                    else:
                        pass
                elif entry == 5: # RD
                    if (i+j+n) % 2 ==0:
                        G += line([(i,j), (i+1,j)])
                    else:
                        G += line([(i,j+1), (i,j)])
        G.axes(False)
        return G
Exemple #37
0
    def plot(self,
             chart=None,
             ambient_coords=None,
             mapping=None,
             chart_domain=None,
             fixed_coords=None,
             ranges=None,
             max_value=8,
             nb_values=None,
             steps=None,
             scale=1,
             color='blue',
             parameters=None,
             label_axes=True,
             **extra_options):
        r"""
        Plot the vector field in a Cartesian graph based on the coordinates
        of some ambient chart.

        The vector field is drawn in terms of two (2D graphics) or three
        (3D graphics) coordinates of a given chart, called hereafter the
        *ambient chart*.
        The vector field's base points `p` (or their images `\Phi(p)` by some
        differentiable mapping `\Phi`) must lie in the ambient chart's domain.

        INPUT:

        - ``chart`` -- (default: ``None``) the ambient chart (see above); if
          ``None``, the default chart of the vector field's domain is used
        - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3
          coordinates of the ambient chart in terms of which the plot is
          performed; if ``None``, all the coordinates of the ambient chart are
          considered
        - ``mapping`` -- (default: ``None``) differentiable mapping `\Phi`
          (instance of
          :class:`~sage.geometry.manifolds.diffmapping.DiffMapping`)
          providing the link between the vector field's domain and
          the ambient chart ``chart``; if ``None``, the identity mapping is
          assumed
        - ``chart_domain`` -- (default: ``None``) chart on the vector
          field's domain to define the points at which vector arrows are to be
          plotted; if ``None``, the default chart of the vector field's domain
          is used
        - ``fixed_coords`` -- (default: ``None``) dictionary with keys the
          coordinates of ``chart_domain`` that are kept fixed and with values
          the value of these coordinates; if ``None``, all the coordinates of
          ``chart_domain`` are used
        - ``ranges`` -- (default: ``None``) dictionary with keys the
          coordinates of ``chart_domain`` to be used and values
          tuples ``(x_min,x_max)`` specifying the
          coordinate range for the plot; if ``None``, the entire coordinate
          range declared during the construction of ``chart_domain`` is
          considered (with ``-Infinity`` replaced by ``-max_value`` and
          ``+Infinity`` by ``max_value``)
        - ``max_value`` -- (default: 8) numerical value substituted to
          ``+Infinity`` if the latter is the upper bound of the range of a
          coordinate for which the plot is performed over the entire coordinate
          range (i.e. for which no specific plot range has been set in
          ``ranges``); similarly ``-max_value`` is the numerical valued
          substituted for ``-Infinity``
        - ``nb_values`` -- (default: ``None``) either an integer or a dictionary
          with keys the coordinates of ``chart_domain`` to be used and values
          the number of values of the coordinate for sampling
          the part of the vector field's domain involved in the plot ; if
          ``nb_values`` is a single integer, it represents the number of
          values for all coordinates; if ``nb_values`` is ``None``, it is set
          to 9 for a 2D plot and to 5 for a 3D plot
        - ``steps`` -- (default: ``None``) dictionary with keys the coordinates
          of ``chart_domain`` to be used and values the step between each
          constant value of the coordinate; if ``None``, the step is computed
          from the coordinate range (specified in ``ranges``) and ``nb_values``.
          On the contrary, if the step is provided for some coordinate, the
          corresponding number of values is deduced from it and the coordinate
          range.
        - ``scale`` -- (default: 1) value by which the lengths of the arrows
          representing the vectors is multiplied
        - ``color`` -- (default: 'blue') color of the arrows representing the
          vectors
        - ``parameters`` -- (default: ``None``) dictionary giving the numerical
          values of the parameters that may appear in the coordinate expression
          of the vector field (see example below)
        - ``label_axes`` -- (default: ``True``) boolean determining whether the
          labels of the coordinate axes of ``chart`` shall be added to the
          graph; can be set to ``False`` if the graph is 3D and must be
          superposed with another graph.
        - ``**extra_options`` -- extra options for the arrow plot, like
          ``linestyle``, ``width`` or ``arrowsize`` (see
          :func:`~sage.plot.arrow.arrow2d` and
          :func:`~sage.plot.plot3d.shapes.arrow3d` for details)

        OUTPUT:

        - a graphic object, either an instance of
          :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on
          2 coordinates of ``chart``) or an instance of
          :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e.
          based on 3 coordinates of ``chart``)

        EXAMPLES:

        Plot of a vector field on a 2-dimensional manifold::

            sage: Manifold._clear_cache_() # for doctests only
            sage: M = Manifold(2, 'M')
            sage: X.<x,y> = M.chart()
            sage: v = M.vector_field(name='v')
            sage: v[:] = -y, x ; v.display()
            v = -y d/dx + x d/dy
            sage: v.plot()
            Graphics object consisting of 80 graphics primitives

        Plot with various options::

            sage: v.plot(scale=0.5, color='green', linestyle='--', width=1, arrowsize=6)
            Graphics object consisting of 80 graphics primitives
            sage: v.plot(max_value=4, nb_values=5, scale=0.5)
            Graphics object consisting of 24 graphics primitives

        Plots along a line of fixed coordinate::

            sage: v.plot(fixed_coords={x: -2})
            Graphics object consisting of 9 graphics primitives
            sage: v.plot(fixed_coords={y: 1})
            Graphics object consisting of 9 graphics primitives

        Let us now consider a vector field on a 4-dimensional manifold::

            sage: Manifold._clear_cache_() # for doctests only
            sage: M = Manifold(4, 'M')
            sage: X.<t,x,y,z> = M.chart()
            sage: v = M.vector_field(name='v')
            sage: v[:] = (t/8)^2, -t*y/4, t*x/4, t*z/4 ; v.display()
            v = 1/64*t^2 d/dt - 1/4*t*y d/dx + 1/4*t*x d/dy + 1/4*t*z d/dz

        We cannot make a 4D plot directly::

            sage: v.plot()
            Traceback (most recent call last):
            ...
            ValueError: the number of ambient coordinates must be either 2 or 3, not 4

        Rather, we have to select some coordinates for the plot, via
        the argument ``ambient_coords``. For instance, for a 3D plot::

            sage: v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1})
            Graphics3d Object
            sage: v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0},
            ....:        ranges={x: (-2,2), y: (-2,2), t: (-1, 4)}, nb_values=4)
            Graphics3d Object

        or, for a 2D plot::

            sage: v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0})
            Graphics object consisting of 80 graphics primitives
            sage: v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0})
            Graphics object consisting of 72 graphics primitives

        An example of plot via a differential mapping: plot of a vector field
        tangent to a 2-sphere viewed in `\RR^3`::

            sage: S2 = Manifold(2, 'S^2')
            sage: U = S2.open_subset('U') # the open set covered by spherical coord.
            sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            sage: R3 = Manifold(3, 'R^3')
            sage: X3.<x,y,z> = R3.chart()
            sage: F = S2.diff_mapping(R3, {(XS, X3): [sin(th)*cos(ph),
            ....:                     sin(th)*sin(ph), cos(th)]}, name='F')
            sage: F.display() # the standard embedding of S^2 into R^3
            F: S^2 --> R^3
            on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th))
            sage: v = XS.frame()[1] ; v
            vector field 'd/dph' on the open subset 'U' of the 2-dimensional manifold 'S^2'
            sage: graph_v = v.plot(chart=X3, mapping=F, label_axes=False)
            sage: graph_S2 = XS.plot(chart=X3, mapping=F, nb_values=9)
            sage: show(graph_v + graph_S2)

        """
        from sage.rings.infinity import Infinity
        from sage.misc.functional import numerical_approx
        from sage.misc.latex import latex
        from sage.plot.graphics import Graphics
        from sage.geometry.manifolds.chart import Chart
        from sage.geometry.manifolds.utilities import set_axes_labels
        #
        # 1/ Treatment of input parameters
        #    -----------------------------
        if chart is None:
            chart = self._domain.default_chart()
        elif not isinstance(chart, Chart):
            raise TypeError("{} is not a chart".format(chart))
        if chart_domain is None:
            chart_domain = self._domain.default_chart()
        elif not isinstance(chart_domain, Chart):
            raise TypeError("{} is not a chart".format(chart_domain))
        elif not chart_domain.domain().is_subset(self._domain):
            raise ValueError("The domain of {} is not ".format(chart_domain) +
                             "included in the domain of {}".format(self))
        if fixed_coords is None:
            coords = chart_domain._xx
        else:
            fixed_coord_list = fixed_coords.keys()
            coords = []
            for coord in chart_domain._xx:
                if coord not in fixed_coord_list:
                    coords.append(coord)
            coords = tuple(coords)
        if ambient_coords is None:
            ambient_coords = chart[:]
        elif not isinstance(ambient_coords, tuple):
            ambient_coords = tuple(ambient_coords)
        nca = len(ambient_coords)
        if nca != 2 and nca != 3:
            raise ValueError("the number of ambient coordinates must be " +
                             "either 2 or 3, not {}".format(nca))
        if ranges is None:
            ranges = {}
        ranges0 = {}
        for coord in coords:
            if coord in ranges:
                ranges0[coord] = (numerical_approx(ranges[coord][0]),
                                  numerical_approx(ranges[coord][1]))
            else:
                bounds = chart_domain._bounds[chart_domain[:].index(coord)]
                if bounds[0][0] == -Infinity:
                    xmin = numerical_approx(-max_value)
                elif bounds[0][1]:
                    xmin = numerical_approx(bounds[0][0])
                else:
                    xmin = numerical_approx(bounds[0][0] + 1.e-3)
                if bounds[1][0] == Infinity:
                    xmax = numerical_approx(max_value)
                elif bounds[1][1]:
                    xmax = numerical_approx(bounds[1][0])
                else:
                    xmax = numerical_approx(bounds[1][0] - 1.e-3)
                ranges0[coord] = (xmin, xmax)
        ranges = ranges0
        if nb_values is None:
            if nca == 2:  # 2D plot
                nb_values = 9
            else:  # 3D plot
                nb_values = 5
        if not isinstance(nb_values, dict):
            nb_values0 = {}
            for coord in coords:
                nb_values0[coord] = nb_values
            nb_values = nb_values0
        if steps is None:
            steps = {}
        for coord in coords:
            if coord not in steps:
                steps[coord] = (ranges[coord][1] - ranges[coord][0])/ \
                               (nb_values[coord]-1)
            else:
                nb_values[coord] = 1 + int(
                    (ranges[coord][1] - ranges[coord][0]) / steps[coord])
        #
        # 2/ Plots
        #    -----
        dom = chart_domain.domain()
        nc = len(chart_domain[:])
        ncp = len(coords)
        xx = [0] * nc
        if fixed_coords is not None:
            if len(fixed_coords) != nc - ncp:
                raise ValueError("Bad number of fixed coordinates.")
            for fc, val in fixed_coords.iteritems():
                xx[chart_domain[:].index(fc)] = val
        index_p = [chart_domain[:].index(cd) for cd in coords]
        resu = Graphics()
        ind = [0] * ncp
        ind_max = [0] * ncp
        ind_max[0] = nb_values[coords[0]]
        xmin = [ranges[cd][0] for cd in coords]
        step_tab = [steps[cd] for cd in coords]
        while ind != ind_max:
            for i in range(ncp):
                xx[index_p[i]] = xmin[i] + ind[i] * step_tab[i]
            if chart_domain.valid_coordinates(*xx,
                                              tolerance=1e-13,
                                              parameters=parameters):
                point = dom(xx, chart=chart_domain)
                resu += self.at(point).plot(chart=chart,
                                            ambient_coords=ambient_coords,
                                            mapping=mapping,
                                            scale=scale,
                                            color=color,
                                            print_label=False,
                                            parameters=parameters,
                                            **extra_options)
            # Next index:
            ret = 1
            for pos in range(ncp - 1, -1, -1):
                imax = nb_values[coords[pos]] - 1
                if ind[pos] != imax:
                    ind[pos] += ret
                    ret = 0
                elif ret == 1:
                    if pos == 0:
                        ind[pos] = imax + 1  # end point reached
                    else:
                        ind[pos] = 0
                        ret = 1
        if label_axes:
            if nca == 2:  # 2D graphic
                # We update the dictionary _extra_kwds (options to be passed
                # to show()), instead of using the method
                # Graphics.axes_labels() since the latter is not robust w.r.t.
                # graph addition
                resu._extra_kwds['axes_labels'] = [
                    r'$' + latex(ac) + r'$' for ac in ambient_coords
                ]
            else:  # 3D graphic
                labels = [str(ac) for ac in ambient_coords]
                resu = set_axes_labels(resu, *labels)
        return resu
Exemple #38
0
    def plot(self, **kwds):
        r"""
        Plot the initial triangulation associated to ``self``.

        INPUT:

        - ``radius`` - the radius of the disk; by default the length of
          the circle is the number of vertices
        - ``points_color`` - the color of the vertices; default 'black'
        - ``points_size`` - the size of the vertices; default 7
        - ``triangulation_color`` - the color of the arcs; default 'black'
        - ``triangulation_thickness`` - the thickness of the arcs; default 0.5
        - ``shading_color`` - the color of the shading used on neuter
          intervals; default 'lightgray'
        - ``reflections_color`` - the color of the reflection axes; default
          'blue'
        - ``reflections_thickness`` - the thickness of the reflection axes;
          default 1

        EXAMPLES::

            sage: Y = SineGordonYsystem('A',(6,4,3))
            sage: Y.plot()  # long time 2s
            Graphics object consisting of 219 graphics primitives
        """
        # Set up plotting options
        if 'radius' in kwds:
            radius = kwds['radius']
        else:
            radius = ceil(self.r() / (2 * pi))
        points_opts = {}
        if 'points_color' in kwds:
            points_opts['color'] = kwds['points_color']
        else:
            points_opts['color'] = 'black'
        if 'points_size' in kwds:
            points_opts['size'] = kwds['points_size']
        else:
            points_opts['size'] = 7
        triangulation_opts = {}
        if 'triangulation_color' in kwds:
            triangulation_opts['color'] = kwds['triangulation_color']
        else:
            triangulation_opts['color'] = 'black'
        if 'triangulation_thickness' in kwds:
            triangulation_opts['thickness'] = kwds['triangulation_thickness']
        else:
            triangulation_opts['thickness'] = 0.5
        shading_opts = {}
        if 'shading_color' in kwds:
            shading_opts['color'] = kwds['shading_color']
        else:
            shading_opts['color'] = 'lightgray'
        reflections_opts = {}
        if 'reflections_color' in kwds:
            reflections_opts['color'] = kwds['reflections_color']
        else:
            reflections_opts['color'] = 'blue'
        if 'reflections_thickness' in kwds:
            reflections_opts['thickness'] = kwds['reflections_thickness']
        else:
            reflections_opts['thickness'] = 1
        # Helper functions

        def triangle(x):
            (a, b) = sorted(x[:2])
            for p in self.vertices():
                if (p, a) in self.triangulation() or (a, p) in self.triangulation():
                    if (p, b) in self.triangulation() or (b, p) in self.triangulation():
                        if p < a or p > b:
                            return sorted((a, b, p))

        def plot_arc(radius, p, q, **opts):
            # TODO: THIS SHOULD USE THE EXISTING PLOT OF ARCS!
            # plot the arc from p to q differently depending on the type of self
            p = ZZ(p)
            q = ZZ(q)
            t = var('t')
            if p - q in [1, -1]:
                def f(t):
                    return (radius * cos(t), radius * sin(t))
                (p, q) = sorted([p, q])
                angle_p = vertex_to_angle(p)
                angle_q = vertex_to_angle(q)
                return parametric_plot(f(t), (t, angle_q, angle_p), **opts)
            if self.type() == 'A':
                angle_p = vertex_to_angle(p)
                angle_q = vertex_to_angle(q)
                if angle_p < angle_q:
                    angle_p += 2 * pi
                internal_angle = angle_p - angle_q
                if internal_angle > pi:
                    (angle_p, angle_q) = (angle_q + 2 * pi, angle_p)
                    internal_angle = angle_p - angle_q
                angle_center = (angle_p+angle_q) / 2
                hypotenuse = radius / cos(internal_angle / 2)
                radius_arc = hypotenuse * sin(internal_angle / 2)
                center = (hypotenuse * cos(angle_center),
                          hypotenuse * sin(angle_center))
                center_angle_p = angle_p + pi / 2
                center_angle_q = angle_q + 3 * pi / 2

                def f(t):
                    return (radius_arc * cos(t) + center[0],
                            radius_arc * sin(t) + center[1])
                return parametric_plot(f(t), (t, center_angle_p,
                                              center_angle_q), **opts)
            elif self.type() == 'D':
                if p >= q:
                    q += self.r()
                px = -2 * pi * p / self.r() + pi / 2
                qx = -2 * pi * q / self.r() + pi / 2
                arc_radius = (px - qx) / 2
                arc_center = qx + arc_radius

                def f(t):
                    return exp(I * ((cos(t) + I * sin(t)) *
                                    arc_radius + arc_center)) * radius
                return parametric_plot((real_part(f(t)), imag_part(f(t))),
                                       (t, 0, pi), **opts)

        def vertex_to_angle(v):
            # v==0 corresponds to pi/2
            return -2 * pi * RR(v) / self.r() + 5 * pi / 2

        # Begin plotting
        P = Graphics()
        # Shade neuter intervals
        neuter_intervals = [x for x in flatten(self.intervals()[:-1],
                                               max_level=1)
                            if x[2] in ["NR", "NL"]]
        shaded_triangles = map(triangle, neuter_intervals)
        for (p, q, r) in shaded_triangles:
            points = list(plot_arc(radius, p, q)[0])
            points += list(plot_arc(radius, q, r)[0])
            points += list(reversed(plot_arc(radius, p, r)[0]))
            P += polygon2d(points, **shading_opts)
        # Disk boundary
        P += circle((0, 0), radius, **triangulation_opts)
        # Triangulation
        for (p, q) in self.triangulation():
            P += plot_arc(radius, p, q, **triangulation_opts)
        if self.type() == 'D':
            s = radius / 50.0
            P += polygon2d([(s, 5 * s), (s, 7 * s),
                            (3 * s, 5 * s), (3 * s, 7 * s)],
                           color=triangulation_opts['color'])
            P += bezier_path([[(0, 0), (2 * s, 1 * s), (2 * s, 6 * s)],
                              [(2 * s, 10 * s), (s, 20 * s)],
                              [(0, 30 * s), (0, radius)]],
                             **triangulation_opts)
            P += bezier_path([[(0, 0), (-2 * s, 1 * s), (-2 * s, 6 * s)],
                              [(-2 * s, 10 * s), (-s, 20 * s)],
                              [(0, 30 * s), (0, radius)]],
                             **triangulation_opts)
            P += point((0, 0), zorder=len(P), **points_opts)
        # Vertices
        v_points = {x: (radius * cos(vertex_to_angle(x)),
                        radius * sin(vertex_to_angle(x)))
                    for x in self.vertices()}
        for v in v_points:
            P += point(v_points[v], zorder=len(P), **points_opts)
        # Reflection axes
        P += line([(0, 1.1 * radius), (0, -1.1 * radius)],
                  zorder=len(P), **reflections_opts)
        axis_angle = vertex_to_angle(-0.5 * (self.rk() + (1, 1))[1])
        (a, b) = (1.1 * radius * cos(axis_angle),
                  1.1 * radius * sin(axis_angle))
        P += line([(a, b), (-a, -b)], zorder=len(P), **reflections_opts)
        # Wrap up
        P.set_aspect_ratio(1)
        P.axes(False)
        return P
Exemple #39
0
    def plot(self,
             chart=None,
             ambient_coords=None,
             mapping=None,
             prange=None,
             include_end_point=(True, True),
             end_point_offset=(0.001, 0.001),
             parameters=None,
             color='red',
             style='-',
             label_axes=True,
             **kwds):
        r"""
        Plot the current curve in a Cartesian graph based on the
        coordinates of some ambient chart.

        The curve is drawn in terms of two (2D graphics) or three (3D graphics)
        coordinates of a given chart, called hereafter the *ambient chart*.
        The ambient chart's domain must overlap with the curve's codomain or
        with the codomain of the composite curve `\Phi\circ c`, where `c` is
        the current curve and `\Phi` some manifold differential map (argument
        ``mapping`` below).

        INPUT:

        - ``chart`` -- (default: ``None``) the ambient chart (see above);
          if ``None``, the default chart of the codomain of the curve (or of
          the curve composed with `\Phi`) is used

        - ``ambient_coords`` -- (default: ``None``) tuple containing the 2
          or 3 coordinates of the ambient chart in terms of which the plot
          is performed; if ``None``, all the coordinates of the ambient chart
          are considered

        - ``mapping`` -- (default: ``None``) differentiable mapping `\Phi`
          (instance of
          :class:`~sage.manifolds.differentiable.diff_map.DiffMap`)
          providing the link between the curve and the ambient chart ``chart``
          (cf. above); if ``None``, the ambient chart is supposed to be defined
          on the codomain of the curve.

        - ``prange`` -- (default: ``None``) range of the curve parameter for
          the plot; if ``None``, the entire parameter range declared during the
          curve construction is considered (with -Infinity
          replaced by ``-max_range`` and +Infinity by ``max_range``)

        - ``include_end_point`` -- (default: ``(True, True)``) determines
          whether the end points of ``prange`` are included in the plot

        - ``end_point_offset`` -- (default: ``(0.001, 0.001)``) offsets from
          the end points when they are not included in the plot: if
          ``include_end_point[0] == False``, the minimal value of the curve
          parameter used for the plot is ``prange[0] + end_point_offset[0]``,
          while if ``include_end_point[1] == False``, the maximal value is
          ``prange[1] - end_point_offset[1]``.

        - ``max_range`` -- (default: 8) numerical value substituted to
          +Infinity if the latter is the upper bound of the parameter range;
          similarly ``-max_range`` is the numerical valued substituted for
          -Infinity

        - ``parameters`` -- (default: ``None``) dictionary giving the numerical
          values of the parameters that may appear in the coordinate expression
          of the curve

        - ``color`` -- (default: 'red') color of the drawn curve

        - ``style`` -- (default: '-') color of the drawn curve; NB: ``style``
          is effective only for 2D plots

        - ``thickness`` -- (default: 1) thickness of the drawn curve

        - ``plot_points`` -- (default: 75) number of points to plot the curve

        - ``label_axes`` -- (default: ``True``) boolean determining whether the
          labels of the coordinate axes of ``chart`` shall be added to the
          graph; can be set to ``False`` if the graph is 3D and must be
          superposed with another graph.

        - ``aspect_ratio`` -- (default: ``'automatic'``) aspect ratio of the
          plot; the default value (``'automatic'``) applies only for 2D plots;
          for 3D plots, the default value is ``1`` instead

        OUTPUT:

        - a graphic object, either an instance of
          :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on
          2 coordinates of ``chart``) or an instance of
          :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e.
          based on 3 coordinates of ``chart``)

        EXAMPLES:

        Plot of the lemniscate of Gerono::

            sage: R2 = Manifold(2, 'R^2')
            sage: X.<x,y> = R2.chart()
            sage: R.<t> = RealLine()
            sage: c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c')
            sage: c.plot()  # 2D plot
            Graphics object consisting of 1 graphics primitive

        .. PLOT::

            R2 = Manifold(2, 'R^2')
            X = R2.chart('x y')
            t = RealLine().canonical_coordinate()
            c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c')
            g = c.plot()
            sphinx_plot(g)

        Plot for a subinterval of the curve's domain::

            sage: c.plot(prange=(0,pi))
            Graphics object consisting of 1 graphics primitive

        .. PLOT::

            R2 = Manifold(2, 'R^2')
            X = R2.chart('x y')
            t = RealLine().canonical_coordinate()
            c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c')
            g = c.plot(prange=(0,pi))
            sphinx_plot(g)

        Plot with various options::

            sage: c.plot(color='green', style=':', thickness=3, aspect_ratio=1)
            Graphics object consisting of 1 graphics primitive

        .. PLOT::

            R2 = Manifold(2, 'R^2')
            X = R2.chart('x y')
            t = RealLine().canonical_coordinate()
            c = R2.curve([sin(t), sin(2*t)/2], (t, 0, 2*pi), name='c')
            g = c.plot(color='green', style=':', thickness=3, aspect_ratio=1)
            sphinx_plot(g)

        Plot via a mapping to another manifold: loxodrome of a sphere viewed
        in `\RR^3`::

            sage: S2 = Manifold(2, 'S^2')
            sage: U = S2.open_subset('U')
            sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            sage: R3 = Manifold(3, 'R^3')
            sage: X3.<x,y,z> = R3.chart()
            sage: F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph),
            ....:                      sin(th)*sin(ph), cos(th)]}, name='F')
            sage: F.display()
            F: S^2 --> R^3
            on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th))
            sage: c = S2.curve([2*atan(exp(-t/10)), t], (t, -oo, +oo), name='c')
            sage: graph_c = c.plot(mapping=F, max_range=40,
            ....:                  plot_points=200, thickness=2, label_axes=False)  # 3D plot
            sage: graph_S2 = XS.plot(X3, mapping=F, number_values=11, color='black') # plot of the sphere
            sage: show(graph_c + graph_S2) # the loxodrome + the sphere

        .. PLOT::

            S2 = Manifold(2, 'S^2')
            U = S2.open_subset('U')
            XS = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            th, ph = XS[:]
            R3 = Manifold(3, 'R^3')
            X3 = R3.chart('x y z')
            F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), sin(th)*sin(ph),
                                            cos(th)]}, name='F')
            t = RealLine().canonical_coordinate()
            c = S2.curve([2*atan(exp(-t/10)), t], (t, -oo, +oo), name='c')
            graph_c = c.plot(mapping=F, max_range=40, plot_points=200,
                             thickness=2, label_axes=False)
            graph_S2 = XS.plot(X3, mapping=F, number_values=11, color='black')
            sphinx_plot(graph_c + graph_S2)

        Example of use of the argument ``parameters``: we define a curve with
        some symbolic parameters ``a`` and ``b``::

            sage: a, b = var('a b')
            sage: c = R2.curve([a*cos(t) + b, a*sin(t)], (t, 0, 2*pi), name='c')

        To make a plot, we set spectific values for ``a`` and ``b`` by means
        of the Python dictionary ``parameters``::

            sage: c.plot(parameters={a: 2, b: -3}, aspect_ratio=1)
            Graphics object consisting of 1 graphics primitive

        .. PLOT::

            R2 = Manifold(2, 'R^2')
            X = R2.chart('x y')
            t = RealLine().canonical_coordinate()
            a, b = var('a b')
            c = R2.curve([a*cos(t) + b, a*sin(t)], (t, 0, 2*pi), name='c')
            g = c.plot(parameters={a: 2, b: -3}, aspect_ratio=1)
            sphinx_plot(g)

        """
        from sage.rings.infinity import Infinity
        from sage.misc.functional import numerical_approx
        from sage.plot.graphics import Graphics
        from sage.plot.line import line
        from sage.manifolds.chart import RealChart
        from sage.manifolds.utilities import set_axes_labels
        #
        # Get the @options from kwds
        #
        thickness = kwds.pop('thickness')
        plot_points = kwds.pop('plot_points')
        max_range = kwds.pop('max_range')
        aspect_ratio = kwds.pop('aspect_ratio')
        #
        # The "effective" curve to be plotted
        #
        if mapping is None:
            eff_curve = self
        else:
            eff_curve = mapping.restrict(self.codomain()) * self
        #
        # The chart w.r.t. which the curve is plotted
        #
        if chart is None:
            chart = eff_curve._codomain.default_chart()
        elif not isinstance(chart, RealChart):
            raise TypeError("{} is not a real chart".format(chart))
        #
        # Coordinates of the above chart w.r.t. which the curve is plotted
        #
        if ambient_coords is None:
            ambient_coords = chart[:]  # all chart coordinates are used
        n_pc = len(ambient_coords)
        if n_pc != 2 and n_pc != 3:
            raise ValueError("the number of coordinates involved in the " +
                             "plot must be either 2 or 3, not {}".format(n_pc))
        # indices of plot coordinates
        ind_pc = [chart[:].index(pc) for pc in ambient_coords]
        #
        # Parameter range for the plot
        #
        if prange is None:
            prange = (self._domain.lower_bound(), self._domain.upper_bound())
        elif not isinstance(prange, (tuple, list)):
            raise TypeError("{} is neither a tuple nor a list".format(prange))
        elif len(prange) != 2:
            raise ValueError("the argument prange must be a tuple/list " +
                             "of 2 elements")
        tmin = prange[0]
        tmax = prange[1]
        if tmin == -Infinity:
            tmin = -max_range
        elif not include_end_point[0]:
            tmin = tmin + end_point_offset[0]
        if tmax == Infinity:
            tmax = max_range
        elif not include_end_point[1]:
            tmax = tmax - end_point_offset[1]
        tmin = numerical_approx(tmin)
        tmax = numerical_approx(tmax)
        #
        # The coordinate expression of the effective curve
        #
        canon_chart = self._domain.canonical_chart()
        transf = None
        for chart_pair in eff_curve._coord_expression:
            if chart_pair == (canon_chart, chart):
                transf = eff_curve._coord_expression[chart_pair]
                break
        else:
            # Search for a subchart
            for chart_pair in eff_curve._coord_expression:
                for schart in chart._subcharts:
                    if chart_pair == (canon_chart, schart):
                        transf = eff_curve._coord_expression[chart_pair]
        if transf is None:
            raise ValueError("No expression has been found for " +
                             "{} in terms of {}".format(self, chart))
        #
        # List of points for the plot curve
        #
        plot_curve = []
        dt = (tmax - tmin) / (plot_points - 1)
        t = tmin
        if parameters is None:
            for i in range(plot_points):
                x = transf(t, simplify=False)
                plot_curve.append([numerical_approx(x[j]) for j in ind_pc])
                t += dt
        else:
            for i in range(plot_points):
                x = transf(t, simplify=False)
                plot_curve.append([
                    numerical_approx(x[j].substitute(parameters))
                    for j in ind_pc
                ])
                t += dt
        #
        # The plot
        #
        resu = Graphics()
        resu += line(plot_curve,
                     color=color,
                     linestyle=style,
                     thickness=thickness)
        if n_pc == 2:  # 2D graphic
            resu.set_aspect_ratio(aspect_ratio)
            if label_axes:
                # We update the dictionary _extra_kwds (options to be passed
                # to show()), instead of using the method
                # Graphics.axes_labels() since the latter is not robust w.r.t.
                # graph addition
                resu._extra_kwds['axes_labels'] = [
                    r'$' + latex(pc) + r'$' for pc in ambient_coords
                ]
        else:  # 3D graphic
            if aspect_ratio == 'automatic':
                aspect_ratio = 1
            resu.aspect_ratio(aspect_ratio)
            if label_axes:
                labels = [str(pc) for pc in ambient_coords]
                resu = set_axes_labels(resu, *labels)
        return resu
    def plot(self,radius=1, orientation=1, cyclic_order_0=None, cyclic_order_1=None,verbose=False):

        """Plot the set of ideal curves on the surface S=S(g,1) of genus g with
        one puncture.

        The free group has rank N=2g, the trees T0 and T1 are roses
        transverse to a maximal set of ideal curves on S. The convex
        core is transverse to the two collections of curves: vertices
        are connected components of the complement of the union of the
        two sets of curves, edges are arcs separating two regions and
        squares are around intersections points of curves.

        For instance T0 can be set to be the rose with the trivial
        marking, while T1 is obtained from T0 by applying a mapping
        class (and not a general automorphism). The embedding of the
        mapping class group is that generated by the
        ``surface_dehn_twist()`` method of the ``FreeGroup`` class.

        The set of ideal curves of T0 is drawn as the boundary of a
        regular 2N-gone, and the set of ideal curves of T1 is drawn
        inside this 2N-gone.

        INPUT:

        - ``radius``: (default 1) the radius of the regular 2N-gone
          which is the fondamental domain of the surface.

        -``cyclic_order_0``: (default None) List of edges outgoing
         from the sole vertex of T0 ordered according to the embedding
         in the surface. A typical value in rank 4, compatible with
         the definition of ``FreeGroup.surface_dehn_twist()`` is :
         ['A','B','a','C','D','c','d','b']

        -``cyclic_order_1``: (default None) List of edges outgoing
         from the sole vertex of T1 ordered according to the embedding
         in the surface.

        EXAMPLES::
        
        sage: F=FreeGroup(4)
        sage: phi=mul([F.surface_dehn_twist(i) for i in [2,1,1,4]])
        sage: C=ConvexCore(phi)
        sage: C.plot_ideal_curve_diagram(cyclic_order_0=['A','B','a','C','D','c','d','b'])

        """

        from sage.plot.graphics import Graphics
        from sage.plot.line import Line
        from sage.plot.arrow import Arrow
        
        T0=self.tree(0)
        T1=self.tree(1)
        A0=T0.alphabet()
        N=len(A0)
        A1=T1.alphabet()

        # Let ``self`` be the convex core of trees T0 and T1. T0 and
        # T1 need to be roses. The trees T0 and T1 may be given as
        # embedded inside the surface. In this case the edges outgoing
        # from the sole vertex are cyclically ordered.

        if len(T0.vertices())!=1:
            raise ValueError('The tree on side 0 must be a rose')
        if len(T1.vertices())!=1:
            raise ValueError('The tree on side 1 must be a rose')
            
        
        if cyclic_order_0 is None:
            cyclic_order_0=getattr(T0,'cyclic_order',None)
        if cyclic_order_1 is None:
            cyclic_order_1=getattr(T1,'cyclic_order',None)

        if verbose:
            if cyclic_order_0 is not None:
                print "The tree on side 0 is embedded in the surface:",cyclic_order_0
            else:
                print "The tree on side 0 is not embedded in the surface, we will try to guess an embedding"
            if cyclic_order_1 is not None:
                print "The tree on side 1 is embedded in the surface:",cyclic_order_1
            else:
                print "The tree on side 1 is not embedded in the surface, we will try to guess an embedding"
            
        squares=self.squares()

        # Coherent orientation of the squares
            

        orientation=self.squares_orientation(orientation=orientation,verbose=verbose and verbose>1 and verbose-1)
            
        if verbose:
            print "Orientation of the squares:"
            if verbose>1:
                for i,sq in enumerate(squares):
                    print i,":",sq,":",orientation[i] 

        boundary=self.surface_boundary(orientation=orientation,verbose=verbose and verbose>1 and verbose-1)


        if verbose:
            print "Edges of the boundary:"
            print boundary
        
        # The boundary of the surface is an Eulerian circuit in the surface_boundary_graph

        
        eulerian_circuits=[]

	boundary_length=2*(self.tree(side=0).alphabet().cardinality()+self.tree(side=1).alphabet().cardinality())

        next=[(boundary[0],0)]
        if boundary[0][2][2]!=0:
            next.append((boundary[1],0))

        if verbose:
            print "Looking for an eulerian circuit in the boundary"

        while len(next)>0:

            e,current=next.pop()

            if verbose:
                print e,current
            
            for i in xrange(current+1,len(boundary)):
                if boundary[i]==e:
                    boundary[i],boundary[current]=boundary[current],boundary[i]
                    # now the boundary is the beginning of an eulerian
                    # circuit up to current
                    break

            # We check that the boundary up to current is acceptable

            acceptable=True

            # First, we check that two edges bounding a strongly
            # connected component of squares share the same
            # orientation
            
            oriented=set()
            for i in xrange(current+1):
                e=boundary[i]
                if e[2][2]!=0 and -e[2][2] in oriented: # edges with orientation 0 are isolated edges
                    acceptable=False
                    if verbose:
                        print "The current boundary does not respect orientation",e[2][2]
                    break
                else:
                    oriented.add(e[2][2])

            if not acceptable:
                continue
            
            # Next we check that this is compatible with the given
            # cyclic order

            if cyclic_order_0 is not None:                
                i=0
                j0=None
                while i<current+1 and boundary[i][2][1]!=0: 
                    i+=1
                if i<current+1:
                    j0=0
                    while j0<len(cyclic_order_0) and cyclic_order_0[j0]!=boundary[i][2][0]:
                        j0+=1
                
                while i<current:
                    i+=1
                    while i<current+1 and boundary[i][2][1]!=0:
                        i+=1
                    if i<current+1:
                        j0+=1
                        if j0==len(cyclic_order_0):
                            j0=0
                        if boundary[i][2][0]!=cyclic_order_0[j0]:
                            acceptable=False
                            if verbose:
                                print "The current boundary does not respect the given cyclic order on side 0"
                            break

            if not acceptable:
                continue
            
            if cyclic_order_1 is not None:                
                i=0
                j1=None
                while i<current+1 and boundary[i][2][1]!=1:
                    i+=1
                if i<current+1:
                    j1=0
                    while j1<len(cyclic_order_1) and cyclic_order_1[j1]!=boundary[i][2][0]:
                        j1+=1
                while i<current:
                    i+=1
                    while i<current+1 and boundary[i][2][1]!=1:
                        i+=1
                    if i<current+1:
                        j1+=1
                        if j1==len(cyclic_order_1):
                            j1=0
                        if boundary[i][2][0]!=cyclic_order_1[j1]:
                            acceptable=False
                            if verbose:
                                print "The current boundary does not respect the given cyclic order on side 1"

                            break

            if not acceptable:
                continue


            # If there are no given cyclic orders we check that on
            # both side there is only one connected component.

            if (cyclic_order_0 is None) and (cyclic_order_1 is None):
                tmp_cyclic_0=[boundary[i][2][0] for i in xrange(current+1) if boundary[i][2][1]==0] 
                i=0
                if len(tmp_cyclic_0)<2*len(A0):
                    while i<len(tmp_cyclic_0):
                        j=i
                        done=False
                        while True: 
                            aa=A0.inverse_letter(tmp_cyclic_0[j])
                            j=0
                            while j<len(tmp_cyclic_0) and tmp_cyclic_0[j]!=aa:
                                j+=1
                            if j==len(tmp_cyclic_0) or j==0:
                                i+=1
                                break
                            j-=1
                            if i==j:
                                acceptable=False
                                if verbose:
                                    print "There is more than one boundary component on side 0"
                                    print "Cyclic order on side 0:",tmp_cyclic_0
                                i=len(tmp_cyclic_0)
                                break

                if not acceptable:
                    continue


                tmp_cyclic_1=[boundary[i][2][0] for i in xrange(current+1) if boundary[i][2][1]==1] 
                i=0
                if len(tmp_cyclic_1)<2*len(A1):
                    while i<len(tmp_cyclic_1):
                        j=i
                        done=False
                        while True: 
                            aa=A1.inverse_letter(tmp_cyclic_1[j])
                            j=0
                            while j<len(tmp_cyclic_1) and tmp_cyclic_1[j]!=aa:
                                j+=1
                            if j==len(tmp_cyclic_1) or j==0:
                                i+=1
                                break
                            j-=1
                            if i==j:
                                acceptable=False
                                if verbose:
                                    print "There is more than one boundary component on side 1"
                                    print "Cyclic order on side 1:",tmp_cyclic_1
                                i=len(tmp_cyclic_1)
                                break

                if not acceptable:
                    continue

            if current+1==boundary_length:
                eulerian_circuits.append(boundary[:current+1])    
            
            for i in xrange(current+1,len(boundary)):
                e=boundary[i]
                if e[0]!=boundary[current][1] or (e[2][2]!=0 and -e[2][2] in oriented):
                    continue
                if e[2][1]==0 and (cyclic_order_0 is not None) and (j0 is not None):
                    jj0=j0+1
                    if jj0==len(cyclic_order_0):
                        jj0=0
                    if e[2][0]!=cyclic_order_0[jj0]:
                        continue
                elif e[2][1]==1 and (cyclic_order_1 is not None) and (j1 is not None):
                    jj1=j1+1
                    if jj1==len(cyclic_order_1):
                        jj1=0
                    if e[2][0]!=cyclic_order_1[jj1]:
                        continue

                next.append((e,current+1))
                
                            

                
        if verbose:
            print "Possible boundaries:",eulerian_circuits
        
        if len(eulerian_circuits)>1:
            print "There is an ambiguity on the choice of the boundary of the surface."
            print "Specify using optionnal argument cyclic_order_0 and cyclic_order_1."
            print "Possible choices:"
            for cyclic_order in eulerian_circuits:
                print "side 0:",[e[2][0] for e in cyclic_order if e[2][1]==0]
                print "side 1:",[e[2][0] for e in cyclic_order if e[2][1]==1]
            print "The first one is chosen"    
        elif len(eulerian_circuits)==0:
            print "There are no eulerian circuit in the boundary compatible with the given cyclic orders."
            print "Probably changing the orientation will solve this problem"
            return False
        
        cyclic_order=eulerian_circuits[0]

        # Now we can fix the orientation of the squares according to
        # the chosen boundary

        direct_orientation=set(e[2][2] for e in cyclic_order if e[2][2]!=0)
        
        for i in xrange(len(self.squares())):
            if orientation[i] in direct_orientation:
                orientation[i]=-1
            else:
                orientation[i]=1
        

        if verbose:
            print "Orientation of the squares coherent with the choice of the boundary"
            print orientation

        self._squares_orientation=orientation

        # We put the edges in the fundamental domain
                    
        initial_vertex=dict()
        terminal_vertex=dict()

        for a in A0.positive_letters():
            aa=A0.inverse_letter(a)
            slicea=[i for i in xrange(len(squares)) if squares[i][4]==a]
            size=len(slicea)+1

            if size==1:
                continue

            # sort the edges of the slice
            i=1
            sqi=slicea[0]
            sq=squares[sqi]
            if orientation[sqi]==1:
                start0=(sq[0],sq[1])
                endi=(sq[3],sq[2])
            else:
                start0=(sq[3],sq[2])
                endi=(sq[0],sq[1])

            while i<len(slicea):
                j=i
                while j<len(slicea):
                    sqjj=slicea[j]
                    sqj=squares[sqjj]
                    if orientation[sqjj]==1:
                        startj=(sqj[0],sqj[1])
                        endj=(sqj[3],sqj[2])
                    else:
                        startj=(sqj[3],sqj[2])
                        endj=(sqj[0],sqj[1])
                    
                    if endi==startj: # next(es[i-1])==es[j]:
                        slicea[j],slicea[i]=slicea[i],slicea[j]
                        i+=1
                        endi=endj
                        if i<j:
                            j=j-1
                    elif endj==start0: #next(es[j])==es[0]:
                        slicea=[slicea[j]]+slicea[:j]+slicea[j+1:]
                        i+=1
                        start0=startj
                        if i<j:
                            j=j-1
                    j+=1

            if verbose:
                print "Slice of", a,":",slicea       

            # put a curve for each edge of the slice
            for i,sqi in enumerate(slicea):
                sq=squares[sqi]
                if orientation[sqi]==1:
                    initial_vertex[(sq[0],sq[3],sq[5])]=(a,(i+1.0)/size)
                    terminal_vertex[(sq[1],sq[2],sq[5])]=(aa,(size-i-1.0)/size)
                else:
                    terminal_vertex[(sq[0],sq[3],sq[5])]=(a,(i+1.0)/size)
                    initial_vertex[(sq[1],sq[2],sq[5])]=(aa,(size-i-1.0)/size)


        # We compute the regular 2N-gone that is the foundamental domain
        # of the surface on side 0

        i=0
        while cyclic_order[i][2][1]==1:
            i+=1


        a=A0.inverse_letter(cyclic_order[i][2][0])
        polygon_side_0=[a]

        for j in xrange(2*N-1):
            k=0
            while cyclic_order[k][2][1]==1 or cyclic_order[k][2][0]!=a:
                k+=1
            k-=1
            while cyclic_order[k][2][1]==1:
                k-=1
                if k==0:
                    k=boundary_length-1
            a=A0.inverse_letter(cyclic_order[k][2][0])
            polygon_side_0.append(a)


        if verbose:
            print "Polygon bounding the fundamental domain of the surface:",polygon_side_0

        i=0
        while polygon_side_0[i]!=A0[0]:
            i+=1
        polygon_side_0=polygon_side_0[i:]+polygon_side_0[:i]
            
            
        g=Graphics()

        boundary_initial_vertex=dict()
        boundary_terminal_vertex=dict()

        for i,a in enumerate(polygon_side_0):
            boundary_initial_vertex[a]=(RR(radius*cos(i*pi/N)),RR(radius*sin(i*pi/N)))
            boundary_terminal_vertex[a]=(RR(radius*cos((i+1)*pi/N)),RR(radius*sin((i+1)*pi/N)))


        # Regular polygon
        text_decalage=1.05
        for a in polygon_side_0:
            x,y=boundary_initial_vertex[a]
            xx,yy=boundary_terminal_vertex[a]
            g+=line([(x,y),(xx,yy)],alpha=1,thickness=2,hue=5.0/6)
            g+=text(a,((x+xx)/2*text_decalage**2,(y+yy)/2*text_decalage**2),hue=5.0/6)
 

        for e in initial_vertex:
            a,p=initial_vertex[e]
            b=e[2]
            x=boundary_initial_vertex[a][0]+p*(boundary_terminal_vertex[a][0]-boundary_initial_vertex[a][0])
            y=boundary_initial_vertex[a][1]+p*(boundary_terminal_vertex[a][1]-boundary_initial_vertex[a][1])
            if e in terminal_vertex:
                aa,pp=terminal_vertex[e]
            else: # the end of e is at the singularity
                i=0
                j=0
                while cyclic_order[i][2][1]==0 or cyclic_order[i][2][0]!=b:
                    if cyclic_order[i][2][1]==0:
                        j=i
                    i+=1
                    
                if j==0 and cyclic_order[j][2][1]==1:
                    j=len(cyclic_order)-1
                    while cyclic_order[j][2][1]==1:
                        j-=1
                aa=A0.inverse_letter(cyclic_order[j][2][0])
                pp=0

            xx=boundary_initial_vertex[aa][0]+pp*(boundary_terminal_vertex[aa][0]-boundary_initial_vertex[aa][0])
            yy=boundary_initial_vertex[aa][1]+pp*(boundary_terminal_vertex[aa][1]-boundary_initial_vertex[aa][1])

            g+=line([(x,y),(xx,yy)],alpha=1,thickness=2,hue=RR(A1.rank(b))/N)
            
        for e in terminal_vertex:
            if e not in initial_vertex: # the initial vertex of e is the singularity
                aa,pp=terminal_vertex[e]
                xx=boundary_initial_vertex[aa][0]+pp*(boundary_terminal_vertex[aa][0]-boundary_initial_vertex[aa][0])
                yy=boundary_initial_vertex[aa][1]+pp*(boundary_terminal_vertex[aa][1]-boundary_initial_vertex[aa][1])
                b=A1.inverse_letter(e[2][0])
                i=0
                j=0
                while cyclic_order[i][2][1]==0 or cyclic_order[i][2][0]!=b:
                    if cyclic_order[i][2][1]==0:
                         j=i
                    i+=1
                    
                if j==0 and cyclic_order[j][2][1]==1:
                    j=len(cyclic_order)-1
                    while cyclic_order[j][2][1]==1:
                        j-=1
                a=A0.inverse_letter(cyclic_order[j][2][0])
                x=boundary_initial_vertex[a][0]
                y=boundary_initial_vertex[a][1]
                
                g+=line([(x,y),(xx,yy)],alpha=1,thickness=2,hue=RR(A1.rank(b))/N)

                g+=text(e[2][0],(text_decalage*xx,text_decalage*yy),hue=RR(A1.rank(b))/N)

        
        for e in self.isolated_edges():
            if e[2][1]==1:
                #The end of e is at the singularity
                b=e[2][0]
                i=0
                j=0
                while cyclic_order[i][2][1]==0 or cyclic_order[i][2][0]!=b:
                    if cyclic_order[i][2][1]==0:
                         j=i
                    i+=1
                    
                if j==0 and cyclic_order[j][2][1]==1:
                    j=len(cyclic_order)-1
                    while cyclic_order[j][2][1]==1:
                        j-=1
                a=A0.inverse_letter(cyclic_order[j][2][0])
                x=boundary_initial_vertex[a][0]
                y=boundary_initial_vertex[a][1]

                #The start of e is also at the singularity 
                bb=A1.inverse_letter(b)
                i=0
                j=0
                while cyclic_order[i][2][1]==0 or cyclic_order[i][2][0]!=bb:
                    if cyclic_order[i][2][1]==0:
                        j=i
                    i+=1
                    
                if j==0 and cyclic_order[j][2][1]==1:
                    j=len(cyclic_order)-1
                    while cyclic_order[j][2][1]==1:
                        j-=1
                aa=A0.inverse_letter(cyclic_order[j][2][0])


                xx=boundary_initial_vertex[aa][0]
                yy=boundary_initial_vertex[aa][1]

                
                g+=line([(x,y),(xx,yy)],alpha=1,thickness=2,hue=RR(A1.rank(b))/N)

                g+=text(b,(text_decalage*x,text_decalage*y),hue=RR(A1.rank(b))/N)

        for sq in self.twice_light_squares():
            b=A1.to_positive_letter(sq[5])
            #The end of b is at the singularity
            i=0
            j=0
            while cyclic_order[i][2][1]==0 or cyclic_order[i][2][0]!=b:
                if cyclic_order[i][2][1]==0:
                     j=i
                i+=1

            if j==0 and cyclic_order[j][2][1]==1:
                j=len(cyclic_order)-1
                while cyclic_order[j][2][1]==1:
                    j-=1
            a=A0.inverse_letter(cyclic_order[j][2][0])
            x=boundary_initial_vertex[a][0]
            y=boundary_initial_vertex[a][1]

            #The start of b is also at the singularity 
            bb=A1.inverse_letter(b)
            i=0
            j=0
            while cyclic_order[i][2][1]==0 or cyclic_order[i][2][0]!=bb:
                if cyclic_order[i][2][1]==0:
                    j=i
                i+=1

            if j==0 and cyclic_order[j][2][1]==1:
                j=len(cyclic_order)-1
                while cyclic_order[j][2][1]==1:
                    j-=1
            aa=A0.inverse_letter(cyclic_order[j][2][0])


            xx=boundary_initial_vertex[aa][0]
            yy=boundary_initial_vertex[aa][1]


            g+=line([(x,y),(xx,yy)],alpha=1,thickness=2,hue=RR(A1.rank(b))/N)

            g+=text(b,(text_decalage*x,text_decalage*y),hue=RR(A1.rank(b))/N)


        g.axes(False)

        return g
Exemple #41
0
    def plot(self,
             chart=None,
             ambient_coords=None,
             mapping=None,
             color='blue',
             print_label=True,
             label=None,
             label_color=None,
             fontsize=10,
             label_offset=0.1,
             parameters=None,
             **extra_options):
        r"""
        Plot the vector in a Cartesian graph based on the coordinates of some
        ambient chart.

        The vector is drawn in terms of two (2D graphics) or three (3D graphics)
        coordinates of a given chart, called hereafter the *ambient chart*.
        The vector's base point `p` (or its image `\Phi(p)` by some
        differentiable mapping `\Phi`) must lie in the ambient chart's domain.
        If `\Phi` is different from the identity mapping, the vector
        actually depicted is `\mathrm{d}\Phi_p(v)`, where `v` is the current
        vector (``self``) (see the example of a vector tangent to the
        2-sphere below, where `\Phi: S^2 \to \RR^3`).

        INPUT:

        - ``chart`` -- (default: ``None``) the ambient chart (see above); if
          ``None``, it is set to the default chart of the open set containing
          the point at which the vector (or the vector image via the
          differential `\mathrm{d}\Phi_p` of ``mapping``) is defined

        - ``ambient_coords`` -- (default: ``None``) tuple containing the 2
          or 3 coordinates of the ambient chart in terms of which the plot
          is performed; if ``None``, all the coordinates of the ambient
          chart are considered

        - ``mapping`` -- (default: ``None``)
          :class:`~sage.manifolds.differentiable.diff_map.DiffMap`;
          differentiable mapping `\Phi` providing the link between the
          point `p` at which the vector is defined and the ambient chart
          ``chart``: the domain of ``chart`` must contain `\Phi(p)`;
          if ``None``, the identity mapping is assumed

        - ``scale`` -- (default: 1) value by which the length of the arrow
          representing the vector is multiplied

        - ``color`` -- (default: 'blue') color of the arrow representing the
          vector

        - ``print_label`` -- (boolean; default: ``True``) determines whether a
          label is printed next to the arrow representing the vector

        - ``label`` -- (string; default: ``None``) label printed next to the
          arrow representing the vector; if ``None``, the vector's symbol is
          used, if any

        - ``label_color`` -- (default: ``None``) color to print the label;
          if ``None``, the value of ``color`` is used

        - ``fontsize`` -- (default: 10) size of the font used to print the
          label

        - ``label_offset`` -- (default: 0.1) determines the separation between
          the vector arrow and the label

        - ``parameters`` -- (default: ``None``) dictionary giving the numerical
          values of the parameters that may appear in the coordinate expression
          of ``self`` (see example below)

        - ``**extra_options`` -- extra options for the arrow plot, like
          ``linestyle``, ``width`` or ``arrowsize`` (see
          :func:`~sage.plot.arrow.arrow2d` and
          :func:`~sage.plot.plot3d.shapes.arrow3d` for details)

        OUTPUT:

        - a graphic object, either an instance of
          :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on
          2 coordinates of ``chart``) or an instance of
          :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e.
          based on 3 coordinates of ``chart``)

        EXAMPLES:

        Vector tangent to a 2-dimensional manifold::

            sage: M = Manifold(2, 'M')
            sage: X.<x,y> = M.chart()
            sage: p = M((2,2), name='p')
            sage: Tp = M.tangent_space(p)
            sage: v = Tp((2, 1), name='v') ; v
            Tangent vector v at Point p on the 2-dimensional differentiable
             manifold M

        Plot of the vector alone (arrow + label)::

            sage: v.plot()
            Graphics object consisting of 2 graphics primitives

        Plot atop of the chart grid::

            sage: X.plot() + v.plot()
            Graphics object consisting of 20 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            p = M((2,2), name='p'); Tp = M.tangent_space(p)
            v = Tp((2, 1), name='v')
            g = X.plot() + v.plot()
            sphinx_plot(g)

        Plots with various options::

            sage: X.plot() + v.plot(color='green', scale=2, label='V')
            Graphics object consisting of 20 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            p = M((2,2), name='p'); Tp = M.tangent_space(p)
            v = Tp((2, 1), name='v')
            g = X.plot() + v.plot(color='green', scale=2, label='V')
            sphinx_plot(g)

        ::

            sage: X.plot() + v.plot(print_label=False)
            Graphics object consisting of 19 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            p = M((2,2), name='p'); Tp = M.tangent_space(p)
            v = Tp((2, 1), name='v')
            g = X.plot() + v.plot(print_label=False)
            sphinx_plot(g)

        ::

            sage: X.plot() + v.plot(color='green', label_color='black',
            ....:                   fontsize=20, label_offset=0.2)
            Graphics object consisting of 20 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            p = M((2,2), name='p'); Tp = M.tangent_space(p)
            v = Tp((2, 1), name='v')
            g = X.plot() + v.plot(color='green', label_color='black', fontsize=20, label_offset=0.2)
            sphinx_plot(g)

        ::

            sage: X.plot() + v.plot(linestyle=':', width=4, arrowsize=8,
            ....:                   fontsize=20)
            Graphics object consisting of 20 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            p = M((2,2), name='p'); Tp = M.tangent_space(p)
            v = Tp((2, 1), name='v')
            g = X.plot() + v.plot(linestyle=':', width=4, arrowsize=8, fontsize=20)
            sphinx_plot(g)

        Plot with specific values of some free parameters::

            sage: var('a b')
            (a, b)
            sage: v = Tp((1+a, -b^2), name='v') ; v.display()
            v = (a + 1) d/dx - b^2 d/dy
            sage: X.plot() + v.plot(parameters={a: -2, b: 3})
            Graphics object consisting of 20 graphics primitives

        Special case of the zero vector::

            sage: v = Tp.zero() ; v
            Tangent vector zero at Point p on the 2-dimensional differentiable
             manifold M
            sage: X.plot() + v.plot()
            Graphics object consisting of 19 graphics primitives

        Vector tangent to a 4-dimensional manifold::

            sage: M = Manifold(4, 'M')
            sage: X.<t,x,y,z> = M.chart()
            sage: p = M((0,1,2,3), name='p')
            sage: Tp = M.tangent_space(p)
            sage: v = Tp((5,4,3,2), name='v') ; v
            Tangent vector v at Point p on the 4-dimensional differentiable
             manifold M

        We cannot make a 4D plot directly::

            sage: v.plot()
            Traceback (most recent call last):
            ...
            ValueError: the number of coordinates involved in the plot must
             be either 2 or 3, not 4

        Rather, we have to select some chart coordinates for the plot, via
        the argument ``ambient_coords``. For instance, for a 2-dimensional
        plot in terms of the coordinates `(x, y)`::

            sage: v.plot(ambient_coords=(x,y))
            Graphics object consisting of 2 graphics primitives

        .. PLOT::

            M = Manifold(4, 'M')
            X = M.chart('t x y z'); t,x,y,z = X[:]
            p = M((0,1,2,3), name='p'); Tp = M.tangent_space(p)
            v = Tp((5,4,3,2), name='v')
            g = X.plot(ambient_coords=(x,y)) + v.plot(ambient_coords=(x,y))
            sphinx_plot(g)

        This plot involves only the components `v^x` and `v^y` of `v`.
        Similarly, for a 3-dimensional plot in terms of the coordinates
        `(t, x, y)`::

            sage: g = v.plot(ambient_coords=(t,x,z))
            sage: print(g)
            Graphics3d Object

        This plot involves only the components `v^t`,  `v^x` and `v^z` of `v`.
        A nice 3D view atop the coordinate grid is obtained via::

            sage: (X.plot(ambient_coords=(t,x,z))  # long time
            ....:  + v.plot(ambient_coords=(t,x,z),
            ....:           label_offset=0.5, width=6))
            Graphics3d Object

        .. PLOT::

            M = Manifold(4, 'M')
            X = M.chart('t x y z'); t,x,y,z = X[:]
            p = M((0,1,2,3), name='p'); Tp = M.tangent_space(p)
            v = Tp((5,4,3,2), name='v')
            g = X.plot(ambient_coords=(t,x,z)) + v.plot(ambient_coords=(t,x,z),
                       label_offset=0.5, width=6)
            sphinx_plot(g)

        An example of plot via a differential mapping: plot of a vector tangent
        to a 2-sphere viewed in `\RR^3`::

            sage: S2 = Manifold(2, 'S^2')
            sage: U = S2.open_subset('U') # the open set covered by spherical coord.
            sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            sage: R3 = Manifold(3, 'R^3')
            sage: X3.<x,y,z> = R3.chart()
            sage: F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph),
            ....:                                 sin(th)*sin(ph),
            ....:                                 cos(th)]}, name='F')
            sage: F.display() # the standard embedding of S^2 into R^3
            F: S^2 --> R^3
            on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th))
            sage: p = U.point((pi/4, 7*pi/4), name='p')
            sage: v = XS.frame()[1].at(p) ; v  # the coordinate vector d/dphi at p
            Tangent vector d/dph at Point p on the 2-dimensional differentiable
             manifold S^2
            sage: graph_v = v.plot(mapping=F)
            sage: graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9)  # long time
            sage: graph_v + graph_S2  # long time
            Graphics3d Object

        .. PLOT::

            S2 = Manifold(2, 'S^2')
            U = S2.open_subset('U')
            XS = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            th, ph = XS[:]
            R3 = Manifold(3, 'R^3')
            X3 = R3.chart('x y z')
            F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), sin(th)*sin(ph),
                                            cos(th)]}, name='F')
            p = U.point((pi/4, 7*pi/4), name='p')
            v = XS.frame()[1].at(p)
            graph_v = v.plot(mapping=F)
            graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9)
            sphinx_plot(graph_v + graph_S2)

        """
        from sage.plot.arrow import arrow2d
        from sage.plot.text import text
        from sage.plot.graphics import Graphics
        from sage.plot.plot3d.shapes import arrow3d
        from sage.plot.plot3d.shapes2 import text3d
        from sage.misc.functional import numerical_approx
        from sage.manifolds.differentiable.chart import DiffChart

        scale = extra_options.pop("scale")

        #
        # The "effective" vector to be plotted
        #
        if mapping is None:
            eff_vector = self
            base_point = self._point
        else:
            #!# check
            # For efficiency, the method FiniteRankFreeModuleMorphism._call_()
            # is called instead of FiniteRankFreeModuleMorphism.__call__()
            eff_vector = mapping.differential(self._point)._call_(self)
            base_point = mapping(self._point)
        #
        # The chart w.r.t. which the vector is plotted
        #
        if chart is None:
            chart = base_point.parent().default_chart()
        elif not isinstance(chart, DiffChart):
            raise TypeError("{} is not a chart".format(chart))
        #
        # Coordinates of the above chart w.r.t. which the vector is plotted
        #
        if ambient_coords is None:
            ambient_coords = chart[:]  # all chart coordinates are used
        n_pc = len(ambient_coords)
        if n_pc != 2 and n_pc != 3:
            raise ValueError("the number of coordinates involved in the " +
                             "plot must be either 2 or 3, not {}".format(n_pc))
        # indices coordinates involved in the plot:
        ind_pc = [chart[:].index(pc) for pc in ambient_coords]
        #
        # Components of the vector w.r.t. the chart frame
        #
        basis = chart.frame().at(base_point)
        vcomp = eff_vector.comp(basis=basis)[:]
        xp = base_point.coord(chart=chart)
        #
        # The arrow
        #
        resu = Graphics()
        if parameters is None:
            coord_tail = [numerical_approx(xp[i]) for i in ind_pc]
            coord_head = [
                numerical_approx(xp[i] + scale * vcomp[i]) for i in ind_pc
            ]
        else:
            coord_tail = [
                numerical_approx(xp[i].substitute(parameters)) for i in ind_pc
            ]
            coord_head = [
                numerical_approx(
                    (xp[i] + scale * vcomp[i]).substitute(parameters))
                for i in ind_pc
            ]
        if coord_head != coord_tail:
            if n_pc == 2:
                resu += arrow2d(tailpoint=coord_tail,
                                headpoint=coord_head,
                                color=color,
                                **extra_options)
            else:
                resu += arrow3d(coord_tail,
                                coord_head,
                                color=color,
                                **extra_options)
        #
        # The label
        #
        if print_label:
            if label is None:
                if n_pc == 2 and self._latex_name is not None:
                    label = r'$' + self._latex_name + r'$'
                if n_pc == 3 and self._name is not None:
                    label = self._name
            if label is not None:
                xlab = [xh + label_offset for xh in coord_head]
                if label_color is None:
                    label_color = color
                if n_pc == 2:
                    resu += text(label,
                                 xlab,
                                 fontsize=fontsize,
                                 color=label_color)
                else:
                    resu += text3d(label,
                                   xlab,
                                   fontsize=fontsize,
                                   color=label_color)
        return resu
Exemple #42
0
    def plot(self, chart=None, ambient_coords=None, mapping=None,
             chart_domain=None, fixed_coords=None, ranges=None, max_value=8,
             nb_values=None, steps=None,scale=1, color='blue', parameters=None,
             label_axes=True, **extra_options):
        r"""
        Plot the vector field in a Cartesian graph based on the coordinates
        of some ambient chart.

        The vector field is drawn in terms of two (2D graphics) or three
        (3D graphics) coordinates of a given chart, called hereafter the
        *ambient chart*.
        The vector field's base points `p` (or their images `\Phi(p)` by some
        differentiable mapping `\Phi`) must lie in the ambient chart's domain.

        INPUT:

        - ``chart`` -- (default: ``None``) the ambient chart (see above); if
          ``None``, the default chart of the vector field's domain is used
        - ``ambient_coords`` -- (default: ``None``) tuple containing the 2 or 3
          coordinates of the ambient chart in terms of which the plot is
          performed; if ``None``, all the coordinates of the ambient chart are
          considered
        - ``mapping`` -- (default: ``None``) differentiable mapping `\Phi`
          (instance of
          :class:`~sage.geometry.manifolds.diffmapping.DiffMapping`)
          providing the link between the vector field's domain and
          the ambient chart ``chart``; if ``None``, the identity mapping is
          assumed
        - ``chart_domain`` -- (default: ``None``) chart on the vector
          field's domain to define the points at which vector arrows are to be
          plotted; if ``None``, the default chart of the vector field's domain
          is used
        - ``fixed_coords`` -- (default: ``None``) dictionary with keys the
          coordinates of ``chart_domain`` that are kept fixed and with values
          the value of these coordinates; if ``None``, all the coordinates of
          ``chart_domain`` are used
        - ``ranges`` -- (default: ``None``) dictionary with keys the
          coordinates of ``chart_domain`` to be used and values
          tuples ``(x_min,x_max)`` specifying the
          coordinate range for the plot; if ``None``, the entire coordinate
          range declared during the construction of ``chart_domain`` is
          considered (with ``-Infinity`` replaced by ``-max_value`` and
          ``+Infinity`` by ``max_value``)
        - ``max_value`` -- (default: 8) numerical value substituted to
          ``+Infinity`` if the latter is the upper bound of the range of a
          coordinate for which the plot is performed over the entire coordinate
          range (i.e. for which no specific plot range has been set in
          ``ranges``); similarly ``-max_value`` is the numerical valued
          substituted for ``-Infinity``
        - ``nb_values`` -- (default: ``None``) either an integer or a dictionary
          with keys the coordinates of ``chart_domain`` to be used and values
          the number of values of the coordinate for sampling
          the part of the vector field's domain involved in the plot ; if
          ``nb_values`` is a single integer, it represents the number of
          values for all coordinates; if ``nb_values`` is ``None``, it is set
          to 9 for a 2D plot and to 5 for a 3D plot
        - ``steps`` -- (default: ``None``) dictionary with keys the coordinates
          of ``chart_domain`` to be used and values the step between each
          constant value of the coordinate; if ``None``, the step is computed
          from the coordinate range (specified in ``ranges``) and ``nb_values``.
          On the contrary, if the step is provided for some coordinate, the
          corresponding number of values is deduced from it and the coordinate
          range.
        - ``scale`` -- (default: 1) value by which the lengths of the arrows
          representing the vectors is multiplied
        - ``color`` -- (default: 'blue') color of the arrows representing the
          vectors
        - ``parameters`` -- (default: ``None``) dictionary giving the numerical
          values of the parameters that may appear in the coordinate expression
          of the vector field (see example below)
        - ``label_axes`` -- (default: ``True``) boolean determining whether the
          labels of the coordinate axes of ``chart`` shall be added to the
          graph; can be set to ``False`` if the graph is 3D and must be
          superposed with another graph.
        - ``**extra_options`` -- extra options for the arrow plot, like
          ``linestyle``, ``width`` or ``arrowsize`` (see
          :func:`~sage.plot.arrow.arrow2d` and
          :func:`~sage.plot.plot3d.shapes.arrow3d` for details)

        OUTPUT:

        - a graphic object, either an instance of
          :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on
          2 coordinates of ``chart``) or an instance of
          :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e.
          based on 3 coordinates of ``chart``)

        EXAMPLES:

        Plot of a vector field on a 2-dimensional manifold::

            sage: Manifold._clear_cache_() # for doctests only
            sage: M = Manifold(2, 'M')
            sage: X.<x,y> = M.chart()
            sage: v = M.vector_field(name='v')
            sage: v[:] = -y, x ; v.display()
            v = -y d/dx + x d/dy
            sage: v.plot()
            Graphics object consisting of 80 graphics primitives

        Plot with various options::

            sage: v.plot(scale=0.5, color='green', linestyle='--', width=1, arrowsize=6)
            Graphics object consisting of 80 graphics primitives
            sage: v.plot(max_value=4, nb_values=5, scale=0.5)
            Graphics object consisting of 24 graphics primitives

        Plots along a line of fixed coordinate::

            sage: v.plot(fixed_coords={x: -2})
            Graphics object consisting of 9 graphics primitives
            sage: v.plot(fixed_coords={y: 1})
            Graphics object consisting of 9 graphics primitives

        Let us now consider a vector field on a 4-dimensional manifold::

            sage: Manifold._clear_cache_() # for doctests only
            sage: M = Manifold(4, 'M')
            sage: X.<t,x,y,z> = M.chart()
            sage: v = M.vector_field(name='v')
            sage: v[:] = (t/8)^2, -t*y/4, t*x/4, t*z/4 ; v.display()
            v = 1/64*t^2 d/dt - 1/4*t*y d/dx + 1/4*t*x d/dy + 1/4*t*z d/dz

        We cannot make a 4D plot directly::

            sage: v.plot()
            Traceback (most recent call last):
            ...
            ValueError: the number of ambient coordinates must be either 2 or 3, not 4

        Rather, we have to select some coordinates for the plot, via
        the argument ``ambient_coords``. For instance, for a 3D plot::

            sage: v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1})
            Graphics3d Object
            sage: v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0},
            ....:        ranges={x: (-2,2), y: (-2,2), t: (-1, 4)}, nb_values=4)
            Graphics3d Object

        or, for a 2D plot::

            sage: v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0})
            Graphics object consisting of 80 graphics primitives
            sage: v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0})
            Graphics object consisting of 72 graphics primitives

        An example of plot via a differential mapping: plot of a vector field
        tangent to a 2-sphere viewed in `\RR^3`::

            sage: S2 = Manifold(2, 'S^2')
            sage: U = S2.open_subset('U') # the open set covered by spherical coord.
            sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            sage: R3 = Manifold(3, 'R^3')
            sage: X3.<x,y,z> = R3.chart()
            sage: F = S2.diff_mapping(R3, {(XS, X3): [sin(th)*cos(ph),
            ....:                     sin(th)*sin(ph), cos(th)]}, name='F')
            sage: F.display() # the standard embedding of S^2 into R^3
            F: S^2 --> R^3
            on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th))
            sage: v = XS.frame()[1] ; v
            vector field 'd/dph' on the open subset 'U' of the 2-dimensional manifold 'S^2'
            sage: graph_v = v.plot(chart=X3, mapping=F, label_axes=False)
            sage: graph_S2 = XS.plot(chart=X3, mapping=F, nb_values=9)
            sage: show(graph_v + graph_S2)

        """
        from sage.rings.infinity import Infinity
        from sage.misc.functional import numerical_approx
        from sage.misc.latex import latex
        from sage.plot.graphics import Graphics
        from sage.geometry.manifolds.chart import Chart
        from sage.geometry.manifolds.utilities import set_axes_labels
        #
        # 1/ Treatment of input parameters
        #    -----------------------------
        if chart is None:
            chart = self._domain.default_chart()
        elif not isinstance(chart, Chart):
            raise TypeError("{} is not a chart".format(chart))
        if chart_domain is None:
            chart_domain = self._domain.default_chart()
        elif not isinstance(chart_domain, Chart):
            raise TypeError("{} is not a chart".format(chart_domain))
        elif not chart_domain.domain().is_subset(self._domain):
            raise ValueError("The domain of {} is not ".format(chart_domain) +
                             "included in the domain of {}".format(self))
        if fixed_coords is None:
            coords = chart_domain._xx
        else:
            fixed_coord_list = fixed_coords.keys()
            coords = []
            for coord in chart_domain._xx:
                if coord not in fixed_coord_list:
                    coords.append(coord)
            coords = tuple(coords)
        if ambient_coords is None:
            ambient_coords = chart[:]
        elif not isinstance(ambient_coords, tuple):
            ambient_coords = tuple(ambient_coords)
        nca = len(ambient_coords)
        if nca != 2 and nca !=3:
            raise ValueError("the number of ambient coordinates must be " +
                             "either 2 or 3, not {}".format(nca))
        if ranges is None:
            ranges = {}
        ranges0 = {}
        for coord in coords:
            if coord in ranges:
                ranges0[coord] = (numerical_approx(ranges[coord][0]),
                                  numerical_approx(ranges[coord][1]))
            else:
                bounds = chart_domain._bounds[chart_domain[:].index(coord)]
                if bounds[0][0] == -Infinity:
                    xmin = numerical_approx(-max_value)
                elif bounds[0][1]:
                    xmin = numerical_approx(bounds[0][0])
                else:
                    xmin = numerical_approx(bounds[0][0] + 1.e-3)
                if bounds[1][0] == Infinity:
                    xmax = numerical_approx(max_value)
                elif bounds[1][1]:
                    xmax = numerical_approx(bounds[1][0])
                else:
                    xmax = numerical_approx(bounds[1][0] - 1.e-3)
                ranges0[coord] = (xmin, xmax)
        ranges = ranges0
        if nb_values is None:
            if nca == 2: # 2D plot
                nb_values = 9
            else:   # 3D plot
                nb_values = 5
        if not isinstance(nb_values, dict):
            nb_values0 = {}
            for coord in coords:
                nb_values0[coord] = nb_values
            nb_values = nb_values0
        if steps is None:
            steps = {}
        for coord in coords:
            if coord not in steps:
                steps[coord] = (ranges[coord][1] - ranges[coord][0])/ \
                               (nb_values[coord]-1)
            else:
                nb_values[coord] = 1 + int(
                           (ranges[coord][1] - ranges[coord][0])/ steps[coord])
        #
        # 2/ Plots
        #    -----
        dom = chart_domain.domain()
        nc = len(chart_domain[:])
        ncp = len(coords)
        xx = [0] * nc
        if fixed_coords is not None:
            if len(fixed_coords) != nc - ncp:
                raise ValueError("Bad number of fixed coordinates.")
            for fc, val in fixed_coords.iteritems():
                xx[chart_domain[:].index(fc)] = val
        index_p = [chart_domain[:].index(cd) for cd in coords]
        resu = Graphics()
        ind = [0] * ncp
        ind_max = [0] * ncp
        ind_max[0] = nb_values[coords[0]]
        xmin = [ranges[cd][0] for cd in coords]
        step_tab = [steps[cd] for cd in coords]
        while ind != ind_max:
            for i in range(ncp):
                xx[index_p[i]] = xmin[i] + ind[i]*step_tab[i]
            if chart_domain.valid_coordinates(*xx, tolerance=1e-13,
                                              parameters=parameters):
                point = dom(xx, chart=chart_domain)
                resu += self.at(point).plot(chart=chart,
                                 ambient_coords=ambient_coords, mapping=mapping,
                                 scale=scale, color=color, print_label=False,
                                 parameters=parameters,
                                 **extra_options)
            # Next index:
            ret = 1
            for pos in range(ncp-1,-1,-1):
                imax = nb_values[coords[pos]] - 1
                if ind[pos] != imax:
                    ind[pos] += ret
                    ret = 0
                elif ret == 1:
                    if pos == 0:
                        ind[pos] = imax + 1 # end point reached
                    else:
                        ind[pos] = 0
                        ret = 1
        if label_axes:
            if nca==2:  # 2D graphic
                # We update the dictionary _extra_kwds (options to be passed
                # to show()), instead of using the method
                # Graphics.axes_labels() since the latter is not robust w.r.t.
                # graph addition
                resu._extra_kwds['axes_labels'] = [r'$'+latex(ac)+r'$'
                                                   for ac in ambient_coords]
            else: # 3D graphic
                labels = [str(ac) for ac in ambient_coords]
                resu = set_axes_labels(resu, *labels)
        return resu
Exemple #43
0
def plot(hyperplane_arrangement, **kwds):
    r"""
    Return a plot of the hyperplane arrangement.  

    If the arrangement is in 4 dimensions but inessential, a plot of
    the essentialization is returned.

    .. NOTE::

        This function is available as the
        :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.plot`
        method of hyperplane arrangements. You should not call this
        function directly, only through the method.

    INPUT:

    - ``hyperplane_arrangement`` -- the hyperplane arrangement to plot

    - ``**kwds`` -- plot options: see
      :mod:`sage.geometry.hyperplane_arrangement.plot`.

    OUTPUT:
    
    A graphics object of the plot.

    EXAMPLES::

        sage: B = hyperplane_arrangements.semiorder(4)
        sage: B.plot()
        Displaying the essentialization.
        Graphics3d Object
    """
    N = len(hyperplane_arrangement)
    dim = hyperplane_arrangement.dimension()
    if hyperplane_arrangement.base_ring().characteristic() != 0:
        raise NotImplementedError('must be a field of characteristic 0')
    elif dim == 4:
        if not hyperplane_arrangement.is_essential():
            print('Displaying the essentialization.')
            hyperplane_arrangement = hyperplane_arrangement.essentialization()
    elif dim not in [1, 2, 3]:  # revise to handle 4d
        return  # silently
    # handle extra keywords
    if 'hyperplane_colors' in kwds:
        hyp_colors = kwds.pop('hyperplane_colors')
        if not isinstance(hyp_colors,
                          list):  # we assume its a single color then
            hyp_colors = [hyp_colors] * N
    else:
        HSV_tuples = [(i * 1.0 / N, 0.8, 0.9) for i in range(N)]
        hyp_colors = [hsv_to_rgb(*x) for x in HSV_tuples]
    if 'hyperplane_labels' in kwds:
        hyp_labels = kwds.pop('hyperplane_labels')
        has_hyp_label = True
        if not isinstance(hyp_labels, list):  # we assume its a boolean then
            hyp_labels = [hyp_labels] * N
        relabeled = []
        for i in range(N):
            if hyp_labels[i] in [True, 'long']:
                relabeled.append(True)
            else:
                relabeled.append(str(i))
        hyp_labels = relabeled
    else:
        has_hyp_label = False
    if 'label_colors' in kwds:
        label_colors = kwds.pop('label_colors')
        has_label_color = True
        if not isinstance(label_colors,
                          list):  # we assume its a single color then
            label_colors = [label_colors] * N
    else:
        has_label_color = False
    if 'label_fontsize' in kwds:
        label_fontsize = kwds.pop('label_fontsize')
        has_label_fontsize = True
        if not isinstance(label_fontsize,
                          list):  # we assume its a single size then
            label_fontsize = [label_fontsize] * N
    else:
        has_label_fontsize = False
    if 'label_offsets' in kwds:
        has_offsets = True
        offsets = kwds.pop('label_offsets')
    else:
        has_offsets = False  # give default values below
    hyperplane_legend = kwds.pop('hyperplane_legend',
                                 'long' if dim < 3 else False)
    if 'hyperplane_opacities' in kwds:
        hyperplane_opacities = kwds.pop('hyperplane_opacities')
        has_opacity = True
        if not isinstance(hyperplane_opacities,
                          list):  # we assume a single number then
            hyperplane_opacities = [hyperplane_opacities] * N
    else:
        has_opacity = False
    point_sizes = kwds.pop('point_sizes', 50)
    if not isinstance(point_sizes, list):
        point_sizes = [point_sizes] * N
    if 'ranges' in kwds:
        ranges_set = True
        ranges = kwds.pop('ranges')
        if not type(ranges) in [list, tuple]:  # ranges is a single number
            ranges = [ranges] * N
        # So ranges is some type of list.
        elif dim == 2:  # arrangement of lines in the plane
            if not type(ranges[0]) in [list, tuple]:  # a single interval
                ranges = [ranges] * N
        elif dim == 3:  # arrangement of planes in 3-space
            if not type(ranges[0][0]) in [list, tuple]:
                ranges = [ranges] * N
        elif dim not in [2, 3]:  # ranges is not an option unless dim is 2 or 3
            ranges_set = False
        else:  # a list of intervals, one for each hyperplane is given
            pass  # ranges does not need to be modified
    else:
        ranges_set = False  # give default values below
    # the extra keywords have now been handled
    # now handle the legend
    if dim in [1, 2]:  # points on a line or lines in the plane
        if hyperplane_legend in [True, 'long']:
            hyps = hyperplane_arrangement.hyperplanes()
            legend_labels = [hyps[i]._latex_() for i in range(N)]
        elif hyperplane_legend == 'short':
            legend_labels = [str(i) for i in range(N)]
    else:  # dim==3,  arrangement of planes in 3-space
        if hyperplane_legend in [True, 'long']:
            legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'long')
        elif hyperplane_legend == 'short':
            legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'short')
    ## done handling the legend
    ## now create the plot
    p = Graphics()
    for i in range(N):
        newk = copy(kwds)
        if has_hyp_label:
            newk['hyperplane_label'] = hyp_labels[i]
            if has_offsets:
                if not isinstance(offsets, list):
                    newk['label_offset'] = offsets
                else:
                    newk['label_offset'] = offsets[i]
        else:
            newk['hyperplane_label'] = False
        if has_label_color:
            newk['label_color'] = label_colors[i]
        if has_label_fontsize:
            newk['label_fontsize'] = label_fontsize[i]
        if has_opacity:
            newk['opacity'] = hyperplane_opacities[i]
        if dim == 1:
            newk['point_size'] = point_sizes[i]
        if dim in [1, 2] and hyperplane_legend:  # more options than T/F
            newk['legend_label'] = legend_labels[i]
        if ranges_set:
            newk['ranges'] = ranges[i]
        p += plot_hyperplane(hyperplane_arrangement[i],
                             rgbcolor=hyp_colors[i],
                             **newk)
    if dim == 1:
        if hyperplane_legend:  # there are more options than T/F
            p.legend(True)
        return p
    elif dim == 2:
        if hyperplane_legend:  # there are more options than T/F
            p.legend(True)
        return p
    else:  # dim==3
        if hyperplane_legend:  # there are more options than T/F
            return p, legend3d
        else:
            return p
Exemple #44
0
def arc(center, r1, r2=None, angle=0.0, sector=(0.0, 2 * pi), **options):
    r"""
    An arc (that is a portion of a circle or an ellipse)

    Type ``arc.options`` to see all options.

    INPUT:

    - ``center`` - 2-tuple of real numbers - position of the center.

    - ``r1``, ``r2`` - positive real numbers - radii of the ellipse. If only ``r1``
      is set, then the two radii are supposed to be equal and this function returns
      an arc of circle.

    - ``angle`` - real number - angle between the horizontal and the axis that
      corresponds to ``r1``.

    - ``sector`` - 2-tuple (default: (0,2*pi))- angles sector in which the arc will
      be drawn.

    OPTIONS:

    - ``alpha`` - float (default: 1) - transparency

    - ``thickness`` - float (default: 1) - thickness of the arc

    - ``color``, ``rgbcolor`` - string or 2-tuple (default: 'blue') - the color
      of the arc

    - ``linestyle`` - string (default: ``'solid'``) - The style of the line,
      which is one of ``'dashed'``, ``'dotted'``, ``'solid'``, ``'dashdot'``,
      or ``'--'``, ``':'``, ``'-'``, ``'-.'``, respectively.

    EXAMPLES:

    Plot an arc of circle centered at (0,0) with radius 1 in the sector
    `(\pi/4,3*\pi/4)`::

        sage: arc((0,0), 1, sector=(pi/4,3*pi/4))
        Graphics object consisting of 1 graphics primitive

    Plot an arc of an ellipse between the angles 0 and `\pi/2`::

        sage: arc((2,3), 2, 1, sector=(0,pi/2))
        Graphics object consisting of 1 graphics primitive

    Plot an arc of a rotated ellipse between the angles 0 and `\pi/2`::

        sage: arc((2,3), 2, 1, angle=pi/5, sector=(0,pi/2))
        Graphics object consisting of 1 graphics primitive

    Plot an arc of an ellipse in red with a dashed linestyle::

        sage: arc((0,0), 2, 1, 0, (0,pi/2), linestyle="dashed", color="red")
        Graphics object consisting of 1 graphics primitive
        sage: arc((0,0), 2, 1, 0, (0,pi/2), linestyle="--", color="red")
        Graphics object consisting of 1 graphics primitive

    The default aspect ratio for arcs is 1.0::

        sage: arc((0,0), 1, sector=(pi/4,3*pi/4)).aspect_ratio()
        1.0

    It is not possible to draw arcs in 3D::

        sage: A = arc((0,0,0), 1)
        Traceback (most recent call last):
        ...
        NotImplementedError
    """
    from sage.plot.all import Graphics

    # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
    # Otherwise matplotlib complains.
    scale = options.get('scale', None)
    if isinstance(scale, (list, tuple)):
        scale = scale[0]
    if scale == 'semilogy' or scale == 'semilogx':
        options['aspect_ratio'] = 'automatic'

    if len(center) == 2:
        if r2 is None:
            r2 = r1
        g = Graphics()
        g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
        if len(sector) != 2:
            raise ValueError("the sector must consist of two angles")
        g.add_primitive(
            Arc(center[0], center[1], r1, r2, angle, sector[0], sector[1],
                options))
        return g
    elif len(center) == 3:
        raise NotImplementedError
Exemple #45
0
def arc(center, r1, r2=None, angle=0.0, sector=(0.0, 2 * pi), **options):
    r"""
    An arc (that is a portion of a circle or an ellipse)

    Type ``arc.options`` to see all options.

    INPUT:

    - ``center`` - 2-tuple of real numbers - position of the center.

    - ``r1``, ``r2`` - positive real numbers - radii of the ellipse. If only ``r1``
      is set, then the two radii are supposed to be equal and this function returns
      an arc of of circle.

    - ``angle`` - real number - angle between the horizontal and the axis that
      corresponds to ``r1``.

    - ``sector`` - 2-tuple (default: (0,2*pi))- angles sector in which the arc will
      be drawn.

    OPTIONS:

    - ``alpha`` - float (default: 1) - transparency

    - ``thickness`` - float (default: 1) - thickness of the arc

    - ``color``, ``rgbcolor`` - string or 2-tuple (default: 'blue') - the color
      of the arc

    - ``linestyle`` - string (default: ``'solid'``) - The style of the line,
      which is one of ``'dashed'``, ``'dotted'``, ``'solid'``, ``'dashdot'``,
      or ``'--'``, ``':'``, ``'-'``, ``'-.'``, respectively.

    EXAMPLES:

    Plot an arc of circle centered at (0,0) with radius 1 in the sector
    `(\pi/4,3*\pi/4)`::

        sage: arc((0,0), 1, sector=(pi/4,3*pi/4))
        Graphics object consisting of 1 graphics primitive

    Plot an arc of an ellipse between the angles 0 and `\pi/2`::

        sage: arc((2,3), 2, 1, sector=(0,pi/2))
        Graphics object consisting of 1 graphics primitive

    Plot an arc of a rotated ellipse between the angles 0 and `\pi/2`::

        sage: arc((2,3), 2, 1, angle=pi/5, sector=(0,pi/2))
        Graphics object consisting of 1 graphics primitive

    Plot an arc of an ellipse in red with a dashed linestyle::

        sage: arc((0,0), 2, 1, 0, (0,pi/2), linestyle="dashed", color="red")
        Graphics object consisting of 1 graphics primitive
        sage: arc((0,0), 2, 1, 0, (0,pi/2), linestyle="--", color="red")
        Graphics object consisting of 1 graphics primitive

    The default aspect ratio for arcs is 1.0::

        sage: arc((0,0), 1, sector=(pi/4,3*pi/4)).aspect_ratio()
        1.0

    It is not possible to draw arcs in 3D::

        sage: A = arc((0,0,0), 1)
        Traceback (most recent call last):
        ...
        NotImplementedError
    """
    from sage.plot.all import Graphics

    # Reset aspect_ratio to 'automatic' in case scale is 'semilog[xy]'.
    # Otherwise matplotlib complains.
    scale = options.get('scale', None)
    if isinstance(scale, (list, tuple)):
        scale = scale[0]
    if scale == 'semilogy' or scale == 'semilogx':
        options['aspect_ratio'] = 'automatic'

    if len(center) == 2:
        if r2 is None:
            r2 = r1
        g = Graphics()
        g._set_extra_kwds(Graphics._extract_kwds_for_show(options))
        if len(sector) != 2:
            raise ValueError("the sector must consist of two angles")
        g.add_primitive(Arc(
            center[0], center[1],
            r1, r2,
            angle,
            sector[0], sector[1],
            options))
        return g
    elif len(center) == 3:
        raise NotImplementedError
Exemple #46
0
    def finalize(self, G):
        r"""
        Finalize a root system plot.

        INPUT:

        - ``G`` -- a root system plot or ``0``

        This sets the aspect ratio to 1 and remove the axes. This
        should be called by all the user-level plotting methods of
        root systems. This will become mostly obsolete when
        customization options won't be lost anymore upon addition of
        graphics objects and there will be a proper empty object for
        2D and 3D plots.

        EXAMPLES::

            sage: L = RootSystem(["B",2,1]).ambient_space()
            sage: options = L.plot_parse_options()
            sage: p = L.plot_roots(plot_options=options)
            sage: p += L.plot_coroots(plot_options=options)
            sage: p.axes()
            True
            sage: p = options.finalize(p)
            sage: p.axes()
            False
            sage: p.aspect_ratio()
            1.0

            sage: options = L.plot_parse_options(affine=False)
            sage: p = L.plot_roots(plot_options=options)
            sage: p += point([[1,1,0]])
            sage: p = options.finalize(p)
            sage: p.aspect_ratio()
            [1.0, 1.0, 1.0]

        If the input is ``0``, this returns an empty graphics object::

            sage: type(options.finalize(0))
            <class 'sage.plot.plot3d.base.Graphics3dGroup'>

            sage: options = L.plot_parse_options()
            sage: type(options.finalize(0))
            <class 'sage.plot.graphics.Graphics'>
            sage: list(options.finalize(0))
            []
        """
        from sage.plot.graphics import Graphics
        if self.dimension == 2:
            if G == 0:
                G = Graphics()
            G.set_aspect_ratio(1)
            # TODO: make this customizable
            G.axes(False)
        elif self.dimension == 3:
            if G == 0:
                from sage.plot.plot3d.base import Graphics3dGroup
                G = Graphics3dGroup()
            G.aspect_ratio(1)
            # TODO: Configuration axes
        return G
Exemple #47
0
    def _graphics(self, plot_curve, ambient_coords, thickness=1,
                  aspect_ratio='automatic', color='red', style='-',
                  label_axes=True):
        r"""
        Plot a 2D or 3D curve in a Cartesian graph with axes labeled by
        the ambient coordinates; it is invoked by the methods
        :meth:`plot` of
        :class:`~sage.manifolds.differentiable.curve.DifferentiableCurve`,
        and its subclasses
        (:class:`~sage.manifolds.differentiable.integrated_curve.IntegratedCurve`,
        :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedAutoparallelCurve`,
        and
        :class:`~sage.manifolds.differentiable.integrated_curve.IntegratedGeodesic`).

        TESTS::

            sage: M = Manifold(2, 'R^2')
            sage: X.<x,y> = M.chart()
            sage: R.<t> = RealLine()
            sage: c = M.curve([cos(t), sin(t)], (t, 0, 2*pi), name='c')
            sage: graph = c._graphics([[1,2], [3,4]], [x,y])
            sage: graph._objects[0].xdata == [1,3]
            True
            sage: graph._objects[0].ydata == [2,4]
            True
            sage: graph._objects[0]._options['thickness'] == 1
            True
            sage: graph._extra_kwds['aspect_ratio'] == 'automatic'
            True
            sage: graph._objects[0]._options['rgbcolor'] == 'red'
            True
            sage: graph._objects[0]._options['linestyle'] == '-'
            True
            sage: l = [r'$'+latex(x)+r'$', r'$'+latex(y)+r'$']
            sage: graph._extra_kwds['axes_labels'] == l
            True

        """

        from sage.plot.graphics import Graphics
        from sage.plot.line import line
        from sage.manifolds.utilities import set_axes_labels


        #
        # The plot
        #
        n_pc = len(ambient_coords)
        resu = Graphics()
        resu += line(plot_curve, color=color, linestyle=style,
                     thickness=thickness)
        if n_pc == 2:  # 2D graphic
            resu.set_aspect_ratio(aspect_ratio)
            if label_axes:
                # We update the dictionary _extra_kwds (options to be passed
                # to show()), instead of using the method
                # Graphics.axes_labels() since the latter is not robust w.r.t.
                # graph addition
                resu._extra_kwds['axes_labels'] = [r'$'+latex(pc)+r'$'
                                                   for pc in ambient_coords]
        else: # 3D graphic
            if aspect_ratio == 'automatic':
                aspect_ratio = 1
            resu.aspect_ratio(aspect_ratio)
            if label_axes:
                labels = [str(pc) for pc in ambient_coords]
                resu = set_axes_labels(resu, *labels)
        return resu
Exemple #48
0
    def plot(self, chart=None, ambient_coords=None, mapping=None,
             chart_domain=None, fixed_coords=None, ranges=None,
             number_values=None, steps=None,
             parameters=None, label_axes=True, **extra_options):
        r"""
        Plot the vector field in a Cartesian graph based on the coordinates
        of some ambient chart.

        The vector field is drawn in terms of two (2D graphics) or three
        (3D graphics) coordinates of a given chart, called hereafter the
        *ambient chart*.
        The vector field's base points `p` (or their images `\Phi(p)` by some
        differentiable mapping `\Phi`) must lie in the ambient chart's domain.

        INPUT:

        - ``chart`` -- (default: ``None``) the ambient chart (see above); if
          ``None``, the default chart of the vector field's domain is used

        - ``ambient_coords`` -- (default: ``None``) tuple containing the 2
          or 3 coordinates of the ambient chart in terms of which the plot
          is performed; if ``None``, all the coordinates of the ambient
          chart are considered

        - ``mapping`` -- :class:`~sage.manifolds.differentiable.diff_map.DiffMap`
          (default: ``None``); differentiable map `\Phi` providing the link
          between the vector field's domain and the ambient chart ``chart``;
          if ``None``, the identity map is assumed

        - ``chart_domain`` -- (default: ``None``) chart on the vector field's
          domain to define the points at which vector arrows are to be plotted;
          if ``None``, the default chart of the vector field's domain is used

        - ``fixed_coords`` -- (default: ``None``) dictionary with keys the
          coordinates of ``chart_domain`` that are kept fixed and with values
          the value of these coordinates; if ``None``, all the coordinates of
          ``chart_domain`` are used

        - ``ranges`` -- (default: ``None``) dictionary with keys the
          coordinates of ``chart_domain`` to be used and values tuples
          ``(x_min, x_max)`` specifying the coordinate range for the plot;
          if ``None``, the entire coordinate range declared during the
          construction of ``chart_domain`` is considered (with ``-Infinity``
          replaced by ``-max_range`` and ``+Infinity`` by ``max_range``)

        - ``number_values`` -- (default: ``None``) either an integer or a
          dictionary with keys the coordinates of ``chart_domain`` to be
          used and values the number of values of the coordinate for sampling
          the part of the vector field's domain involved in the plot ; if
          ``number_values`` is a single integer, it represents the number of
          values for all coordinates; if ``number_values`` is ``None``, it is
          set to 9 for a 2D plot and to 5 for a 3D plot

        - ``steps`` -- (default: ``None``) dictionary with keys the
          coordinates of ``chart_domain`` to be used and values the step
          between each constant value of the coordinate; if ``None``, the
          step is computed from the coordinate range (specified in ``ranges``)
          and ``number_values``; on the contrary, if the step is provided
          for some coordinate, the corresponding number of values is deduced
          from it and the coordinate range

        - ``parameters`` -- (default: ``None``) dictionary giving the numerical
          values of the parameters that may appear in the coordinate expression
          of the vector field (see example below)

        - ``label_axes`` -- (default: ``True``) boolean determining whether
          the labels of the coordinate axes of ``chart`` shall be added to
          the graph; can be set to ``False`` if the graph is 3D and must be
          superposed with another graph

        - ``color`` -- (default: 'blue') color of the arrows representing
          the vectors

        - ``max_range`` -- (default: 8) numerical value substituted to
          ``+Infinity`` if the latter is the upper bound of the range of a
          coordinate for which the plot is performed over the entire coordinate
          range (i.e. for which no specific plot range has been set in
          ``ranges``); similarly ``-max_range`` is the numerical valued
          substituted for ``-Infinity``

        - ``scale`` -- (default: 1) value by which the lengths of the arrows
          representing the vectors is multiplied

        - ``**extra_options`` -- extra options for the arrow plot, like
          ``linestyle``, ``width`` or ``arrowsize`` (see
          :func:`~sage.plot.arrow.arrow2d` and
          :func:`~sage.plot.plot3d.shapes.arrow3d` for details)

        OUTPUT:

        - a graphic object, either an instance of
          :class:`~sage.plot.graphics.Graphics` for a 2D plot (i.e. based on
          2 coordinates of ``chart``) or an instance of
          :class:`~sage.plot.plot3d.base.Graphics3d` for a 3D plot (i.e.
          based on 3 coordinates of ``chart``)

        EXAMPLES:

        Plot of a vector field on a 2-dimensional manifold::

            sage: M = Manifold(2, 'M')
            sage: X.<x,y> = M.chart()
            sage: v = M.vector_field(name='v')
            sage: v[:] = -y, x ; v.display()
            v = -y d/dx + x d/dy
            sage: v.plot()
            Graphics object consisting of 80 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            v = M.vector_field(name='v'); v[:] = -y, x
            g = v.plot()
            sphinx_plot(g)

        Plot with various options::

            sage: v.plot(scale=0.5, color='green', linestyle='--', width=1,
            ....:        arrowsize=6)
            Graphics object consisting of 80 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            v = M.vector_field(name='v'); v[:] = -y, x
            g = v.plot(scale=0.5, color='green', linestyle='--', width=1, arrowsize=6)
            sphinx_plot(g)

        ::

            sage: v.plot(max_range=4, number_values=5, scale=0.5)
            Graphics object consisting of 24 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            v = M.vector_field(name='v'); v[:] = -y, x
            g = v.plot(max_range=4, number_values=5, scale=0.5)
            sphinx_plot(g)

        Plot using parallel computation::

            sage: Parallelism().set(nproc=2)
            sage: v.plot(scale=0.5,  number_values=10, linestyle='--', width=1,
            ....:        arrowsize=6)
            Graphics object consisting of 100 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            v = M.vector_field(name='v'); v[:] = -y, x
            g = v.plot(scale=0.5,  number_values=10, linestyle='--', width=1, arrowsize=6)
            sphinx_plot(g)

        ::

            sage: Parallelism().set(nproc=1)  # switch off parallelization

        Plots along a line of fixed coordinate::

            sage: v.plot(fixed_coords={x: -2})
            Graphics object consisting of 9 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            v = M.vector_field(name='v'); v[:] = -y, x
            g = v.plot(fixed_coords={x: -2})
            sphinx_plot(g)

        ::

            sage: v.plot(fixed_coords={y: 1})
            Graphics object consisting of 9 graphics primitives

        .. PLOT::

            M = Manifold(2, 'M')
            X = M.chart('x y'); x, y = X[:]
            v = M.vector_field(name='v'); v[:] = -y, x
            g = v.plot(fixed_coords={y: 1})
            sphinx_plot(g)

        Let us now consider a vector field on a 4-dimensional manifold::

            sage: M = Manifold(4, 'M')
            sage: X.<t,x,y,z> = M.chart()
            sage: v = M.vector_field(name='v')
            sage: v[:] = (t/8)^2, -t*y/4, t*x/4, t*z/4 ; v.display()
            v = 1/64*t^2 d/dt - 1/4*t*y d/dx + 1/4*t*x d/dy + 1/4*t*z d/dz

        We cannot make a 4D plot directly::

            sage: v.plot()
            Traceback (most recent call last):
            ...
            ValueError: the number of ambient coordinates must be either 2 or 3, not 4

        Rather, we have to select some coordinates for the plot, via
        the argument ``ambient_coords``. For instance, for a 3D plot::

            sage: v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1},  # long time
            ....:        number_values=4)
            Graphics3d Object

        .. PLOT::

            M = Manifold(4, 'M')
            X = M.chart('t x y z') ; t,x,y,z = X[:]
            v = M.vector_field(name='v')
            v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4
            sphinx_plot(v.plot(ambient_coords=(x, y, z), fixed_coords={t: 1},
                               number_values=4))

        ::

            sage: v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0},  # long time
            ....:        ranges={x: (-2,2), y: (-2,2), t: (-1, 4)},
            ....:        number_values=4)
            Graphics3d Object

        .. PLOT::

            M = Manifold(4, 'M')
            X = M.chart('t x y z'); t,x,y,z = X[:]
            v = M.vector_field(name='v')
            v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4
            sphinx_plot(v.plot(ambient_coords=(x, y, t), fixed_coords={z: 0},
                               ranges={x: (-2,2), y: (-2,2), t: (-1, 4)},
                               number_values=4))

        or, for a 2D plot::

            sage: v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0})  # long time
            Graphics object consisting of 80 graphics primitives

        .. PLOT::

            M = Manifold(4, 'M')
            X = M.chart('t x y z'); t,x,y,z = X[:]
            v = M.vector_field(name='v')
            v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4
            g = v.plot(ambient_coords=(x, y), fixed_coords={t: 1, z: 0})
            sphinx_plot(g)

        ::

            sage: v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0})  # long time
            Graphics object consisting of 72 graphics primitives

        .. PLOT::

            M = Manifold(4, 'M')
            X = M.chart('t x y z'); t,x,y,z = X[:]
            v = M.vector_field(name='v')
            v[:] = v[:] = (t/8)**2, -t*y/4, t*x/4, t*z/4
            g = v.plot(ambient_coords=(x, t), fixed_coords={y: 1, z: 0})
            sphinx_plot(g)

        An example of plot via a differential mapping: plot of a vector field
        tangent to a 2-sphere viewed in `\RR^3`::

            sage: S2 = Manifold(2, 'S^2')
            sage: U = S2.open_subset('U') # the open set covered by spherical coord.
            sage: XS.<th,ph> = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            sage: R3 = Manifold(3, 'R^3')
            sage: X3.<x,y,z> = R3.chart()
            sage: F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph),
            ....:                       sin(th)*sin(ph), cos(th)]}, name='F')
            sage: F.display() # the standard embedding of S^2 into R^3
            F: S^2 --> R^3
            on U: (th, ph) |--> (x, y, z) = (cos(ph)*sin(th), sin(ph)*sin(th), cos(th))
            sage: v = XS.frame()[1] ; v  # the coordinate vector d/dphi
            Vector field d/dph on the Open subset U of the 2-dimensional
             differentiable manifold S^2
            sage: graph_v = v.plot(chart=X3, mapping=F, label_axes=False)
            sage: graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9)
            sage: graph_v + graph_S2
            Graphics3d Object

        .. PLOT::

            S2 = Manifold(2, 'S^2')
            U = S2.open_subset('U')
            XS = U.chart(r'th:(0,pi):\theta ph:(0,2*pi):\phi')
            th, ph = XS[:]
            R3 = Manifold(3, 'R^3')
            X3 = R3.chart('x y z')
            F = S2.diff_map(R3, {(XS, X3): [sin(th)*cos(ph), sin(th)*sin(ph),
                                            cos(th)]}, name='F')
            v = XS.frame()[1]
            graph_v = v.plot(chart=X3, mapping=F, label_axes=False)
            graph_S2 = XS.plot(chart=X3, mapping=F, number_values=9)
            sphinx_plot(graph_v + graph_S2)

        Note that the default values of some arguments of the method ``plot``
        are stored in the dictionary ``plot.options``::

            sage: v.plot.options  # random (dictionary output)
            {'color': 'blue', 'max_range': 8, 'scale': 1}

        so that they can be adjusted by the user::

            sage: v.plot.options['color'] = 'red'

        From now on, all plots of vector fields will use red as the default
        color. To restore the original default options, it suffices to type::

            sage: v.plot.reset()

        """
        from sage.rings.infinity import Infinity
        from sage.misc.functional import numerical_approx
        from sage.misc.latex import latex
        from sage.plot.graphics import Graphics
        from sage.manifolds.chart import RealChart
        from sage.manifolds.utilities import set_axes_labels
        from sage.parallel.decorate import parallel
        from sage.parallel.parallelism import Parallelism

        #
        # 1/ Treatment of input parameters
        #    -----------------------------
        max_range = extra_options.pop("max_range")
        scale = extra_options.pop("scale")
        color = extra_options.pop("color")
        if chart is None:
            chart = self._domain.default_chart()
        elif not isinstance(chart, RealChart):
            raise TypeError("{} is not a chart on a real ".format(chart) +
                            "manifold")
        if chart_domain is None:
            chart_domain = self._domain.default_chart()
        elif not isinstance(chart_domain, RealChart):
            raise TypeError("{} is not a chart on a ".format(chart_domain) +
                            "real manifold")
        elif not chart_domain.domain().is_subset(self._domain):
            raise ValueError("the domain of {} is not ".format(chart_domain) +
                             "included in the domain of {}".format(self))
        coords_full = tuple(chart_domain[:]) # all coordinates of chart_domain
        if fixed_coords is None:
            coords = coords_full
        else:
            fixed_coord_list = fixed_coords.keys()
            coords = []
            for coord in coords_full:
                if coord not in fixed_coord_list:
                    coords.append(coord)
            coords = tuple(coords)
        if ambient_coords is None:
            ambient_coords = chart[:]
        elif not isinstance(ambient_coords, tuple):
            ambient_coords = tuple(ambient_coords)
        nca = len(ambient_coords)
        if nca != 2 and nca !=3:
            raise ValueError("the number of ambient coordinates must be " +
                             "either 2 or 3, not {}".format(nca))
        if ranges is None:
            ranges = {}
        ranges0 = {}
        for coord in coords:
            if coord in ranges:
                ranges0[coord] = (numerical_approx(ranges[coord][0]),
                                  numerical_approx(ranges[coord][1]))
            else:
                bounds = chart_domain._bounds[coords_full.index(coord)]
                xmin0 = bounds[0][0]
                xmax0 = bounds[1][0]
                if xmin0 == -Infinity:
                    xmin = numerical_approx(-max_range)
                elif bounds[0][1]:
                    xmin = numerical_approx(xmin0)
                else:
                    xmin = numerical_approx(xmin0 + 1.e-3)
                if xmax0 == Infinity:
                    xmax = numerical_approx(max_range)
                elif bounds[1][1]:
                    xmax = numerical_approx(xmax0)
                else:
                    xmax = numerical_approx(xmax0 - 1.e-3)
                ranges0[coord] = (xmin, xmax)
        ranges = ranges0
        if number_values is None:
            if nca == 2: # 2D plot
                number_values = 9
            else:   # 3D plot
                number_values = 5
        if not isinstance(number_values, dict):
            number_values0 = {}
            for coord in coords:
                number_values0[coord] = number_values
            number_values = number_values0
        if steps is None:
            steps = {}
        for coord in coords:
            if coord not in steps:
                steps[coord] = (ranges[coord][1] - ranges[coord][0])/ \
                               (number_values[coord]-1)
            else:
                number_values[coord] = 1 + int(
                           (ranges[coord][1] - ranges[coord][0])/ steps[coord])
        #
        # 2/ Plots
        #    -----
        dom = chart_domain.domain()
        vector = self.restrict(dom)
        if vector.parent().destination_map() is dom.identity_map():
            if mapping is not None:
                vector = mapping.pushforward(vector)
                mapping = None
        nc = len(coords_full)
        ncp = len(coords)
        xx = [0] * nc
        if fixed_coords is not None:
            if len(fixed_coords) != nc - ncp:
                raise ValueError("bad number of fixed coordinates")
            for fc, val in fixed_coords.items():
                xx[coords_full.index(fc)] = val
        ind_coord = []
        for coord in coords:
            ind_coord.append(coords_full.index(coord))

        resu = Graphics()
        ind = [0] * ncp
        ind_max = [0] * ncp
        ind_max[0] = number_values[coords[0]]
        xmin = [ranges[cd][0] for cd in coords]
        step_tab = [steps[cd] for cd in coords]

        nproc = Parallelism().get('tensor')
        if nproc != 1 and nca == 2:
            # parallel plot construct : Only for 2D plot (at  moment) !

            # creation of the list of parameters
            list_xx = []

            while ind != ind_max:
                for i in  range(ncp):
                    xx[ind_coord[i]] = xmin[i] + ind[i]*step_tab[i]

                if chart_domain.valid_coordinates(*xx, tolerance=1e-13,
                                                  parameters=parameters):

                    # needed a xx*1 to copy the list by value
                    list_xx.append(xx*1)

                # Next index:
                ret = 1
                for pos in range(ncp-1,-1,-1):
                    imax = number_values[coords[pos]] - 1
                    if ind[pos] != imax:
                        ind[pos] += ret
                        ret = 0
                    elif ret == 1:
                        if pos == 0:
                            ind[pos] = imax + 1 # end point reached
                        else:
                            ind[pos] = 0
                            ret = 1

            lol = lambda lst, sz: [lst[i:i+sz] for i in range(0, len(lst), sz)]
            ind_step = max(1, int(len(list_xx)/nproc/2))
            local_list = lol(list_xx,ind_step)

            # definition of the list of input parameters
            listParalInput = [(vector, dom, ind_part,
                               chart_domain, chart,
                               ambient_coords, mapping,
                               scale, color, parameters,
                               extra_options)
                              for ind_part in local_list]


            # definition of the parallel function
            @parallel(p_iter='multiprocessing', ncpus=nproc)
            def add_point_plot(vector, dom, xx_list, chart_domain, chart,
                               ambient_coords, mapping, scale, color,
                               parameters, extra_options):
                count = 0
                for xx in xx_list:
                    point = dom(xx, chart=chart_domain)
                    part = vector.at(point).plot(chart=chart,
                                                 ambient_coords=ambient_coords,
                                                 mapping=mapping,scale=scale,
                                                 color=color, print_label=False,
                                                 parameters=parameters,
                                                 **extra_options)
                    if count == 0:
                        local_resu = part
                    else:
                        local_resu += part
                    count += 1
                return local_resu

            # parallel execution and reconstruction of the plot
            for ii, val in add_point_plot(listParalInput):
                resu += val

        else:
            # sequential plot
            while ind != ind_max:
                for i in range(ncp):
                    xx[ind_coord[i]] = xmin[i] + ind[i]*step_tab[i]
                if chart_domain.valid_coordinates(*xx, tolerance=1e-13,
                                                  parameters=parameters):
                    point = dom(xx, chart=chart_domain)
                    resu += vector.at(point).plot(chart=chart,
                                                  ambient_coords=ambient_coords,
                                                  mapping=mapping, scale=scale,
                                                  color=color, print_label=False,
                                                  parameters=parameters,
                                                  **extra_options)
                # Next index:
                ret = 1
                for pos in range(ncp-1, -1, -1):
                    imax = number_values[coords[pos]] - 1
                    if ind[pos] != imax:
                        ind[pos] += ret
                        ret = 0
                    elif ret == 1:
                        if pos == 0:
                            ind[pos] = imax + 1 # end point reached
                        else:
                            ind[pos] = 0
                            ret = 1

        if label_axes:
            if nca == 2:  # 2D graphic
                # We update the dictionary _extra_kwds (options to be passed
                # to show()), instead of using the method
                # Graphics.axes_labels() since the latter is not robust w.r.t.
                # graph addition
                resu._extra_kwds['axes_labels'] = [r'$'+latex(ac)+r'$'
                                                   for ac in ambient_coords]
            else: # 3D graphic
                labels = [str(ac) for ac in ambient_coords]
                resu = set_axes_labels(resu, *labels)
        return resu
Exemple #49
0
    def plot(self, color='sign'):
        """
        Return a plot of ``self``.

        INPUT:

        - ``color`` -- can be any of the following:

          * ``4`` - use 4 colors: black, red, blue, and green with each
            corresponding to up, right, down, and left respectively
          * ``2`` - use 2 colors: red for horizontal, blue for vertical arrows
          * ``'sign'`` - use red for right and down arrows, blue for left
            and up arrows
          * a list of 4 colors for each direction
          * a function which takes a direction and a boolean corresponding
            to the sign

        EXAMPLES::

            sage: M = SixVertexModel(2, boundary_conditions='ice')
            sage: print(M[0].plot().description())
            Arrow from (-1.0,0.0) to (0.0,0.0)
            Arrow from (-1.0,1.0) to (0.0,1.0)
            Arrow from (0.0,0.0) to (0.0,-1.0)
            Arrow from (0.0,0.0) to (1.0,0.0)
            Arrow from (0.0,1.0) to (0.0,0.0)
            Arrow from (0.0,1.0) to (0.0,2.0)
            Arrow from (1.0,0.0) to (1.0,-1.0)
            Arrow from (1.0,0.0) to (1.0,1.0)
            Arrow from (1.0,1.0) to (0.0,1.0)
            Arrow from (1.0,1.0) to (1.0,2.0)
            Arrow from (2.0,0.0) to (1.0,0.0)
            Arrow from (2.0,1.0) to (1.0,1.0)
        """
        from sage.plot.graphics import Graphics
        from sage.plot.circle import circle
        from sage.plot.arrow import arrow

        if color == 4:
            color_list = ['black', 'red', 'blue', 'green']
            cfunc = lambda d,pm: color_list[d]
        elif color == 2:
            cfunc = lambda d,pm: 'red' if d % 2 == 0 else 'blue'
        elif color == 1 or color is None:
            cfunc = lambda d,pm: 'black'
        elif color == 'sign':
            cfunc = lambda d,pm: 'red' if pm else 'blue' # RD are True
        elif isinstance(color, (list, tuple)):
            cfunc = lambda d,pm: color[d]
        else:
            cfunc = color

        G = Graphics()
        for j,row in enumerate(reversed(self)):
            for i,entry in enumerate(row):
                if entry == 0: # LR
                    G += arrow((i,j+1), (i,j), color=cfunc(2, True))
                    G += arrow((i,j), (i+1,j), color=cfunc(1, True))
                    if j == 0:
                        G += arrow((i,j-1), (i,j), color=cfunc(0, False))
                    if i == 0:
                        G += arrow((i,j), (i-1,j), color=cfunc(3, False))
                elif entry == 1: # LU
                    G += arrow((i,j), (i,j+1), color=cfunc(0, False))
                    G += arrow((i+1,j), (i,j), color=cfunc(3, False))
                    if j == 0:
                        G += arrow((i,j-1), (i,j), color=cfunc(0, False))
                    if i == 0:
                        G += arrow((i,j), (i-1,j), color=cfunc(3, False))
                elif entry == 2: # LD
                    G += arrow((i,j+1), (i,j), color=cfunc(2, True))
                    G += arrow((i+1,j), (i,j), color=cfunc(3, False))
                    if j == 0:
                        G += arrow((i,j), (i,j-1), color=cfunc(2, True))
                    if i == 0:
                        G += arrow((i,j), (i-1,j), color=cfunc(3, False))
                elif entry == 3: # UD
                    G += arrow((i,j), (i,j+1), color=cfunc(0, False))
                    G += arrow((i+1,j), (i,j), color=cfunc(3, False))
                    if j == 0:
                        G += arrow((i,j), (i,j-1), color=cfunc(2, True))
                    if i == 0:
                        G += arrow((i-1,j), (i,j), color=cfunc(1, True))
                elif entry == 4: # UR
                    G += arrow((i,j), (i,j+1), color=cfunc(0, False))
                    G += arrow((i,j), (i+1,j), color=cfunc(1, True))
                    if j == 0:
                        G += arrow((i,j-1), (i,j), color=cfunc(0, False))
                    if i == 0:
                        G += arrow((i-1,j), (i,j), color=cfunc(1, True))
                elif entry == 5: # RD
                    G += arrow((i,j+1), (i,j), color=cfunc(2, True))
                    G += arrow((i,j), (i+1,j), color=cfunc(1, True))
                    if j == 0:
                        G += arrow((i,j), (i,j-1), color=cfunc(2, True))
                    if i == 0:
                        G += arrow((i-1,j), (i,j), color=cfunc(1, True))
        G.axes(False)
        return G
Exemple #50
0
def plot(hyperplane_arrangement, **kwds):
    r"""
    Return a plot of the hyperplane arrangement.  

    If the arrangement is in 4 dimensions but inessential, a plot of
    the essentialization is returned.

    .. NOTE::

        This function is available as the
        :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.plot`
        method of hyperplane arrangements. You should not call this
        function directly, only through the method.

    INPUT:

    - ``hyperplane_arrangement`` -- the hyperplane arrangement to plot

    - ``**kwds`` -- plot options: see
      :mod:`sage.geometry.hyperplane_arrangement.plot`.

    OUTPUT:
    
    A graphics object of the plot.

    EXAMPLES::

        sage: B = hyperplane_arrangements.semiorder(4)
        sage: B.plot()
        Displaying the essentialization.
        Graphics3d Object
    """
    N = len(hyperplane_arrangement)
    dim = hyperplane_arrangement.dimension()
    if hyperplane_arrangement.base_ring().characteristic() != 0:
        raise NotImplementedError('must be a field of characteristic 0')
    elif dim == 4:
        if not hyperplane_arrangement.is_essential():
            print('Displaying the essentialization.')
            hyperplane_arrangement = hyperplane_arrangement.essentialization()
    elif dim not in [1,2,3]: # revise to handle 4d
        return # silently
    # handle extra keywords
    if 'hyperplane_colors' in kwds:
        hyp_colors = kwds.pop('hyperplane_colors')
        if not isinstance(hyp_colors, list): # we assume its a single color then
            hyp_colors = [hyp_colors] * N
    else:
        HSV_tuples = [(i*1.0/N, 0.8, 0.9) for i in range(N)]
        hyp_colors = [hsv_to_rgb(*x) for x in HSV_tuples]
    if 'hyperplane_labels' in kwds:
        hyp_labels = kwds.pop('hyperplane_labels')
        has_hyp_label = True
        if not isinstance(hyp_labels, list): # we assume its a boolean then
            hyp_labels = [hyp_labels] * N
        relabeled = []
        for i in range(N):
            if hyp_labels[i] in [True,'long']:
                relabeled.append(True)
            else:
                relabeled.append(str(i))
        hyp_labels = relabeled
    else:
        has_hyp_label = False
    if 'label_colors' in kwds:
        label_colors = kwds.pop('label_colors')
        has_label_color = True
        if not isinstance(label_colors, list): # we assume its a single color then
            label_colors = [label_colors] * N
    else:
        has_label_color = False
    if 'label_fontsize' in kwds:
        label_fontsize = kwds.pop('label_fontsize')
        has_label_fontsize = True
        if not isinstance(label_fontsize, list): # we assume its a single size then
            label_fontsize = [label_fontsize] * N
    else:
        has_label_fontsize = False
    if 'label_offsets' in kwds:
        has_offsets = True
        offsets = kwds.pop('label_offsets')
    else:
        has_offsets = False # give default values below
    hyperplane_legend = kwds.pop('hyperplane_legend', 'long' if dim < 3 else False)
    if 'hyperplane_opacities' in kwds:
        hyperplane_opacities = kwds.pop('hyperplane_opacities')
        has_opacity = True
        if not isinstance(hyperplane_opacities, list): # we assume a single number then
            hyperplane_opacities = [hyperplane_opacities] * N
    else:
        has_opacity = False
    point_sizes = kwds.pop('point_sizes', 50)
    if not isinstance(point_sizes, list):
        point_sizes = [point_sizes] * N
    if 'ranges' in kwds:
        ranges_set = True
        ranges = kwds.pop('ranges')
        if not type(ranges) in [list,tuple]: # ranges is a single number
            ranges = [ranges] * N
        # So ranges is some type of list.
        elif dim == 2: # arrangement of lines in the plane
            if not type(ranges[0]) in [list,tuple]: # a single interval
                ranges = [ranges] * N
        elif dim == 3: # arrangement of planes in 3-space
            if not type(ranges[0][0]) in [list,tuple]:
                ranges = [ranges] * N
        elif dim not in [2,3]: # ranges is not an option unless dim is 2 or 3
            ranges_set = False
        else: # a list of intervals, one for each hyperplane is given
            pass # ranges does not need to be modified
    else:
        ranges_set = False # give default values below
    # the extra keywords have now been handled
    # now handle the legend
    if dim in [1,2]: # points on a line or lines in the plane
        if hyperplane_legend in [True,'long']:
            hyps = hyperplane_arrangement.hyperplanes()
            legend_labels = [hyps[i]._latex_() for i in range(N)]
        elif hyperplane_legend == 'short' :
            legend_labels = [str(i) for i in range(N)]
    else: # dim==3,  arrangement of planes in 3-space
        if hyperplane_legend in [True, 'long']:
            legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'long')
        elif hyperplane_legend == 'short':
            legend3d = legend_3d(hyperplane_arrangement, hyp_colors, 'short')
    ## done handling the legend
    ## now create the plot
    p = Graphics()
    for i in range(N):
        newk = copy(kwds)
        if has_hyp_label:
            newk['hyperplane_label'] = hyp_labels[i]
            if has_offsets:
                if not isinstance(offsets, list):
                    newk['label_offset'] = offsets
                else:
                    newk['label_offset'] = offsets[i]
        else:
            newk['hyperplane_label'] = False
        if has_label_color:
            newk['label_color'] = label_colors[i]
        if has_label_fontsize:
            newk['label_fontsize'] = label_fontsize[i]
        if has_opacity:
            newk['opacity'] = hyperplane_opacities[i]
        if dim == 1:
            newk['point_size'] = point_sizes[i]
        if dim in [1,2] and hyperplane_legend: # more options than T/F
            newk['legend_label'] = legend_labels[i]
        if ranges_set:
            newk['ranges'] = ranges[i]
        p += plot_hyperplane(hyperplane_arrangement[i], rgbcolor=hyp_colors[i], **newk)
    if dim == 1:
        if hyperplane_legend: # there are more options than T/F
            p.legend(True)
        return p
    elif dim == 2:
        if hyperplane_legend: # there are more options than T/F
            p.legend(True)
        return p
    else: # dim==3
        if hyperplane_legend: # there are more options than T/F
            return p, legend3d
        else:
            return p