Changing colour of plotted shape when the mouse hovers it?

6 views (last 30 days)
I am trying to change the color of the side of a shape when the mouse hovers over any of the sides of the shape.
So e.g if I have the shape below and hover my mouse over the right side I want that side to change to a different color.
x = [1 1 5 5 10 10];
y = [1 10 10 5 5 1];
pgon = polyshape(x,y);
plot(pgon)

Accepted Answer

Benjamin Kraus
Benjamin Kraus on 17 Nov 2021
You cannot change the color of a single side of a polygon object, so I think your only option is to overlay a line over the side you want to highlight.
I'm not 100% clear on your requirements. Here is a simple script that will highlight the indicated side, regardless of where you've hovered over the polyshape.
x = [1 1 5 5 10 10];
y = [1 10 10 5 5 1];
pgon = polyshape(x,y);
f = figure;
ax = axes(f);
polygonObj = plot(ax, pgon);
% Create a line to use as an overlay, but leave it invisible for now.
lineObj = line(ax, [10 10],[1 5],'LineWidth',2,'Color','red');
lineObj.Visible = false;
% Add a callback to be called when the mouse moves.
f.WindowButtonMotionFcn = @(~,~) hover(polygonObj, lineObj);
function hover(polygonObj, lineObj)
% Find out where the mouse is pointing.
ax = polygonObj.Parent;
x = ax.CurrentPoint(1,1);
y = ax.CurrentPoint(1,2);
% Determine whether the mouse is inside the shape.
pgon = polygonObj.Shape;
if isinterior(pgon, x, y)
% Make the line visible
lineObj.Visible = true;
else
% Hide the line
lineObj.Visible = false;
end
end

More Answers (1)

Benjamin Kraus
Benjamin Kraus on 17 Nov 2021
Here is a much more complicated version that I think does what you wanted (although I'm not sure).
This version figures out which edge of the polygon is closest to the mouse and highlights that edge.
I adapted this code heavily from the example provided in this MATLAB Answer: Distance Between Points and a Line Segment in 3D
x = [1 1 5 5 10 10];
y = [1 10 10 5 5 1];
pgon = polyshape(x,y);
% Draw the polyshape
f = figure;
ax = axes(f);
polygonObj = plot(ax, pgon);
% Create a line to use as an overlay, but leave it invisible for now.
lineObj = line(ax, NaN, NaN,'LineWidth',3,'Color','red');
lineObj.Visible = false;
% Add a callback to be called when the mouse moves.
f.WindowButtonMotionFcn = @(~,~) hover(polygonObj, lineObj);
function hover(polygonObj, lineObj)
% Find out where the mouse is pointing.
ax = polygonObj.Parent;
x = ax.CurrentPoint(1,1);
y = ax.CurrentPoint(1,2);
% Determine whether the mouse is inside the shape.
pgon = polygonObj.Shape;
if isinterior(pgon, x, y)
% Append the first vertex to the end.
vertices = pgon.Vertices;
vertices = [vertices; vertices(1,:)];
% Find the closest edge.
ind = closestEdge([x y], vertices);
% Draw the line along the closest edge.
lineObj.XData = vertices(ind+[0 1],1);
lineObj.YData = vertices(ind+[0 1],2);
lineObj.Visible = true;
else
% Outside the polygon, hide the line.
lineObj.Visible = false;
end
end
function ind = closestEdge(pt, vertices)
% Pad both vertices and point with 0.
vertices(:,3) = 0;
pt(3) = 0;
% Find the length of each edge:
lengthEdges = vecnorm(diff(vertices, [], 1), 2, 2);
% Find the distance from the point to each individual vertex.
distPointToVertices = vecnorm(pt - vertices, 2, 2);
distPointToStart = distPointToVertices(1:end-1);
distPointToEnd = distPointToVertices(2:end);
% Find the distance from each point to an infinite line along each edge.
a = diff(vertices, [], 1);
b = pt - vertices(2:end,:);
distPointToLine = vecnorm(cross(a,b,2),2,2) ./ vecnorm(a,2,2);
% Determine which edges have the point past the "end" of the edge.
pastTheEnd = vecnorm([lengthEdges distPointToEnd], 2, 2) <= distPointToStart;
% Determine which edges have the point past the "start" of the edge.
pastTheStart = vecnorm([lengthEdges distPointToStart], 2, 2) <= distPointToEnd;
% Find the distance to the edge.
distToEdge = distPointToLine;
distToEdge(pastTheEnd) = distPointToEnd(pastTheEnd);
distToEdge(pastTheStart) = distPointToStart(pastTheStart);
% Find the closest edge.
[~,ind] = min(distToEdge);
end
  1 Comment
Benjamin Kraus
Benjamin Kraus on 17 Nov 2021
And, one more version. This one highlights the closest edge, but only if you are within a short distance from the edge.
x = [1 1 5 5 10 10];
y = [1 10 10 5 5 1];
pgon = polyshape(x,y);
% Draw the polyshape
f = figure;
ax = axes(f);
polygonObj = plot(ax, pgon);
% Create a line to use as an overlay, but leave it invisible for now.
lineObj = line(ax, NaN, NaN,'LineWidth',3,'Color','red');
lineObj.Visible = false;
% Add a callback to be called when the mouse moves.
f.WindowButtonMotionFcn = @(~,~) hover(polygonObj, lineObj);
function hover(polygonObj, lineObj)
% Find out where the mouse is pointing.
ax = polygonObj.Parent;
x = ax.CurrentPoint(1,1);
y = ax.CurrentPoint(1,2);
% Append the first vertex to the end.
vertices = polygonObj.Shape.Vertices;
vertices = [vertices; vertices(1,:)];
% Find the distance to the closest edge.
[d, ind] = closestEdge([x y], vertices);
% If the distance is within a tolerance of 0.1, highlight the edge.
if d < 0.1
% Draw the line along the closest edge.
lineObj.XData = vertices(ind+[0 1],1);
lineObj.YData = vertices(ind+[0 1],2);
lineObj.Visible = true;
else
% Hide the line.
lineObj.Visible = false;
end
end
function [d,ind] = closestEdge(pt, vertices)
% Pad both vertices and point with 0.
vertices(:,3) = 0;
pt(3) = 0;
% Find the length of each edge:
lengthEdges = vecnorm(diff(vertices, [], 1), 2, 2);
% Find the distance from the point to each individual vertex.
distPointToVertices = vecnorm(pt - vertices, 2, 2);
distPointToStart = distPointToVertices(1:end-1);
distPointToEnd = distPointToVertices(2:end);
% Find the distance from each point to an infinite line along each edge.
a = diff(vertices, [], 1);
b = pt - vertices(2:end,:);
distPointToLine = vecnorm(cross(a,b,2),2,2) ./ vecnorm(a,2,2);
% Determine which edges have the point past the "end" of the edge.
pastTheEnd = vecnorm([lengthEdges distPointToEnd], 2, 2) <= distPointToStart;
% Determine which edges have the point past the "start" of the edge.
pastTheStart = vecnorm([lengthEdges distPointToStart], 2, 2) <= distPointToEnd;
% Find the distance to the edge.
distToEdge = distPointToLine;
distToEdge(pastTheEnd) = distPointToEnd(pastTheEnd);
distToEdge(pastTheStart) = distPointToStart(pastTheStart);
% Find the closest edge.
[d,ind] = min(distToEdge);
end

Sign in to comment.

Tags

Community Treasure Hunt

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

Start Hunting!