Search code examples
matlabplotgraphmatlab-figure

How to display dark edges over lighter ones in Matlab plots?


I have the following Matlab plot representing a graph. I would like to display the darker on top of the lighter ones in such a way the lighter edges don't modify the darker when crossing them. How could I do?

Edit: the Matlab code for reproduce the example is the following

plot(G, 'XData', Xcoords, 'YData', Ycoords,'NodeLabel',{}, 'MarkerSize', 7,...
 'Linewidth',1.6, 'EdgeCData', G.Edges.Weight)
  colormap(flipud(gray(40)));
  colorbar('southoutside');
  caxis([min(G.Edges.Weight) max(G.Edges.Weight)])
 axis off

where the weights of the edges are encoded in G.Edges.Weight

To reproduce the effect (with a smaller graph), you can try with the following code:

A= zeros(4,4);
A(1,[2 3 4])=1;
A(2,4)=0.04;
A(2,[1 3])=1;
A(3,[2 1 4])=1; 
A(4,2)=0.04;
A(4,[3 1])=1;

Xcoords=[1 2 2 1]';
Ycoords= [1 1 2 2 ]';

G= graph(A);% base toolbox

figure()
plot(G, 'XData', Xcoords, 'YData', Ycoords, 'NodeLabel',{}, 'MarkerSize', 7,...
    'LineWidth', 3.8, 'EdgeCdata', G.Edges.Weight)
colormap(flipud(gray(40)));
colorbar('southoutside'); caxis([0 1]);
axis off

It seems that is the ordering of the edges that decide who is on top. For instance, if the weight 0.04 is assigned to the other crossing edge (A(1,3)=A(3,1)) the effect is not visible since the edge A(2,4)=A(4,2) cames after. Graph


Solution

  • The order of the edge table in MATLAB's graph class seems pretty tightly dependent on position in the graph's adjacency matrix, which is inherently impossible to contrive in a way that guarantees some arbitrary edge order. So I think you only have two options:

    1. Write your own graph plotting routine; then you can control the plotting order however you like because it's your own software design.
    2. Manipulate MATLAB's graph plotting output using the undocumented primitives it creates.

    The second option is possible by noting that the plotted GraphPlot object has a LineStrip object in its NodeChildren which is responsible for drawing all the relevant edges. Because you're using a grayscale color map, the RGB data in this object is all you need to figure out how its vertices need to be ordered to get the right plot order.

    First, store the plotted result in P and set EdgeAlpha to 1 so the graph is plotted

    in such a way the lighter edges don't modify the darker when crossing them

    P = plot(G, 'XData', Xcoords, 'YData', Ycoords, 'NodeLabel',{}, 'MarkerSize', 7,...
        'LineWidth', 3.8, 'EdgeCdata', G.Edges.Weight, 'EdgeAlpha',1);
    colormap(flipud(gray(40)));
    colorbar('southoutside'); caxis([0 1]);
    axis off
    

    Then find the LineStrip created in the drawing process:

    drawnow
    s = P.NodeChildren(arrayfun(@(o) isa(o,'matlab.graphics.primitive.world.LineStrip'), P.NodeChildren));
    

    The new order of the vertices in s can then be determined from its ColorData, which must then be applied to both the ColorData and VertexData properties to reorder the edges without anything else changing:

    [~,idx] = sortrows(s.ColorData','desc');
    set(s, 'VertexData',s.VertexData(:,idx),  'ColorData',s.ColorData(:,idx));
    

    This will be liable to be overridden by any further redrawing that takes place and being undocumented functionality comes with no guarantees as to how it will behave – but superficially it seems to do what you're looking for.