Dynamically adapt the number of self-determining ticks

Taking an example from SO , I would like to adapt the axis ticks depending on the current view. This is the default behavior, unless a specific number of ticks is specified,

The resulting behavior is shown in the figure below. On the left side is the default behavior, on the right is a figure with self-determining ticks. If you rotate the chart with self-determined ticks Z their number will not be adapted to the currently available space (see the figure on the bottom right).

Is there a simple general solution to this without any fancy stuff like getting the current angle on camva() ? I would like not to scale the data itself, since this is a large data set, but use self-defined ticks and tag labels. It should work with MATLAB Version: 8.0.0.783 (R2012b) .

enter image description here

the code

 %# sample graph vertices and edges (similar to your data) [adj,XYZ] = bucky; [r, c] = find(adj); edges = [rc]; %# 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_)) 
+5
source share
1 answer

As pointed out by @bdecaf's comments, you can write an event handler when z-ticks changes change in order to customize label tags. It uses undocumented functionality .

This is convenient because we still allow MATLAB to automatically select the maximum number of ticks to use depending on the screen space available for the axis, we just adjust the format of the displayed labels.

Example:

 function customize_ticks_example() % data [adj,XYZ] = bucky; [r,c] = find(adj); edges = [rc]; 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 

animation

Of course, labels do not have to be numerical, you can use any custom labels if you have some scale at which you can interpolate the values ​​to determine which label to use.


EDIT:

easier way to install an event listener :

 ev = addlistener(gca, 'ZTick', 'PostSet', @onZTickChange); 

(in this case, there is no need to store ev inside the GUI using setappdata . This syntax connects the listener to the life cycle of the GUI object).


EDIT 2:

In response to the comments, here is another example:

 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 

animation2

The idea is to create a hidden copy axis synchronized with the original during 3D rotation. This second axis will have automatic tick marks, while we adjust the ticks of the first axis as we see fit (similar to how we register a callback on a hidden axis when ticks change).

Again, I use this hidden axis to let MATLAB handle the task of determining the best number of ticks, given the view (this is much easier than messing around with camera angles and matrix transformations to find out the length of the projected axis in pixels).

In the above example, I just set the number of ticks to one less than the automatic number ( {2,4,10} instead of {3,5,11} ), but you can use any matching you want.

+5
source

Source: https://habr.com/ru/post/1205836/


All Articles