MATLAB Answers

Data tip on the centroid of a patch face?

16 ビュー (過去 30 日間)
Oleg Iupikov
Oleg Iupikov 2021 年 3 月 28 日
編集済み: Oleg Iupikov 2021 年 3 月 31 日
Hallo.
Is it possible programmatically create a data tip on a patch face center? It looks like it always sticks to the patch vertices.
The color of this 2D patch represents some data which would be nice to explore using data tip with a custom update function. I know how to write such update function but the problem is that the data tip automatically sticks only to the patch vertices...
Here is the patch example:
Thanks!
----- Update ----
Here is an example:
clc
close all
% Define some data
[xo,yo] = ndgrid(1:4,1:5); % data point positions
data = 100*rand(numel(xo),1); % data point values
% Form patch faces, each for each point of the data
width = 0.8;
x = nan(4,numel(xo));
y = nan(4,numel(xo));
x(1,:) = xo(:) - width/2; y(1,:) = yo(:) - width/2;
x(2,:) = xo(:) - width/2; y(2,:) = yo(:) + width/2;
x(3,:) = xo(:) + width/2; y(3,:) = yo(:) + width/2;
x(4,:) = xo(:) + width/2; y(4,:) = yo(:) - width/2;
% Plot
hFig = figure('color','w');
hPatch = patch('XData',x, 'YData',y, 'ZData',0*x, 'CData',data, 'FaceColor','flat');
colormap jet; colorbar;
% Set custom update function
dcm = datacursormode(hFig);
set(dcm,'UpdateFcn',{@myupdatefcn,data});
% Add tatatip. Here I would like to display the data tip at exact location (2,3) which is middle of the patch face, but
% it is snapped to the face vertex despite the option 'SnapToDataVertex' is 'off'...
% Moreover, it seems the datatip function do not respect the new update function (but this is not important for me now).
% See the figure below.
dt = datatip(hPatch, 2,3, 'SnapToDataVertex','off');
function txt = myupdatefcn(~,evt,data)
pos = get(evt,'Position');
ind = ceil( get(evt, 'DataIndex') / 4 );
txt = { sprintf('(x,y): (%g, %g)', pos(1:2)),...
sprintf('data index: %i', ind),...
sprintf('data value: %g', data(ind))
};
end
Here is result:
First datatip was created by the function datatip which is not at the position (2, 3) despite the option 'SnapToDataVertex' is 'off' (and which do not use the new update function). Second data tip was created by clicking the patch.
How to switch off the snapping?
P.S. MATLAB version R2020b.
  7 件のコメント
Oleg Iupikov
Oleg Iupikov 2021 年 3 月 30 日
I'm writing a general function, which can be used in many different occasions, including 2D (like in this example) and 3D (like surf) plots, including some interactivity elements. I like patch for its high customizability. Data can represent some physical objects (e.g. in my use case they can be antenna elements in an array), which are not necessary rectangular and equispaced. They may be located in 3D space, and not in plane. And so far I have plotted data with tens of thouthads faces without facing performance issues.
Yes, imagesc is nice and useful funtion, but only for a particular use case...
patch is very useful in many visualization cases, and it would be really nice if it can support data tips with SnapToDataVertex='off', like lines do.
But thanks for your tips anyway! Very much appreciated! Perhaps I will go for small dots for now.

サインインしてコメントする。

採用された回答

Adam Danz
Adam Danz 2021 年 3 月 30 日
編集済み: Adam Danz 2021 年 3 月 30 日
Datatips are anchored to the vertices of patches and other graphics objects so the only way I know of to make datatip positions more flexible is to create a temporarily point that hosts the datatip.
That's what this demo does.
Basic methods (see inline comments for details)
  1. Assigns a ButtonDownFcn to the patch objects that responds to mouse clicks on the patches.
  2. Stores the data variable in the patch object UserData so we don't have to pass it to the function.
  3. Turns off datacursormode because that will block the hit detection on patches (at least it did for me).
The ButtonDownFcn does the following
  1. Finds the nearst patch vertices to the mouse click
  2. Finds the patch associated with those vertices
  3. Stores original axis hold-state, places axis on hold (returns hold state at the end).
  4. Searches for pre-existing data tips and data tip markers (see next step) created by this function and deletes them.
  5. Adds a small, barely visible temporary marker at the location of the mouse click. This marker will host the datatip.
  6. Adds datatip to the temporary marker.
  7. Updates the datatip to display the index and data info.
% Define some data
[xo,yo] = ndgrid(1:4,1:5); % data point positions
data = 100*rand(numel(xo),1); % data point values
% Form patch faces, each for each point of the data
width = 0.8;
x = nan(4,numel(xo));
y = nan(4,numel(xo));
x(1,:) = xo(:) - width/2; y(1,:) = yo(:) - width/2;
x(2,:) = xo(:) - width/2; y(2,:) = yo(:) + width/2;
x(3,:) = xo(:) + width/2; y(3,:) = yo(:) + width/2;
x(4,:) = xo(:) + width/2; y(4,:) = yo(:) - width/2;
% Plot patch objects
hFig = figure('color','w');
hPatch = patch('XData',x, 'YData',y, 'ZData',0*x, 'CData',data, 'FaceColor','flat');
colormap jet; colorbar;
% Store data in patch-userdata
hPatch.UserData.data = data;
% Set buttondownfcn on patch objects.
datacursormode(hFig,'off') %data cursor mode will prevent mouse clicks!
hPatch.ButtonDownFcn = @patchButtonDownFcn;
function patchButtonDownFcn(h, hit)
% Responds to mouse clicks on patch objs.
% Add point at click location and adds a datacursor representing
% the underlying patch.
% datatip() requires Matlab r2019b or later
% Find closet vertices to mouse click
hitPoint = hit.IntersectionPoint;
[~, minDistIdx] = min(pdist2(h.Vertices,hitPoint));
% Find patch associated with nearest vertices
[patchIndex, ~] = find(h.Faces == minDistIdx);
% store original hold state and return at the end
ax = ancestor(h,'axes');
holdStates = ["off","on"];
holdstate = ishold(ax);
cleanup = onCleanup(@()hold(holdStates(holdstate+1)));
hold(ax,'on')
% Search for and destroy previously existing datatips
% produced by this callback fuction.
preexisting = findobj(ax,'Tag','TempDataTipMarker');
delete(preexisting)
% detect 2D|3D axes
nAxes = numel(axis(ax))/2;
% Plot temp invisible point at click location and add basic datatip
if nAxes==2 % 2D axes
hh=plot(ax,hitPoint(1),hitPoint(2),'k.','Tag','TempDataTipMarker');
dt = datatip(hh, hitPoint(1), hitPoint(2),'Tag','TempDataTipMarker');
else %3D axes
dt = datatip(h, hitPoint(1), hitPoint(2), hitPoint(3),'Tag','TempDataTipMarker');
end
clear cleanup % return hold state
% Update datatip
pos = hit.IntersectionPoint(1:2);
dtr = dataTipTextRow('Patch index:', patchIndex, '%i');
dtr(2) = dataTipTextRow('Data value:', h.UserData.data(patchIndex), '%g');
hh.DataTipTemplate.DataTipRows(end+1:end+2) = dtr;
end
  1 件のコメント
Oleg Iupikov
Oleg Iupikov 2021 年 3 月 31 日
Nice! I've learned several things from it. Thanks!

サインインしてコメントする。

その他の回答 (0 件)

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!

Translated by