目录
- 前言
- 自定义标记函数
- 自定义标记函数的说明
- 纵横比调整
- 将图形大小按磅数设置
- 平移标记点
- 绘制标记点
- 边界标记点不裁剪
- 拓展功能——标记点自适应绘图区的缩放
- 绘图区缩放回调函数
- 标记点大小自适应
- 标记点裁剪自适应
- 示例
- 基本绘图
- 自定义标记函数的使用
- 总代码
- 主函数
- 自定义标记函数
- 回调函数
- 其他函数
- 总结
前言
在MATLAB中,使用plot、scatter和patch等函数进行绘图时,很多时候都需要使用标记(Marker)对数据点进行标记,以便更清晰地展示数据。在MATLAB只能使用默认的标记形状(如下图),但有时觉得MATLAB自带的标记不好看或者不够用时就需要自定义标记形状,而MATLAB中并没有提供自定义标记这样一个功能或者函数。
本文章提供了一个可以自定义标记形状的函数,只需要输入标记形状的点坐标和连接方式即可绘制出与MATLAB自带标记基本一致的自定义标记,包括随着绘图区的缩放,标记在屏幕上的大小不会改变,以及绘图区边缘的标记可以超出绘图区等功能,同时可以设置该标记的颜色、磅数、线宽等基本属性。
自定义标记函数
自定义标记的主函数如下,该函数目前只能绘制没有交叉线的图形。前5个参数为必须输入,包括ax绘图区对象,曲线的x和y坐标,标记形状的点坐标GPoints(第一列为x坐标,第二列为y坐标),形状点的连接顺序(不需要首尾相连)GSeq。
其他可设置参数为填充的面颜色faceColor,填充的线颜色edgeColor,形状的大小pounds,形状的线宽度lineWidth,形状的数量NMarker(近似数量, 采用等间距, 如不能整除则四舍五入,输入0则表示绘制所有点的标记)。同时,若不需要则以空数组[ ]代替, 如不需要形状磅数pounds, 则pounds = [ ]。
function setMarker(ax, x, y, GPoints, GSeq, faceColor, edgeColor, pounds, lineWidth, NMarker)
% 自定义设置标记形状(目前只能绘制没有交叉线的图形), 前5个参数为必须输入, 其他参数为可选参数
% 若不需要则以空数组[]代替, 如不需要形状磅数pounds, 则pounds = []
%
% ax 绘图区
% x,y 绘图的数据点
% GPoints 形状的点坐标
% GSeq 形状点的连接顺序(不需要首尾相连)
% faceColor 填充的面颜色 默认:none
% edgeColor 填充的线颜色 默认:k
% pounds 形状的大小(磅数) 默认:6
% lineWidth 形状的线宽度 % 默认:0.5
% NMarker 形状的数量(近似数量, 采用等间距, 如不能整除则四舍五入)
% 默认:0, 0则代表全部点都绘制if nargin < 6, faceColor = 'none'; end
if nargin < 7, edgeColor = 'k'; end
if nargin < 8, pounds = 6; end
if nargin < 9, lineWidth = 0.5; end
if nargin < 10, NMarker = 0; endif isempty(faceColor), faceColor = 'none'; end
if isempty(edgeColor), edgeColor = 'k'; end
if isempty(pounds), pounds = 6; end
if isempty(lineWidth), lineWidth = 0.5; end
if isempty(NMarker), NMarker = 0; endif NMarker == 0, NMarker = length(x); endhold(ax,"on") NG = length(GSeq); % 点数量% 将图形的比例拉正
xl = xlim;
yl = ylim;
lenx = xl(2)-xl(1); % x轴的长度
leny = yl(2)-yl(1); % y轴的长度
yRatio = leny/lenx; % 纵横比比例
GPoints(:,2) = GPoints(:,2)*yRatio; % 修正坐标 % 获取绘图区的每单位在屏幕上的长度(磅)
unitLength = getUnitLength();% 计算水平距离矩阵
W = zeros(NG);
for i = 1:NGfor j = 1:NGif i ~= jW(i,j) = sqrt((GPoints(i,1)-GPoints(j,1))^2);endend
end% 缩放比例
scale = 2*pounds/(max(max(W))*unitLength);
% 缩放
GPoints = GPoints*scale;PM = 1:round(length(x)/NMarker):length(x); % 需要绘制的点位置
NMarker = length(PM); % 绘制标记的真实数量% 重心坐标
[Cx, Cy] = polygon_centroid(GPoints);% 初始化所有标记的坐标和连接顺序
GPointsAll = zeros(NMarker*NG,2);
GSeqAll = zeros(NMarker,NG);
% 边界点判断初始化
BJudge = false(NMarker,1);
for k = 1:NMarker% 取出当前点坐标xk = x(PM(k));yk = y(PM(k));% 平移displacement = [xk-Cx,yk-Cy];% 更新坐标GPointsNew = GPoints;GPointsNew = GPointsNew + repmat(displacement,size(GPoints,1),1);% 保存所有标记点的坐标和连接顺序GPointsAll(NG*(k-1)+1:NG*k,:) = GPointsNew;GSeqAll(k,:) = GSeq + NG*(k-1);%%% 判断当前标记点是否处于边界 %%%xkk = GPointsNew(:, 1); % 当前标记点的x坐标ykk = GPointsNew(:, 2); % 当前标记点的y坐标% 标记形状坐标判断BLeft = xkk - xl(1); % 左边界BRight = xkk - xl(2); % 右边界BDown = ykk - yl(1); % 下边界BUp = ykk - yl(2); % 上边界% 标记中心坐标判断BLeft2 = xk - xl(1); % 左边界BRight2 = xl(2)-xk; % 右边界BDown2 = yk - yl(1); % 下边界BUp2 = yl(2) - yk; % 上边界% 判断形状坐标是否同号以及中心点是否在绘图区if (~judgeSign(BLeft) || ~judgeSign(BRight) || ~judgeSign(BDown) || ~judgeSign(BUp)) && ...all([BLeft2,BRight2,BDown2,BUp2]>=0)BJudge(k) = true; % 记录处于边界的标记点endend
% 绘制标记点
pa = patch('Faces', GSeqAll, 'Vertices', GPointsAll, 'FaceColor', faceColor,...'EdgeColor', edgeColor,'lineWidth',lineWidth);% 设置绘图区边缘图形不裁剪
pa2 = patch('Faces', GSeqAll(BJudge,:), 'Vertices', GPointsAll, 'FaceColor', faceColor,...'EdgeColor', edgeColor,'LineWidth',lineWidth, 'Clip', 'off');%%%% 控制图形放大缩小 %%%%
zm = zoom(ax);
zm.Enable = 'on'; % 启用缩放% 初始化xy轴范围
xl = xlim;
yl = ylim;
setappdata(ax,'lxl',xl)
setappdata(ax,'lyl',yl) % 绘图区缩放回调函数
set(zm, 'ActionPostCallback', @(src, event) updateZoomInfo(ax,x,y,PM,NG,NMarker,pa,pa2)); end
自定义标记函数的说明
纵横比调整
在自定义形状时,如果x轴和y轴的刻度一致,则绘制出来的图形与实际一致。大多数情况下,MATLAB绘制图形时一般x轴和y轴的刻度并不一致,比如x轴一格长度为2,y轴一格长度为500,这时绘制出来的图形看起来会呈现上下被压扁的性质。比如,原本需要绘制的形状是下图这个四边形,但是因为x轴和y轴的刻度不一致,会出现绘制出来的形状只呈现出一条线(下面第二张图)。
为了避免这种情况,首先就要对图形比例进行修正,即获取x轴和y轴刻度之间的比例,对标记图形的x或者y坐标进行拉伸或者收缩。
% 将图形的比例拉正
xl = xlim;
yl = ylim;
lenx = xl(2)-xl(1); % x轴的长度
leny = yl(2)-yl(1); % y轴的长度
yRatio = leny/lenx; % 纵横比比例
GPoints(:,2) = GPoints(:,2)*yRatio; % 修正坐标
下面是调整后的效果图。
将图形大小按磅数设置
放缩了x轴或者y轴的比例后,会出现标记图形过大或者过小的情况,这时就需要对标记的坐标进行放缩。在MATLAB中,标记的大小(MarkerSize)通常以磅数为单位,默认大小为6磅。同样参考MATLAB的方式,我们绘制图形也可以用磅数设置图形大小,但并不知道绘图区单位长度对应的磅数大小是多少。因此,首先要计算绘图区中的单位磅数。
下面的函数中,Position(3)对应的就是x轴的总磅数,除x轴的总长度即可得到绘图区中的单位磅数。在上图中,绘图区中的单位磅数为50.9091磅。
function unitLength = getUnitLength()
% 获取绘图区的每单位在屏幕上的长度(磅)% 获取图形窗口的大小和位置
Po = get(gcf, 'Position'); % 返回的单位是点 (points),每个点约为 1/72 英寸
figWidth = Po(3); % 图形的宽度 % X轴范围
xl = xlim;
rangeX = xl(2) - xl(1); % 绘图区的每单位在屏幕上的长度(磅)
unitLength = figWidth/rangeX;end
得到了绘图区中的单位磅数后就可以以某一个标准(比如图形的水平长度、竖直长度和最长边的长度等)对图形的坐标进行放缩。本文采用图形的水平长度L为标准,将长度L乘单位长度unitLength可以得到图形当前的磅数,由于MATLAB中图形的大小是以半径为参考的,故需要在前面乘2。
% 获取绘图区的每单位在屏幕上的长度(磅)
unitLength = getUnitLength();% 水平长度矩阵
L = max(GPoints(:,1)) - min(GPoints(:,1));% 缩放比例
scale = 2*pounds/(L*unitLength);% 缩放
GPoints = GPoints*scale;
设置磅数pounds = 6,可以得到下面的效果图(红色为自定义标记,黑色为MATLAB自带的三角形标记),可以看到,自定义标记与MATLAB自带标记的大小一致。
平移标记点
已知标记点初始的点坐标和连接顺序后,还需要将标记点平移到相应的位置。
NMarker为需要绘制的标记点数量,为得到标记点的位置,需要将整个数据区间均分成NMarker份。但是,输入的NMarker并不一定能均分,因此文章采用一种四舍五入的方法,即下面的round(length(x)/NMarker),以及更新NMarker的数量。
% 计算绘制的点位置
PM = 1:round(length(x)/NMarker):length(x); % 需要绘制的点位置
NMarker = length(PM); % 绘制标记的真实数量
得到了绘制标记点的位置后,需要将标记平移到该位置。本文以标记图形的重心作为参考,计算重心到该位置的位移,再到坐标进行更新。
计算多边形的重心方法如下:
- 计算面积 A A A
假设一个多边形的顶点坐标为 ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x n , y n ) (x_1, y_1), (x_2, y_2), \ldots, (x_n, y_n) (x1,y1),(x2,y2),…,(xn,yn),连接顺序为 1 − n − 1 1-n-1 1−n−1,则该多边形的面积 A