Add data tips to non-supported chart

12 views (last 30 days)
Roger Breton
Roger Breton on 18 Jan 2022
Edited: Walter Roberson on 3 Dec 2024
I'm creating a 2D graph, element by element, using rectangles. This is my current code:
clc
fig=figure('Position', [600 300 1000 1000])
fig =
Figure (1) with properties: Number: 1 Name: '' Color: [1 1 1] Position: [600 300 1000 1000] Units: 'pixels' Use GET to show all properties
ax = axes
ax =
Axes with properties: XLim: [0 1] YLim: [0 1] XScale: 'linear' YScale: 'linear' GridLineStyle: '-' Position: [0.1300 0.1100 0.7750 0.8150] Units: 'normalized' Use GET to show all properties
axis([-128 128 -128 128])
xticks([-128 -112 -96 -80 -64 -48 -32 -16 0 16 32 48 64 80 96 112 128]);
yticks([-128 -112 -96 -80 -64 -48 -32 -16 0 16 32 48 64 80 96 112 128]);
grid on
grid minor
OffsetX=0;
OffsetY=0;
Axe_A = transpose(linspace(0, 96, 24))
Axe_A = 24×1
0 4.1739 8.3478 12.5217 16.6957 20.8696 25.0435 29.2174 33.3913 37.5652
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Axe_L = transpose(linspace(50, 50, 24))
Axe_L = 24×1
50 50 50 50 50 50 50 50 50 50
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Axe_B = transpose(linspace(0, 0, 24))
Axe_B = 24×1
0 0 0 0 0 0 0 0 0 0
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
InGamut = transpose(linspace(0, 0, 24))
InGamut = 24×1
0 0 0 0 0 0 0 0 0 0
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Lab = [Axe_L, Axe_A, Axe_B];
RGB = lab2rgb(Lab)
RGB = 24×3
0.4663 0.4663 0.4663 0.4951 0.4573 0.4668 0.5226 0.4477 0.4674 0.5492 0.4376 0.4679 0.5749 0.4269 0.4685 0.5999 0.4156 0.4690 0.6242 0.4035 0.4696 0.6481 0.3906 0.4702 0.6714 0.3767 0.4708 0.6943 0.3619 0.4715
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
%Add 4th column to flag out of gamut colors
RGBx = [RGB InGamut];
for ROW = 1:row
if RGB(ROW,1) < 0
RGBx(ROW,1) = 0;
RGBx(ROW,4) = 1;
end
if RGB(ROW,1) > 1
RGBx(ROW,1) = 1;
RGBx(ROW,4) = 1;
end
if RGB(ROW,2) < 0
RGBx(ROW,2) = 0;
RGBx(ROW,4) = 1;
end
if RGB(ROW,2) > 1
RGBx(ROW,2) = 1;
RGBx(ROW,4) = 1;
end
if RGB(ROW,3) < 0
RGBx(ROW,3) = 0;
RGBx(ROW,4) = 1;
end
if RGB(ROW,3) > 1
RGBx(ROW,3) = 1;
RGBx(ROW,4) = 1;
end
end
Unrecognized function or variable 'row'.
T = RGBx
[row,col] = size(RGB); % 16 x 3 double
for ROW = 1:row
if RGBx(ROW,4) == 0
rectangle(ax,'Position',[OffsetX OffsetY 4 4],'FaceColor',[RGBx(ROW,1) RGBx(ROW,2) RGBx(ROW,3)], 'LineStyle', 'none');
else
rectangle(ax,'Position',[OffsetX OffsetY 4 4],'FaceColor',[RGBx(ROW,1) RGBx(ROW,2) RGBx(ROW,3)]');
end
OffsetX=OffsetX+4
end
This is what the result looks like, so far :
Trouble is, there does not seem to be a way to add data tips to this graphics?
I wish I could display the Lab values when the mouse hover the graphic.

Answers (1)

Adam Danz
Adam Danz on 18 Jan 2022
Edited: Adam Danz on 21 Jan 2022
Demo 1: Click rectangle to show datatip using ButtonDownFcn
This demo is based on this answer using patches. A ButtonDownFcn is assigned to each rectangle. When clicked, a datatip appears. A 1x3 vector of RGB color values is added to the datatip.
% Define rectangle data
[xo,yo] = meshgrid(1:4,1:5); % rectangle (x,y) positions
rng('default') % for reproducibility of this demo
colors = rand(numel(xo),3) % mx3 color matrix for m rectangles (aka "Lab" in your code)
labels = compose('[%.2f %.2f %.2f]',colors) % Rectangle labels
% Draw rectangles
hFig = figure();
ax = axes(hFig);
hold(ax, 'on')
for i = 1:numel(xo)
rectangle(ax, 'Position', [xo(i), yo(i), .6, .6], ...
'FaceColor', colors(i,:), ...
'LineStyle','none', ...
'UserData', labels(i), ... % Assign label to each rectangle
'ButtonDownFcn', @rectButtonDownFcn); % Assign button-down-function
end
function rectButtonDownFcn(rectObj, hit)
% Responds to mouse clicks on rectangle objs.
% Add point at click location and adds a datatip representing
% the underlying patch.
% datatip() requires Matlab r2019b or later
% store mouse click coordinate
hitPoint = hit.IntersectionPoint;
% store original hold state and return at the end
ax = ancestor(rectObj,'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 point at click location and add 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
hh=plot(ax,hitPoint(1),hitPoint(2),hitPoint(3),'k.','Tag','TempDataTipMarker');
dt = datatip(hh, hitPoint(1), hitPoint(2), hitPoint(3),'Tag','TempDataTipMarker');
end
dt.DeleteFcn = @(~,~)delete(hh);
clear cleanup % return hold state
% Update datatip
% pos = hit.IntersectionPoint(1:2);
dtr = dataTipTextRow('Color:', repelem(rectObj.UserData,1,4));
hh.DataTipTemplate.DataTipRows(end+1) = dtr;
end
Demo 2: Move cursor over rectangles to show datatip using WindowButtonMotionFcn
A WindowButtonMotionFcn is assigned to the figure. When the mouse is within a rectangle, a datatip appears and follows the mouse until it leaves the rectangle. A 1x3 vector of RGB color values is added to the datatip. For simplicity, this demo assumes rectangles do not overlap and that their edges are parallel to the x and y axes. If any rectangle is deleted, the WindowButtonMotionFcn will be removed.
% Define rectangle data
[xo,yo] = meshgrid(1:4,1:5); % rectangle (x,y) positions
rng('default') % for reproducibility of this demo
colors = rand(numel(xo),3) % mx3 color matrix for m rectangles (aka "Lab" in your code)
labels = compose('[%.2f %.2f %.2f]',colors) % Rectangle labels
% Draw rectangles
hFig = figure();
ax = axes(hFig);
hold(ax, 'on') % required by WindowButtonMotionFcn
rectHandles = gobjects(size(xo));
for i = 1:numel(xo)
rectHandles(i) = rectangle(ax, ...
'Position', [xo(i), yo(i), .6, .6], ...
'FaceColor', colors(i,:), ...
'LineStyle','none', ...
'UserData', labels(i), ... % Assign label to each rectangle
'DeleteFc', @(h,~)set(ancestor(h,'figure'), 'WindowButtonMotionFcn',''));
% Remove WindowButtonMotionFcn if any rectangle is deleted
end
% Assign WindowButtonMotionFcn to figure
hFig.WindowButtonMotionFcn = {@rectButtonMoFcn, rectHandles(:), ax};
function rectButtonMoFcn(~, ~, rectObj, axHandle)
% Responds to mouse movement in figure (fig).
% Displays datatip if within rectangle (rectObj).
% This fcn should be as efficient and simple as possible.
% datatip() requires Matlab r2019b or later
persistent floatingMarkerHandle
% Create floating marker if it doesn't exist
if isempty(floatingMarkerHandle) || ~isvalid(floatingMarkerHandle)
floatingMarkerHandle = plot(axHandle,rectObj(1).Position(1),rectObj(1).Position(2),'k.','Visible','off');
datatip(floatingMarkerHandle, floatingMarkerHandle.XData, floatingMarkerHandle.YData);
dtr = dataTipTextRow('Color:', repelem({'[0 0 0]'},1,4));
floatingMarkerHandle.DataTipTemplate.DataTipRows(end+1) = dtr;
end
% get rectangle values
rectPos = cell2mat(get(rectObj,'position'));
rectX = [rectPos(:,1), rectPos(:,1)+rectPos(:,3)];
rectY = [rectPos(:,2), rectPos(:,2)+rectPos(:,4)];
% Determine which rectangle the mouse is in, if any.
% Assumes rectangle edges are vertical or horizontal (parallel to x,y axes)
cp = axHandle.CurrentPoint(1,1:2); % Current mouse point, data units, 2D axes.
rectIdx = cp(1) >= rectX(:,1) & ...
cp(1) <=rectX(:,2) & ...
cp(2) >= rectY(:,1) & ...
cp(2) <=rectY(:,2);
% Update floating marker and data tip if inside a rectangle; otherwise turnoff
if any(rectIdx)
set(floatingMarkerHandle,'XData',cp(1),'YData',cp(2),'Visible','on');
rectUserData = rectObj(rectIdx).UserData; % Assumes only 1 rectangle is selected (ie, no overlap)
floatingMarkerHandle.DataTipTemplate.DataTipRows(end).Value = rectUserData;
else
set(floatingMarkerHandle,'Visible','off');
end
end
  10 Comments
Muhammad Anam
Muhammad Anam on 1 Dec 2024
@Adam Danz: Why there is a need to use command 'plot'. Why data cannot directly be pinned to patch?
Adam Danz
Adam Danz on 2 Dec 2024
@Muhammad Anam, good question. Rather than limiting data tips to the 4 vertices of a rectanlge, my solution allows you to click anywhere in the rectangle. But the datatip still has to be associated with an existing plotted cooredinate. So I'm using plot() to add a temporary point at the click location where the datatip should appear.

Sign in to comment.

Categories

Find more on Graphics Performance in Help Center and File Exchange

Products


Release

R2021b

Community Treasure Hunt

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

Start Hunting!