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: 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.]))