动态调整自定义刻度数
Posted
技术标签:
【中文标题】动态调整自定义刻度数【英文标题】:Dynamically adapt number of self defined ticks 【发布时间】:2014-10-30 06:55:46 【问题描述】:以an example from SO 为例,我想根据当前视图调整轴刻度。这是默认行为,除非设置自定义的刻度数,
产生的行为如下图所示。左侧是默认行为,右侧是带有自定义刻度的图形。当使用自定义的Z
刻度旋转绘图时,它们的数字将不会适应当前可用空间(见右下图)。
是否有一个简单、通用的解决方案,没有一些花哨的东西,比如通过camva()
获取当前角度?我不想缩放数据本身,因为它是一个大数据集,但要使用自定义刻度和刻度标签。它需要使用MATLAB Version: 8.0.0.783 (R2012b)
。
代码
%# sample graph vertices and edges (similar to your data)
[adj,XYZ] = bucky;
[r, c] = find(adj);
edges = [r c]; %# M-by-2 matrix holding the vertex indices
points = XYZ'; %# 3-by-N matrix of points X/Y/Z coordinates
%# build a list of separated lines
e = edges';
e(end+1,:) = 1;
e = e(:);
p = points(:,e);
p(:,3:3:end) = NaN;
figure
line(p(1,:), p(2,:), p(3,:));
view(3)
% Now the same with self defined ticks
figure
line(p(1,:), p(2,:), p(3,:));
view(3)
z = points(3, :);
fac = 3.14159265359;
tickstep = (max(z)-min(z))/9;
ticklabels_ = min(z):tickstep:max(z);
set(gca, 'ZTick', ticklabels_)
set(gca, 'ZTickLabel', sprintf('%.3f|',ticklabels_))
【问题讨论】:
通常您会在这些答案***.com/questions/4940561/… 中编写回调,但我不确定您如何调整视图以及您可以使用哪些函数及其回调。 @embert:在这种情况下你想发生什么(当图像右下角的例子那样旋转时,带有自定义刻度线)?您希望它恢复为自动刻度间距和标签吗?如果不是你想如何指定手动间距? 【参考方案1】:正如 cmets 中的 @bdecaf 所指出的,您可以编写一个事件处理程序来处理 z-ticks 何时发生变化,以便相应地自定义刻度标签。这是使用undocumentedfunctionality。
这很方便,因为我们仍然让 MATLAB 根据轴可用的屏幕空间自动决定使用的最佳刻度数,我们只需自定义显示标签的格式。
示例:
function customize_ticks_example()
% data
[adj,XYZ] = bucky;
[r,c] = find(adj);
edges = [r c];
points = XYZ';
e = edges';
e(end+1,:) = 1;
e = e(:);
p = points(:,e);
p(:,3:3:end) = NaN;
% plot
hFig = figure;
line(p(1,:), p(2,:), p(3,:), ...
'LineWidth',2, 'Marker','.', 'MarkerSize',20);
ax = handle(gca);
view(3), grid on, box on
xlabel x, ylabel y, zlabel z
rotate3d on
% listen to changes on ZTick property
ev = handle.listener(ax, findprop(ax,'ZTick'), ...
'PropertyPostSet', @onZTickChange);
setappdata(hFig, 'my_ztick_listener', ev);
end
function onZTickChange(~,e)
% get the new 'ZTick', and format them as needed
labels = num2str(e.NewValue(:),'%g $');
% update the 'ZTickLabel'
set(double(e.AffectedObject), 'ZTickLabel',labels)
end
当然,标签不必是数字,您可以使用任何自定义标签,只要您有某种比例尺,您可以沿着该比例尺插入值来确定要使用的标签。
编辑:
set the event listener 的更简单方法:
ev = addlistener(gca, 'ZTick', 'PostSet', @onZTickChange);
(在这种情况下,无需将ev
与setappdata
一起存储在GUI 中。This syntax 将监听器绑定到GUI 对象的生命周期)。
编辑 2:
针对cmets,这里再举一个例子:
function example_manual_ticks
%% some 3d plot
hFig = figure;
sphere
view(3), grid on, box on
xlabel x, ylabel y, zlabel z
%% create a hidden copy of the axis
hax1 = gca;
hax2 = copyobj(hax1, hFig);
set(hax2, 'Visible','off', 'Color','none', 'HitTest','off', ...
'XLimMode','manual', 'YLimMode','manual', 'ZLimMode','manual')
delete(get(hax2, 'Children'))
uistack(hax2, 'bottom')
% sync axes on 3d rotation
hlink = linkprop([hax1,hax2], 'CameraPosition','CameraUpVector');
setappdata(hax1, 'my_axes_linkprop', hlink);
rotate3d on
% respnd to changes in ZTick axis property
ev = addlistener(hax2, 'ZTick', 'PostSet',@(o,e) onZTickChange(o,e,hax1));
%% animation
el = 90 .* sin(linspace(0,2*pi,100) + asin(1/3));
for n=1:numel(el)
view(-37.5, el(n))
drawnow
end
end
function onZTickChange(~,e,ax)
% determine number of ticks
num_ticks = numel(e.NewValue);
new_num_ticks = num_ticks - 1;
% interpolate new ticks along the axis limits
limits = get(ax, 'ZLim');
zticks = linspace(limits(1), limits(2), new_num_ticks);
zticks_labels = num2str(zticks(:), '%.2f ($)');
% update ticks
set(ax, 'ZTick',zticks, 'ZTickLabel',zticks_labels)
drawnow
end
这个想法是创建一个隐藏的copy 轴,kept in sync 与原始的 3D 旋转。这第二个轴将具有自动刻度线,而我们根据需要自定义第一个轴的刻度(类似于我们在隐藏轴上注册回调以了解刻度变化时)。
我再次使用这个隐藏轴作为让 MATLAB 处理确定给定视图的最佳刻度数的任务的一种方式(这比为了找出答案而弄乱相机角度和矩阵变换要容易得多投影轴的长度(以像素为单位)。
在上面的示例中,我只是将刻度数设置为比自动数少一(2,4,10
而不是 3,5,11
),但您可以使用任何类型的映射。
【讨论】:
一些关于情节刻度的相关链接:mathworks.com/matlabcentral/answers/…, mathworks.com/matlabcentral/answers/… addlistener 语法正在运行,amro。 handle.listener 在我的 matlab 版本中没有。您是否也知道如何控制 matlab 如何排列刻度?例如在 matplotlib(一个 python 包)中有 locators 可以使用。例如,您的动画使用 1、3、5 或 11 个刻度。可以改成e吗? G。 2、4、6、8?handle.listener
使用未记录的 UDD 系统,我认为这适用于每个版本(也许不是最新的 R2014b 和 HG2 ?)。无论如何,普通的addlistener
是等价的,所以使用它也很好......
至于控制这样的滴答数,我认为 MATLAB 不支持这样的自定义,而不是深入研究相机属性和投影变换(问题是没有办法获得当前旋转视图中轴边的长度(以像素为单位),如果我们想使用一些规则来确定刻度数,例如每 10 个垂直像素有 1 个 z-tick 标记)...无论如何有一个解决方法,看我的新例子。
不幸的是,这不适用于 2014b+。经过搜索,我找到了Yair commenting on this issue here。我在提供给 FEX 的ticklabelformat
函数的回调函数中使用ticks = eventData.Source.Tick
和eventData.Source.TickLabel = formatted_ticks
语法尝试了提供的FEX,但它不能正常工作。很难猜测现在什么是可靠的方法。 Amro,已经有 HG2 的经验了吗?【参考方案2】:
在 2015b 中,不再支持 PostSet 属性 (https://de.mathworks.com/matlabcentral/answers/231377-problem-with-addlistener-to-xlim)。
如果手动更改大小,则可以使用 WindowMouseRelease 和 SizeChanged 属性。我已经改编了https://***.com/a/26705913/2295776 的代码,但是动画在这个例子中不再起作用了:
% Create sphere
figure1 = figure;
axes1 = axes('Parent',figure1);
hold(axes1,'on');
sphere;
view(axes1, 3);
% create a hidden copy of the axis
axesCopy = copyobj(axes1, figure1);
% sync axes on 3d rotation
Link = linkprop([axes1, axesCopy],'CameraUpVector', 'CameraPosition', 'CameraTarget', 'XLim', 'YLim', 'ZLim');
setappdata(figure1, 'StoreTheLink', Link);
rotate3d on
set(axes1, 'Visible','off', 'Color','none', ...
'XLimMode','auto', 'YLimMode','auto', 'ZLimMode','auto')
set(axesCopy, ...
'XLimMode','manual', 'YLimMode','manual', 'ZLimMode','manual')
delete(get(axesCopy, 'Children'))
grid(axesCopy,'on');
uistack(axesCopy, 'bottom')
% respond to changes in XTick and YTick axis properties
addlistener(figure1,'WindowMouseRelease', @(src, evnt) onTickChange(axes1, axesCopy));
addlistener(figure1,'SizeChanged', @(src, evnt) onTickChange(axes1, axesCopy));
onTickChange(axes1, axesCopy);
%% functions
function onTickChange(axes, axesCopy)
zticks_orig = zticks(axes);
ZTickLabels = num2str(zticks_orig(:), '%.2f ($)');
% update ticks
set(axesCopy, 'ZTick', zticks(axes), 'ZTickLabel', ZTickLabels);
drawnow;
end
【讨论】:
以上是关于动态调整自定义刻度数的主要内容,如果未能解决你的问题,请参考以下文章
R语言ggplot2可视化增加坐标轴的刻度数实战:自定义坐标轴刻度粒度增加坐标轴刻度的粒度更加精细地表达
根据自定义类动态调整 Create Table 和 Insert Into 语句