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
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
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(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(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
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
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
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
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
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
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() # not tested """ # 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): # 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
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