如何在Matlab绘图中显示深色边缘覆盖浅色边缘?

3
我有一个代表图形的Matlab绘图。我想以某种方式显示较暗的线条在较浅的线条之上,以使较浅的线条在穿越它们时不改变较暗的线条。我该怎么做?
编辑:用于复制示例的Matlab代码如下:
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

在G.Edges.Weight中编码边的权重,其中G是指代图。为了模拟这种效果(使用更小的图),可以尝试以下代码:

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

似乎边的排序决定了谁在图形的最上面。例如,如果将重量0.04分配给其他交叉的边(A(1,3)=A(3,1)),则不会看到效果,因为边A(2,4)=A(4,2)出现在其后面。 Graph

请展示一下您生成图表的样例。如果没有可重现的代码,很难帮助您。总的来说,先画浅色边缘,这样深色的边缘就可以覆盖它们。 - Mad Physicist
你能展示一些创建 G 的示例代码吗? - Mad Physicist
很遗憾,图是通过算法学习的。我有已经学习好的邻接矩阵和拉普拉斯矩阵。你知道如何在这里加载整个图吗? - VanBaffo
你不需要在这里加载整个东西,只需提供一个包含固定数据的小样本以重现问题。你只需要展示一些相交的边缘即可。 - Mad Physicist
修改后的问题。似乎是边的顺序决定了谁在上面。例如,如果将权重0.04分配给另一个交叉边(A(1,3)= A(3,1)),则效果不可见,因为边A(2,4)= A(4,2)在其后。如果查看G.Edges.Weight属性,可以看到边缘权重有一个固定的排序,因此也在绘图中。 - VanBaffo
显示剩余2条评论
1个回答

3
MATLAB的graph类中边表的顺序似乎与其在邻接矩阵中的位置紧密相关,但这在本质上是无法以一种保证任意边顺序的方式来设计的。因此,我认为您只有两个选择:
  1. 编写自己的图形绘制程序;然后,由于它是您自己的软件设计,因此可以控制绘制顺序。
  2. 使用MATLAB创建的未公开基元操纵MATLAB的图形绘制输出。
通过注意到绘制的GraphPlot对象在其NodeChildren中具有LineStrip对象,该对象负责绘制所有相关边缘,可以实现第二个选项。由于使用灰度颜色映射,因此此对象中的RGB数据是您需要确定其顶点需要按哪种顺序排序以获取正确绘图顺序的全部内容。
首先,将绘制结果存储在P中,并将EdgeAlpha设置为1,以便以一种使较浅的边缘在穿越时不修改较暗的边缘的方式进行绘制。
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

然后找到在绘制过程中创建的LineStrip:

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

接下来可以从 sColorData 中确定顶点的新顺序,然后必须将其应用于 ColorDataVertexData 属性,以重新排序边缘而不改变任何其他内容:

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

这可能会被任何进一步的重绘覆盖,并且作为未经记录的功能,无法保证其行为 - 但表面上它似乎做到了你想要的。

感谢您的回复。根据我所看到的,唯一的变化是在一个交叉的示例图中,现在白边在黑边之上。这是由于EdgeAlpha属性设置为1。其他代码行似乎没有改变属性。我尝试了检查LineStrip对象但它是空的,而且从命令行中也没有产生任何输出。此外,当输入P.时,自动编译不显示NodeChildren,而是显示Childen。然而,按照您所说的做法,它没有抛出任何错误,但并未显示所需的结果。您知道更多吗? - VanBaffo
1
我误解了你问题中的一行,认为你想要在顶部使用浅色。根据我的编辑答案,反转排序顺序会使其反转。NodeChildren是图形对象的隐藏属性,而LineStrip没有公共属性,因此您对变量检查的结果是正确的。您需要直接查询隐藏属性才能看到它们,并使用像metaclass这样的方法来发现它们。 - Will
我尝试了,但是对于4个节点的简单图形效果没有改变...你知道什么原因吗? - VanBaffo
1
@Alberto,你是否一次性运行了以上的代码?我已经意识到NodeChildren属性中的对象依赖于图表绘制完成才能进行初始化。我之前是从命令行分别运行每个阶段的,但如果一次性运行整个程序,你需要在访问NodeChildren中的原始数据之前先调用drawnow函数。如果这不解决问题,那么你就需要探索一下在R2017b中事物是如何工作的了。这就是依赖于未记录特性的缺陷所在 - 无法保证在不同版本间程序会如何运行。 - Will
1
@Alberto 是的,这就是我在答案最后一段担心的脆弱性。GraphPlot 对象有一个隐藏事件 MarkedClean,我认为每次重绘基元时都会触发它。因此,您可以使用 addlistener 让重新排序边缘顶点的代码每次触发此事件时运行。我不确定这是否会干扰缩放过程,但值得一试。 - Will
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接