MATLAB | Finally found a way to modify the legend icon (can you design the legend yourself?)

Let me talk a little bit about MATLAB bottoms. I wrote some drawing functions before, such as the shadow histogram. I want to modify the icons in the legend so that they also have shadows. I take the method of directly drawing some legends that will detect them. The shadow icon of the moving box is used to pretend to be the icon of the legend, so is there a way to really customize the legend?

I really found a way, let me tell you how to do it first, and then I may write some related functions specially:

Or the heart-shaped legend in the upper right corner:

Or change the square box directly:

Thanks to @左右通源
(https://www.zhihu.com/people/zuo-you-feng-yuan-4-38)
for the ideas and some codes! !


principle

First of all, as we all know, MATLAB legends have many hidden properties, roughly like this:

The first thing I think about is how to get out the object bound in the legend icon. Our research has found that there are still many ways, for example:

hLegend.EntryContainer.NodeChildren(n).Icon.Transform.Children.Children

Or with a little more trouble:

hLegend.EntryContainer.Children(n).Children(1).Transform.Children.Children

Or findobjselect the object directly after using:

objHdl=findobj(hLegend.EntryContainer)
objHdl(4).Transform.Children.Children

左右逢源A piece of code for batch acquisition is given:

lgdEntryChild=hLegend.EntryContainer.NodeChildren;
iconSet=arrayfun(@(lgdEntryChild)lgdEntryChild.Icon.Transform.Children.Children,lgdEntryChild,UniformOutput=false)

After getting this object, you can actually modify it directly:

clc;clear;close all
hold on;
plot(1:5,'LineWidth',2);
plot(2:6,'LineWidth',2);
hLegend=legend();
hLegend.Title.String='MyLegend';

% 批量获取全部图标对象
pause(1e-6)
lgdEntryChild=hLegend.EntryContainer.NodeChildren;
iconSet=arrayfun(@(lgdEntryChild)lgdEntryChild.Icon.Transform.Children.Children,lgdEntryChild,UniformOutput=false);

% 换颜色+加粗
iconSet{
    
    2}.ColorData=uint8([0;0;0;255]);
iconSet{
    
    2}.LineWidth=10;

So is there a way to combine it? , this method is 左右逢源thought of, generally just copy an object and modify the group (parent class) it belongs to:

% 复制并改父类
tempIcon=copy(iconSet{
    
    1});
tempIcon.Parent=iconSet{
    
    2}.Parent;


very amazing, so we started various attempts:

writing code for fun

circle+cross

The easiest way to create a new legend is to combine several old legends, so I first tried a combination of circle + cross:

clc;clear;close all
hold on
% 绘制等高线图为构造自定义图例提供素材
x=linspace(-2*pi,2*pi);
y=linspace(0,4*pi);
[X,Y]=meshgrid(x,y);
Z=sin(X)+cos(Y);
contour(X,Y,Z,'LineWidth',1);
% 绘制横线竖线为构造自定义图例提供素材
plot(1:5,'LineWidth',1);
scatter(1,1,'LineWidth',1,'Marker','|');
% 获取横线竖线素材,之所以分两次构建图例
% 且copy第一次构建图例的素材
% 是因为如果直接将图例素材移动再
% 删除无用素材的EntryContainer也依旧会保留空白
hLegend1=legend();
pause(1e-6)
lgdEntryChild=hLegend1.EntryContainer.NodeChildren;
iconSet=arrayfun(@(lgdEntryChild)copy(lgdEntryChild.Icon.Transform.Children.Children),lgdEntryChild,UniformOutput=false);
% 第二次构建图例且只保留等高线图的图例
cHdl=findobj(gca,'Type','Contour');
hLegend2=legend(cHdl,'Data');
pause(1e-6)
iconParent=hLegend2.EntryContainer.NodeChildren(1).Icon.Transform.Children;
icon=hLegend2.EntryContainer.NodeChildren(1).Icon.Transform.Children.Children;
% 简单修改素材组成(删除等高线图例的两个内圈环)
delete(icon(1))
delete(icon(2))
% 修改横线竖线素材的父类组
iconSet{
    
    1}.Parent=iconParent;
iconSet{
    
    2}.Parent=iconParent;
% 修饰素材颜色和大小利于拼接
iconSet{
    
    1}.EdgeColorData=uint8([0;0;0;255]);
iconSet{
    
    1}.Size=8;
iconSet{
    
    2}.ColorData=uint8([0;0;0;255]);
icon(3).ColorData=uint8([0;0;0;255]);

Square contours?

At this time, it has been found that VertexDatathe shape is controlled by the method, and I tried to copy and shrink the legend icon itself:

clc;clear;close all
hold on
% 绘制等高线图为构造自定义图例提供素材
fill([0,0,1,1],[1,0,0,1],[1,1,1],'LineWidth',.8)

hLegend=legend();
pause(1e-6)
icon=hLegend.EntryContainer.NodeChildren.Icon.Transform.Children.Children;

% 复制第一圈改颜色
LineIcon=copy(icon(1));
QuadIcon=copy(icon(2));
LineIcon.VertexData=(LineIcon.VertexData-.5).*.7+.5;
LineIcon.Parent=icon(1).Parent;
QuadIcon.VertexData=(QuadIcon.VertexData-.5).*.7+.5;
QuadIcon.ColorData=uint8([255;220;220;255]);
QuadIcon.Parent=icon(2).Parent;
% 复制第二圈改颜色
LineIcon=copy(icon(1));
QuadIcon=copy(icon(2));
LineIcon.VertexData=(LineIcon.VertexData-.5).*.4+.5;
LineIcon.Parent=icon(1).Parent;
QuadIcon.VertexData=(QuadIcon.VertexData-.5).*.4+.5;
QuadIcon.ColorData=uint8([210;220;225;255]);
QuadIcon.Parent=icon(2).Parent;

triangle

At this time, it is found that the points cannot be adjusted LineLoopand Quadrilateralthe object can only be modified in a limited way:

clc;clear;close all
hold on
% 绘制等高线图为构造自定义图例提供素材
fill([0,0,1,1],[1,0,0,1],[1,1,1],'LineWidth',.8)

hLegend=legend();
pause(1e-6)
icon=hLegend.EntryContainer.NodeChildren.Icon.Transform.Children.Children;
icon(1).VertexData=single([0     1 1     1
                           0     1 1     0
                           0     0 0     0]);

love

After trying to find out LineStripthat TriangleStripthe object can adjust the number of points, I tried to construct a heart-shaped legend icon:

clc;clear;close all
x = -1:1/100:1;
 
y1 = 0.6 * abs(x) .^ 0.5 + ((1 - x .^ 2) / 2) .^ 0.5;
y2 = 0.6 * abs(x) .^ 0.5 - ((1 - x .^ 2) / 2) .^ 0.5;
plot([x, flip(x)], [y1, y2], 'r')

hLegend=legend();
pause(1e-6)
icon=hLegend.EntryContainer.NodeChildren.Icon.Transform.Children.Children;

XX=[x, flip(x),x(1)]./2+.5;
YY=([y1, y2,y1(1)]-.2)./2+.5;
XYZ=single([XX;YY;0.*XX]);

icon.VertexData=XYZ;
icon.LineWidth=2;

It seems that it supports horizontal and vertical legends.


Use of other hidden attributes

hide dividing line

Knowing that there are still many attempts to do what those hidden attributes can do, such as hiding the horizontal line between the title and the legend icon:

clc;clear;close all
hold on;
plot(1:5,'LineWidth',2);
plot(2:6,'LineWidth',2);
hLegend=legend();
hLegend.Title.String='MyLegend';

% 批量获取全部图标对象
pause(1e-6)
hLegend.TitleSeparator.Visible='off';

Could look better.

parallelogram box

clc;clear;close all
hold on;
plot(1:5,'LineWidth',2);
plot(2:6,'LineWidth',2);
hLegend=legend();
hLegend.Title.String='MyLegend';

pause(1e-6)
hLegend.BoxEdge.VertexData=single([0    -.5    1     1.5
                                   0     1     1     0
                                   0     0     0     0]);


over

The above are all the attempts we have made so far for its hidden attributes. I feel that we can make a lot of interesting things. In the future, it is possible to develop drawing functions that really use the legend icon designed by ourselves. The above code is not perfect and needs to be designed. All kinds of monitoring callbacks can reach the level of real use, but it is quite interesting to popularize these hidden things for everyone, see you in the next issue~

Guess you like

Origin blog.csdn.net/slandarer/article/details/131558740