Modifying arrow length based on intersection with polygon

3 views (last 30 days)

Background: I'm running a niche analysis for animal body part coordinates, attempting to estimate the visual field of an animal. For each frame of a video, I have extracted XY coordinates of a body part, which I have been using as an origin to draw polygons representing the visual fields of this animal (not shown here). For each row of coordinates, I am drawing multiple cone-like polygons, rotating them such that they are evenly spaced by 10°, extending outwards from the coordinate of this body part.
Difficulty with new analysis: I would like to create a separate analysis, where instead of extending polygons, I am instead extending arrows from the coordinate/origin, with their own rotations, as the animal moves about a separate polygon object. I would like the arrows/lines to be set at a fixed default length (let's say 10 'units'), except when they intersect the boundary/edges of the polygon. When an arrow would intersect, I would like to automatically adjust the length such that the tip of the intersecting arrow stops at the boundary. In Part 3 of my sample figure, you can see that when the arrows 'intersect' or contact the red polygon boundary, their length is capped at the edge of the boundary.
Main Question: Is is possible to set the length of the arrows in this dynamic way? Would it be recommended to write a statement that checks if an arrow intersects the polygon and instead change the endpoint position to the intersection coordinate?

Accepted Answer

Mike Croucher
Mike Croucher on 18 Sep 2024
Edited: Mike Croucher on 18 Sep 2024
The polyxpoly function is what you need
polygon_x = [-20 20 20 -20 -20];
polygon_y = [-50 -50 50 50 -50];
origin = [-50, 0];
% Define arrow parameters
num_arrows = 5; % Number of arrows to be drawn
arrow_length_default = 50; % Default arrow length
angle_offset = 10; % Angle offset between arrows (degrees)
% Calculate arrow directions (evenly spaced by angle_offset)
angles = linspace(-30, 30, num_arrows); % Adjust angle range as necessary
% Convert angles to radians for trigonometry
angles_rad = deg2rad(angles);
figure;
hold on;
axis equal;
plot([polygon_x polygon_x(1)], [polygon_y polygon_y(1)], 'r'); % Plot polygon
% Function to calculate intersection of line with polygon
for i = 1:num_arrows
% Calculate default arrow endpoint assuming no intersection
arrow_end_x = origin(1) + arrow_length_default * cos(angles_rad(i));
arrow_end_y = origin(2) + arrow_length_default * sin(angles_rad(i));
% Check for intersection between the arrow and the polygon
[xi, yi] = polyxpoly([origin(1) arrow_end_x], [origin(2) arrow_end_y], polygon_x, polygon_y);
if ~isempty(xi)
% If intersection occurs, shorten the arrow to stop at the boundary
arrow_end_x = xi(1);
arrow_end_y = yi(1);
end
% Plot the arrow
quiver(origin(1), origin(2), arrow_end_x - origin(1), arrow_end_y - origin(2), 0, 'b', 'LineWidth', 2);
end
%plot the oirigin
plot(origin(1), origin(2), 'ro', 'MarkerSize', 15, 'MarkerFaceColor', 'r');
  2 Comments
Lochlan
Lochlan on 19 Sep 2024
Thanks for the quick response, Mike.
I ran into the issue here that if the origin gets close enough to the polygon such that the arrows would intersect another boundary of the polygon, the tip gets placed there instead.
origin = [-30 10]
When I look at the intersections, the order is:
xi = [20; -20]
yi = [10; 10]
So, would it make sense for me to check if there are multiple intersections, and then set the endpoint as [xi(end) yi(end)]? I just want to make sure I'm thinking about this correctly
Mike Croucher
Mike Croucher on 19 Sep 2024
If there are multiple intersections, choose the closest one. Something like
if ~isempty(xi)
% If multiple intersection points, choose the closest one to the origin
distances = sqrt((xi - origin(1)).^2 + (yi - origin(2)).^2);
[~, idx] = min(distances); % Find the index of the closest intersection
arrow_end_x = xi(idx);
arrow_end_y = yi(idx);
end

Sign in to comment.

More Answers (1)

Matt J
Matt J on 18 Sep 2024
Edited: Matt J on 18 Sep 2024
An alternative to Mike's approach using linexlines2D() from the File Exchange,
is below:
polygon= polyshape([-20 20 20 -20 -20] , [-50 -50 50 50 -50]);
origin = [-50, 0];
% Define arrow parameters
num_arrows = 5; % Number of arrows to be drawn
arrow_length_default = 50; % Default arrow length
angle_offset = 10; % Angle offset between arrows (degrees)
% Calculate arrow directions (evenly spaced by angle_offset)
angles = linspace(-30, 30, num_arrows)'; % Adjust angle range as necessary
% Calculate intersection(s) of line with polygon and shorten arrows
ArrowStart = repmat(origin, num_arrows,1);
ArrowEnd= ArrowStart + arrow_length_default*[cosd(angles), sind(angles)];
for i = 1:num_arrows
I = linexlines2D(polygon,ArrowStart(i,:),ArrowEnd(i,:));
if ~isnan(I(:,1))
ArrowEnd(i,:)=I(:,1);
end
end
Directions=ArrowEnd-ArrowStart;
%Plot everything
hold on;
plot(polygon, 'FaceColor','none','EdgeColor','r'); axis equal;
plot(origin(1), origin(2), 'ro', 'MarkerSize', 15, 'MarkerFaceColor', 'r');
quiver(ArrowStart(:,1), ArrowStart(:,2),Directions(:,1), Directions(:,2),0, 'b', 'LineWidth', 2);
hold off
  3 Comments
Matt J
Matt J on 19 Sep 2024
That's fine. Just accept whichever of them you ended up most closely adapting.
Mike Croucher
Mike Croucher on 19 Sep 2024
Indeed, all is fair in love and accepting an aswer :)
I honestly didn't know about either of these functions until I investigated your problem so you've already done me a favour by asking the question :)

Sign in to comment.

Categories

Find more on Elementary Polygons in Help Center and File Exchange

Community Treasure Hunt

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

Start Hunting!