def plot_generators(self): r""" Plot ray generators. Ray generators must be specified during construction or using :meth:`set_rays` before calling this method. OUTPUT: - a plot. EXAMPLES:: sage: from sage.geometry.toric_plotter import ToricPlotter sage: tp = ToricPlotter(dict(), 2, [(3,4)]) sage: print tp.plot_generators() Graphics object consisting of 1 graphics primitive """ generators = self.generators result = Graphics() if not generators or not self.show_generators: return result colors = color_list(self.generator_color, len(generators)) d = self.dimension extra_options = self.extra_options origin = self.origin thickness = self.generator_thickness zorder = self.generator_zorder for generator, ray, color in zip(generators, self.rays, colors): if ray.norm() < generator.norm(): result += line([origin, ray], color=color, thickness=thickness, zorder=zorder, **extra_options) else: # This should not be the case, but as of 4.6 plotting # functions are inconsistent and arrows behave very # different compared to lines. if d <= 2: result += arrow( origin, generator, color=color, width=thickness, arrowsize=thickness + 1, zorder=zorder, **extra_options ) else: result += line( [origin, generator], arrow_head=True, color=color, thickness=thickness, zorder=zorder, **extra_options ) return result
def render_outline_2d(self, **kwds): """ Return the outline (edges) of a polyhedron in 2d. EXAMPLES:: sage: penta = polytopes.regular_polygon(5) sage: outline = penta.projection().render_outline_2d() sage: outline._objects[0] Line defined by 2 points """ wireframe = []; for l in self.lines: l_coords = self.coordinates_of(l) wireframe.append( line2d(l_coords, **kwds) ) for a in self.arrows: a_coords = self.coordinates_of(a) wireframe.append( arrow(a_coords[0], a_coords[1], **kwds) ) return sum(wireframe)
def set_edges(self, **edge_options): """ Sets the edge (or arrow) plotting parameters for the GraphPlot object. This function is called by the constructor but can also be called to make updates to the vertex options of an existing GraphPlot object. Note that the changes are cumulative. EXAMPLES:: sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() sage: GP.set_edges(edge_color='black') sage: GP.plot() sage: d = DiGraph({}, loops=True, multiedges=True, sparse=True) sage: d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() sage: GP.set_edges(edge_color='black') sage: GP.plot() TESTS:: sage: G = Graph("Fooba") sage: G.show(edge_colors={'red':[(3,6),(2,5)]}) Verify that default edge labels are pretty close to being between the vertices in some cases where they weren't due to truncating division (trac #10124):: sage: test_graphs = graphs.FruchtGraph(), graphs.BullGraph() sage: tol = 0.001 sage: for G in test_graphs: ... E=G.edges() ... for e0, e1, elab in E: ... G.set_edge_label(e0, e1, '%d %d' % (e0, e1)) ... gp = G.graphplot(save_pos=True,edge_labels=True) ... vx = gp._plot_components['vertices'][0].xdata ... vy = gp._plot_components['vertices'][0].ydata ... for elab in gp._plot_components['edge_labels']: ... textobj = elab[0] ... x, y, s = textobj.x, textobj.y, textobj.string ... v0, v1 = map(int, s.split()) ... vn = vector(((x-(vx[v0]+vx[v1])/2.),y-(vy[v0]+vy[v1])/2.)).norm() ... assert vn < tol """ for arg in edge_options: self._options[arg] = edge_options[arg] if 'edge_colors' in edge_options: self._options['color_by_label'] = False # Handle base edge options: thickness, linestyle eoptions={} if 'edge_style' in self._options: eoptions['linestyle'] = self._options['edge_style'] if 'thickness' in self._options: eoptions['thickness'] = self._options['thickness'] # Set labels param to add labels on the fly labels = False if self._options['edge_labels']: labels = True self._plot_components['edge_labels'] = [] # Make dict collection of all edges (keep label and edge color) edges_to_draw = {} if self._options['color_by_label'] or isinstance(self._options['edge_colors'], dict): if self._options['color_by_label']: edge_colors = self._graph._color_by_label() else: edge_colors = self._options['edge_colors'] for color in edge_colors: for edge in edge_colors[color]: key = tuple(sorted([edge[0],edge[1]])) if key == (edge[0],edge[1]): head = 1 else: head = 0 if len(edge) < 3: label = self._graph.edge_label(edge[0],edge[1]) if isinstance(label, list): if key in edges_to_draw: edges_to_draw[key].append((label[-1], color, head)) else: edges_to_draw[key] = [(label[-1], color, head)] for i in range(len(label)-1): edges_to_draw[key].append((label[-1], color, head)) else: label = edge[2] if key in edges_to_draw: edges_to_draw[key].append((label, color, head)) else: edges_to_draw[key] = [(label, color, head)] # add unspecified edges in (default color black) for edge in self._graph.edge_iterator(): key = tuple(sorted([edge[0],edge[1]])) label = edge[2] specified = False if key in edges_to_draw: for old_label, old_color, old_head in edges_to_draw[key]: if label == old_label: specified = True break if not specified: if key == (edge[0],edge[1]): head = 1 else: head = 0 edges_to_draw[key] = [(label, 'black', head)] else: for edge in self._graph.edges(sort=True): key = tuple(sorted([edge[0],edge[1]])) if key == (edge[0],edge[1]): head = 1 else: head = 0 if key in edges_to_draw: edges_to_draw[key].append((edge[2], self._options['edge_color'], head)) else: edges_to_draw[key] = [(edge[2], self._options['edge_color'], head)] if edges_to_draw: self._plot_components['edges'] = [] else: return # Check for multi-edges or loops if self._arcs or self._loops: tmp = edges_to_draw.copy() dist = self._options['dist']*2. loop_size = self._options['loop_size'] max_dist = self._options['max_dist'] from sage.functions.all import sqrt for (a,b) in tmp: if a == b: # Loops distance = dist local_labels = edges_to_draw.pop((a,b)) if len(local_labels)*dist > max_dist: distance = float(max_dist)/len(local_labels) curr_loop_size = loop_size for i in range(len(local_labels)): self._plot_components['edges'].append(circle((self._pos[a][0],self._pos[a][1]-curr_loop_size), curr_loop_size, rgbcolor=local_labels[i][1], **eoptions)) if labels: self._plot_components['edge_labels'].append(text(local_labels[i][0], (self._pos[a][0], self._pos[a][1]-2*curr_loop_size))) curr_loop_size += distance/4 elif len(edges_to_draw[(a,b)]) > 1: # Multi-edge local_labels = edges_to_draw.pop((a,b)) # Compute perpendicular bisector p1 = self._pos[a] p2 = self._pos[b] M = ((p1[0]+p2[0])/2., (p1[1]+p2[1])/2.) # midpoint if not p1[1] == p2[1]: S = float(p1[0]-p2[0])/(p2[1]-p1[1]) # perp slope y = lambda x : S*x-S*M[0]+M[1] # perp bisector line # f,g are functions of distance d to determine x values # on line y at d from point M f = lambda d : sqrt(d**2/(1.+S**2)) + M[0] g = lambda d : -sqrt(d**2/(1.+S**2)) + M[0] odd_x = f even_x = g if p1[0] == p2[0]: odd_y = lambda d : M[1] even_y = odd_y else: odd_y = lambda x : y(f(x)) even_y = lambda x : y(g(x)) else: odd_x = lambda d : M[0] even_x = odd_x odd_y = lambda d : M[1] + d even_y = lambda d : M[1] - d # We now have the control points for each bezier curve # in terms of distance parameter d. # Also note that the label for each edge should be drawn at d/2. # (This is because we're using the perp bisectors). distance = dist if len(local_labels)*dist > max_dist: distance = float(max_dist)/len(local_labels) for i in range(len(local_labels)/2): k = (i+1.0)*distance if self._arcdigraph: odd_start = self._polar_hack_for_multidigraph(p1, [odd_x(k),odd_y(k)], self._vertex_radius)[0] odd_end = self._polar_hack_for_multidigraph([odd_x(k),odd_y(k)], p2, self._vertex_radius)[1] even_start = self._polar_hack_for_multidigraph(p1, [even_x(k),even_y(k)], self._vertex_radius)[0] even_end = self._polar_hack_for_multidigraph([even_x(k),even_y(k)], p2, self._vertex_radius)[1] self._plot_components['edges'].append(arrow(path=[[odd_start,[odd_x(k),odd_y(k)],odd_end]], head=local_labels[2*i][2], zorder=1, rgbcolor=local_labels[2*i][1], **eoptions)) self._plot_components['edges'].append(arrow(path=[[even_start,[even_x(k),even_y(k)],even_end]], head=local_labels[2*i+1][2], zorder=1, rgbcolor=local_labels[2*i+1][1], **eoptions)) else: self._plot_components['edges'].append(bezier_path([[p1,[odd_x(k),odd_y(k)],p2]],zorder=1, rgbcolor=local_labels[2*i][1], **eoptions)) self._plot_components['edges'].append(bezier_path([[p1,[even_x(k),even_y(k)],p2]],zorder=1, rgbcolor=local_labels[2*i+1][1], **eoptions)) if labels: j = k/2.0 self._plot_components['edge_labels'].append(text(local_labels[2*i][0],[odd_x(j),odd_y(j)])) self._plot_components['edge_labels'].append(text(local_labels[2*i+1][0],[even_x(j),even_y(j)])) if len(local_labels)%2 == 1: edges_to_draw[(a,b)] = [local_labels[-1]] # draw line for last odd dir = self._graph.is_directed() for (a,b) in edges_to_draw: if self._arcdigraph: C,D = self._polar_hack_for_multidigraph(self._pos[a], self._pos[b], self._vertex_radius) self._plot_components['edges'].append(arrow(C,D, rgbcolor=edges_to_draw[(a,b)][0][1], head=edges_to_draw[(a,b)][0][2], **eoptions)) if labels: self._plot_components['edge_labels'].append(text(str(edges_to_draw[(a,b)][0][0]),[(C[0]+D[0])/2., (C[1]+D[1])/2.])) elif dir: self._plot_components['edges'].append(arrow(self._pos[a],self._pos[b], rgbcolor=edges_to_draw[(a,b)][0][1], arrowshorten=self._arrowshorten, head=edges_to_draw[(a,b)][0][2], **eoptions)) else: self._plot_components['edges'].append(line([self._pos[a],self._pos[b]], rgbcolor=edges_to_draw[(a,b)][0][1], **eoptions)) if labels and not self._arcdigraph: self._plot_components['edge_labels'].append(text(str(edges_to_draw[(a,b)][0][0]),[(self._pos[a][0]+self._pos[b][0])/2., (self._pos[a][1]+self._pos[b][1])/2.]))
def set_edges(self, **edge_options): """ Sets the edge (or arrow) plotting parameters for the ``GraphPlot`` object. This function is called by the constructor but can also be called to make updates to the vertex options of an existing ``GraphPlot`` object. Note that the changes are cumulative. EXAMPLES:: sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) sage: GP = g.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() sage: GP.set_edges(edge_color='black') sage: GP.plot() sage: d = DiGraph({}, loops=True, multiedges=True, sparse=True) sage: d.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ... (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) sage: GP = d.graphplot(vertex_size=100, edge_labels=True, color_by_label=True, edge_style='dashed') sage: GP.set_edges(edge_style='solid') sage: GP.plot() sage: GP.set_edges(edge_color='black') sage: GP.plot() TESTS:: sage: G = Graph("Fooba") sage: G.show(edge_colors={'red':[(3,6),(2,5)]}) Verify that default edge labels are pretty close to being between the vertices in some cases where they weren't due to truncating division (:trac:`10124`):: sage: test_graphs = graphs.FruchtGraph(), graphs.BullGraph() sage: tol = 0.001 sage: for G in test_graphs: ... E=G.edges() ... for e0, e1, elab in E: ... G.set_edge_label(e0, e1, '%d %d' % (e0, e1)) ... gp = G.graphplot(save_pos=True,edge_labels=True) ... vx = gp._plot_components['vertices'][0].xdata ... vy = gp._plot_components['vertices'][0].ydata ... for elab in gp._plot_components['edge_labels']: ... textobj = elab[0] ... x, y, s = textobj.x, textobj.y, textobj.string ... v0, v1 = map(int, s.split()) ... vn = vector(((x-(vx[v0]+vx[v1])/2.),y-(vy[v0]+vy[v1])/2.)).norm() ... assert vn < tol """ for arg in edge_options: self._options[arg] = edge_options[arg] if 'edge_colors' in edge_options: self._options['color_by_label'] = False # Handle base edge options: thickness, linestyle eoptions = {} if 'edge_style' in self._options: from sage.plot.misc import get_matplotlib_linestyle eoptions['linestyle'] = get_matplotlib_linestyle( self._options['edge_style'], return_type='long') if 'thickness' in self._options: eoptions['thickness'] = self._options['thickness'] # Set labels param to add labels on the fly labels = False if self._options['edge_labels']: labels = True self._plot_components['edge_labels'] = [] # Make dict collection of all edges (keep label and edge color) edges_to_draw = {} if self._options['color_by_label'] or isinstance( self._options['edge_colors'], dict): if self._options['color_by_label']: edge_colors = self._graph._color_by_label( format=self._options['color_by_label']) else: edge_colors = self._options['edge_colors'] for color in edge_colors: for edge in edge_colors[color]: key = tuple(sorted([edge[0], edge[1]])) if key == (edge[0], edge[1]): head = 1 else: head = 0 if len(edge) < 3: label = self._graph.edge_label(edge[0], edge[1]) if isinstance(label, list): if key in edges_to_draw: edges_to_draw[key].append( (label[-1], color, head)) else: edges_to_draw[key] = [(label[-1], color, head)] for i in range(len(label) - 1): edges_to_draw[key].append( (label[-1], color, head)) else: label = edge[2] if key in edges_to_draw: edges_to_draw[key].append((label, color, head)) else: edges_to_draw[key] = [(label, color, head)] # add unspecified edges in (default color black) for edge in self._graph.edge_iterator(): key = tuple(sorted([edge[0], edge[1]])) label = edge[2] specified = False if key in edges_to_draw: for old_label, old_color, old_head in edges_to_draw[key]: if label == old_label: specified = True break if not specified: if key == (edge[0], edge[1]): head = 1 else: head = 0 edges_to_draw[key] = [(label, 'black', head)] else: for edge in self._graph.edges(sort=True): key = tuple(sorted([edge[0], edge[1]])) if key == (edge[0], edge[1]): head = 1 else: head = 0 if key in edges_to_draw: edges_to_draw[key].append( (edge[2], self._options['edge_color'], head)) else: edges_to_draw[key] = [(edge[2], self._options['edge_color'], head)] if edges_to_draw: self._plot_components['edges'] = [] else: return # Check for multi-edges or loops if self._arcs or self._loops: tmp = edges_to_draw.copy() dist = self._options['dist'] * 2. loop_size = self._options['loop_size'] max_dist = self._options['max_dist'] from sage.functions.all import sqrt for (a, b) in tmp: if a == b: # Loops distance = dist local_labels = edges_to_draw.pop((a, b)) if len(local_labels) * dist > max_dist: distance = float(max_dist) / len(local_labels) curr_loop_size = loop_size for i in range(len(local_labels)): self._plot_components['edges'].append( circle((self._pos[a][0], self._pos[a][1] - curr_loop_size), curr_loop_size, rgbcolor=local_labels[i][1], **eoptions)) if labels: self._plot_components['edge_labels'].append( text(local_labels[i][0], (self._pos[a][0], self._pos[a][1] - 2 * curr_loop_size))) curr_loop_size += distance / 4 elif len(edges_to_draw[(a, b)]) > 1: # Multi-edge local_labels = edges_to_draw.pop((a, b)) # Compute perpendicular bisector p1 = self._pos[a] p2 = self._pos[b] M = ( (p1[0] + p2[0]) / 2., (p1[1] + p2[1]) / 2.) # midpoint if not p1[1] == p2[1]: S = float(p1[0] - p2[0]) / (p2[1] - p1[1] ) # perp slope y = lambda x: S * x - S * M[0] + M[ 1] # perp bisector line # f,g are functions of distance d to determine x values # on line y at d from point M f = lambda d: sqrt(d**2 / (1. + S**2)) + M[0] g = lambda d: -sqrt(d**2 / (1. + S**2)) + M[0] odd_x = f even_x = g if p1[0] == p2[0]: odd_y = lambda d: M[1] even_y = odd_y else: odd_y = lambda x: y(f(x)) even_y = lambda x: y(g(x)) else: odd_x = lambda d: M[0] even_x = odd_x odd_y = lambda d: M[1] + d even_y = lambda d: M[1] - d # We now have the control points for each bezier curve # in terms of distance parameter d. # Also note that the label for each edge should be drawn at d/2. # (This is because we're using the perp bisectors). distance = dist if len(local_labels) * dist > max_dist: distance = float(max_dist) / len(local_labels) for i in range(len(local_labels) / 2): k = (i + 1.0) * distance if self._arcdigraph: odd_start = self._polar_hack_for_multidigraph( p1, [odd_x(k), odd_y(k)], self._vertex_radius)[0] odd_end = self._polar_hack_for_multidigraph( [odd_x(k), odd_y(k)], p2, self._vertex_radius)[1] even_start = self._polar_hack_for_multidigraph( p1, [even_x(k), even_y(k)], self._vertex_radius)[0] even_end = self._polar_hack_for_multidigraph( [even_x(k), even_y(k)], p2, self._vertex_radius)[1] self._plot_components['edges'].append( arrow(path=[[ odd_start, [odd_x(k), odd_y(k)], odd_end ]], head=local_labels[2 * i][2], zorder=1, rgbcolor=local_labels[2 * i][1], **eoptions)) self._plot_components['edges'].append( arrow(path=[[ even_start, [even_x(k), even_y(k)], even_end ]], head=local_labels[2 * i + 1][2], zorder=1, rgbcolor=local_labels[2 * i + 1][1], **eoptions)) else: self._plot_components['edges'].append( bezier_path( [[p1, [odd_x(k), odd_y(k)], p2]], zorder=1, rgbcolor=local_labels[2 * i][1], **eoptions)) self._plot_components['edges'].append( bezier_path( [[p1, [even_x(k), even_y(k)], p2]], zorder=1, rgbcolor=local_labels[2 * i + 1][1], **eoptions)) if labels: j = k / 2.0 self._plot_components['edge_labels'].append( text(local_labels[2 * i][0], [odd_x(j), odd_y(j)])) self._plot_components['edge_labels'].append( text(local_labels[2 * i + 1][0], [even_x(j), even_y(j)])) if len(local_labels) % 2 == 1: edges_to_draw[(a, b)] = [local_labels[-1] ] # draw line for last odd dir = self._graph.is_directed() for (a, b) in edges_to_draw: if self._arcdigraph: C, D = self._polar_hack_for_multidigraph( self._pos[a], self._pos[b], self._vertex_radius) self._plot_components['edges'].append( arrow(C, D, rgbcolor=edges_to_draw[(a, b)][0][1], head=edges_to_draw[(a, b)][0][2], **eoptions)) if labels: self._plot_components['edge_labels'].append( text(str(edges_to_draw[(a, b)][0][0]), [(C[0] + D[0]) / 2., (C[1] + D[1]) / 2.])) elif dir: self._plot_components['edges'].append( arrow(self._pos[a], self._pos[b], rgbcolor=edges_to_draw[(a, b)][0][1], arrowshorten=self._arrowshorten, head=edges_to_draw[(a, b)][0][2], **eoptions)) else: self._plot_components['edges'].append( line([self._pos[a], self._pos[b]], rgbcolor=edges_to_draw[(a, b)][0][1], **eoptions)) if labels and not self._arcdigraph: self._plot_components['edge_labels'].append( text(str(edges_to_draw[(a, b)][0][0]), [(self._pos[a][0] + self._pos[b][0]) / 2., (self._pos[a][1] + self._pos[b][1]) / 2.]))
def plot(self, *args, **kwds): r""" Overrides Graph's plot function, to illustrate the bundle nature. EXAMPLE:: sage: P = graphs.PetersenGraph() sage: partition = [range(5), range(5,10)] sage: B = GraphBundle(P, partition) sage: #B.plot() Test disabled due to bug in GraphBundle.__init__(). See trac #8329. """ if 'pos' not in kwds.keys(): kwds['pos'] = None if kwds['pos'] is None: import sage.graphs.generic_graph_pyx as generic_graph_pyx if 'iterations' not in kwds.keys(): kwds['iterations'] = 50 iters = kwds['iterations'] total_pos = generic_graph_pyx.spring_layout_fast(self, iterations=iters) base_pos = generic_graph_pyx.spring_layout_fast(self.base, iterations=iters) for v in base_pos.iterkeys(): for v_tilde in self.fiber[v]: total_pos[v_tilde][0] = base_pos[v][0] tot_x = [p[0] for p in total_pos.values()] tot_y = [p[1] for p in total_pos.values()] bas_x = [p[0] for p in base_pos.values()] bas_y = [p[1] for p in base_pos.values()] tot_x_min = min(tot_x) tot_x_max = max(tot_x) tot_y_min = min(tot_y) tot_y_max = max(tot_y) bas_x_min = min(bas_x) bas_x_max = max(bas_x) bas_y_min = min(bas_y) bas_y_max = max(bas_y) if tot_x_max == tot_x_min and tot_y_max == tot_y_min: tot_y_max += 1 tot_y_min -= 1 elif tot_y_max == tot_y_min: delta = (tot_x_max - tot_x_min) / 2.0 tot_y_max += delta tot_y_min -= delta if bas_x_max == bas_x_min and bas_y_max == bas_y_min: bas_y_max += 1 bas_y_min -= 1 elif bas_y_max == bas_y_min: delta = (bas_x_max - bas_x_min) / 2.0 bas_y_max += delta bas_y_min -= delta y_diff = (bas_y_max - tot_y_min) + 2 * (bas_y_max - bas_y_min) pos = {} for v in self: pos[('t', v)] = [total_pos[v][0], total_pos[v][1] + y_diff] for v in self.base: pos[('b', v)] = base_pos[v] from copy import copy G = copy(self) rd = {} for v in G: rd[v] = ('t', v) G.relabel(rd) B = copy(self.base) rd = {} for v in B: rd[v] = ('b', v) B.relabel(rd) E = G.disjoint_union(B) kwds['pos'] = pos from sage.plot.all import arrow G = Graph.plot(E, *args, **kwds) G += arrow(((tot_x_max + tot_x_min) / 2.0, tot_y_min + y_diff), ((tot_x_max + tot_x_min) / 2.0, bas_y_max), axes=False) G.axes(False) return G else: return G.plot(self, *args, **kwds)
def plot(self, *args, **kwds): r""" Overrides Graph's plot function, to illustrate the bundle nature. EXAMPLE:: sage: P = graphs.PetersenGraph() sage: partition = [range(5), range(5,10)] sage: B = GraphBundle(P, partition) sage: #B.plot() Test disabled due to bug in GraphBundle.__init__(). See trac #8329. """ if "pos" not in kwds.keys(): kwds["pos"] = None if kwds["pos"] is None: import sage.graphs.generic_graph_pyx as generic_graph_pyx if "iterations" not in kwds.keys(): kwds["iterations"] = 50 iters = kwds["iterations"] total_pos = generic_graph_pyx.spring_layout_fast(self, iterations=iters) base_pos = generic_graph_pyx.spring_layout_fast(self.base, iterations=iters) for v in base_pos.iterkeys(): for v_tilde in self.fiber[v]: total_pos[v_tilde][0] = base_pos[v][0] tot_x = [p[0] for p in total_pos.values()] tot_y = [p[1] for p in total_pos.values()] bas_x = [p[0] for p in base_pos.values()] bas_y = [p[1] for p in base_pos.values()] tot_x_min = min(tot_x) tot_x_max = max(tot_x) tot_y_min = min(tot_y) tot_y_max = max(tot_y) bas_x_min = min(bas_x) bas_x_max = max(bas_x) bas_y_min = min(bas_y) bas_y_max = max(bas_y) if tot_x_max == tot_x_min and tot_y_max == tot_y_min: tot_y_max += 1 tot_y_min -= 1 elif tot_y_max == tot_y_min: delta = (tot_x_max - tot_x_min) / 2.0 tot_y_max += delta tot_y_min -= delta if bas_x_max == bas_x_min and bas_y_max == bas_y_min: bas_y_max += 1 bas_y_min -= 1 elif bas_y_max == bas_y_min: delta = (bas_x_max - bas_x_min) / 2.0 bas_y_max += delta bas_y_min -= delta y_diff = (bas_y_max - tot_y_min) + 2 * (bas_y_max - bas_y_min) pos = {} for v in self: pos[("t", v)] = [total_pos[v][0], total_pos[v][1] + y_diff] for v in self.base: pos[("b", v)] = base_pos[v] from copy import copy G = copy(self) rd = {} for v in G: rd[v] = ("t", v) G.relabel(rd) B = copy(self.base) rd = {} for v in B: rd[v] = ("b", v) B.relabel(rd) E = G.disjoint_union(B) kwds["pos"] = pos from sage.plot.all import arrow G = Graph.plot(E, *args, **kwds) G += arrow( ((tot_x_max + tot_x_min) / 2.0, tot_y_min + y_diff), ((tot_x_max + tot_x_min) / 2.0, bas_y_max), axes=False, ) G.axes(False) return G else: return G.plot(self, *args, **kwds)