Collision issues in matlab - matlab

I'm trying to write a program that bounces a square around the environment.
It should be changing it's velocity when it collides with the 'walls' and the central square.
I am fairly sure I am close to getting it right but there seems to be a real issue with the collision detection which I can't figure out.
Would greatly appreciate anyone having a look and giving some pointers.
fps = 40;
dt = 1/fps;
tmax = 10;
t = 0;
theta = 0;
dtheta = 1/20;
p = transpose([20,20,1]);
v = transpose([50,25,1]);
Vobj = transpose([2, -2, 1; 2,2,1; -2, 2, 1; -2, -2, 1]);
Vobj2 = transpose([60,60,1; 60,40,1; 40, 40, 1; 40, 60, 1]);
while t < tmax
x = p + t*v;
if x(1)<= 5 || x(1) >= 95
v(1)=-v(1);
elseif x(2)<= 5 || x(2) >= 95
v(2)=-v(2);
elseif ((40<=x(1)) && (x(1)<= 60)) && ((x(2) == 40) || (x(2) == 60))
v(2)=-v(2);
elseif ((40<=x(2)) && (x(2)<= 60)) && ((x(1) == 40) || (x(1) == 60))
v(1)=-v(1);
end
p = x;
% Transformations
T = [1,0,p(1);0,1,p(2);0,0,1];
S = [1+0.2*sin(2*t),0,0; 0,1+0.2*sin(2*t),0;0,0,1];
R = [cos(theta),sin(theta),0 ;-sin(theta),cos(theta),0;0,0,1];
L = R*S*T;
% Application of Transformations
V = L*Vobj;
fill(V(1,:),V(2,:),'r')
hold on
fill(Vobj2(1,:),Vobj2(2,:),'g')
axis([0,100,0,100])
hold off
shg;
pause(0.1);
% Updates
t = t + dt;
theta = theta + dtheta;
end

There are a couple things not entirely correct.
Your numerical integration should multiply with dt instead of t, if you are using a forward Euler method.
You are now checking for collision at the center of the moving block, but perhaps you actually want to check for collision of the corners of the block. If that's the case, you could loop over the coordinates of the corner of the block, and check them individually with the working collision detection if statement. So instead of checking once, do the check four times for the corners. Or even multiple times, if add points by interpolating the edges of the block.
The transformation of the block is incorrect. The location of V is not centered about the position p. If you try to simplify the problem, and just take the center of the block, you will see that the collision detection is in fact working.
fps = 40;
dt = 1/fps;
tmax = 10;
t = 0;
theta = 0;
dtheta = 1/20;
p = transpose([20,20,1]);
v = transpose([50,25,1]);
Vobj2 = transpose([60,60,1; 60,40,1; 40, 40, 1; 40, 60, 1]);
while t < tmax
x = p + dt*v;
if x(1)<= 5 || x(1) >= 95
v(1)=-v(1);
elseif x(2)<= 5 || x(2) >= 95
v(2)=-v(2);
elseif ((40<=x(1)) && (x(1)<= 60)) && ((x(2) == 40) || (x(2) == 60))
v(2)=-v(2);
elseif ((40<=x(2)) && (x(2)<= 60)) && ((x(1) == 40) || (x(1) == 60))
v(1)=-v(1);
end
p = x;
% plot the center of the block
scatter(p(1),p(2))
% plot the obstacle
hold on
fill(Vobj2(1,:),Vobj2(2,:),'g')
axis([0,100,0,100])
hold off
shg;
pause(0.1);
% Updates
t = t + dt;
theta = theta + dtheta;
end
Try to fix your transformation, by just applying some angle and translation, to a point where you know where your corners should end up (for example 90 degree rotation, or translation of a known value). This way you can check if the transformation is correct. Then incorporate this again in the simulation.

Related

How do I properly reverse particle velocities after a collision?

So I'm trying to write a program that simulates the movement of gas particles in a sealed environment for an undergrad project (this is my first project so I'm pretty new to coding as a whole). My issue comes down to particle-particle collisions specifically. I have an if statement within an overarching while statement that reverses either the x components or y components of the colliding particles. When I run the script however, particles that should simply collide and have their velocities reversed end up stuttering back and forth. I think what's happening is the separation distance between collided particles after their velocities have been reversed is still below the initial value for collision so the velocities are reversed again. Kinda don't want this to happen and I've been stuck on it for a while. I just want the velocities to be reversed once until a new collision occurs. Collision handling can be found at the bottom of my script. Any advice on this would be greatly appreciated.
clc; clear; close all;
f = figure;
ax = axes('XLim', [0 10], 'YLim', [0 10]);
numberOfParticles = 30;
[particles, particlePositions, particleVelocities] = initializeNewParticle(numberOfParticles);
particleMotion(particles, particleVelocities, particlePositions);
%instantiates N particles
function [particles, particlePositions, particleVelocities] = initializeNewParticle(numberOfParticles)
speedMultiplier = 0.05;
for i = 1:numberOfParticles
particlePositionX(i) = 10*rand;
particlePositionY(i) = 10*rand;
particleVelocityX(i) = speedMultiplier * (-1 + 2*rand);
particleVelocityY(i) = speedMultiplier * (-1 + 2*rand);
particles(i) = line(particlePositionX(i), particlePositionY(i), 'marker', 'o', 'color', 'k', 'markersize', 15);
end
particlePositions = [particlePositionX; particlePositionY];
particleVelocities = [particleVelocityX; particleVelocityY];
end
%initial velocities & collision handling
function [] = particleMotion(particles, particleVelocities, particlePositions)
tic;
while toc < 10
%checks if particles are within bounds of figure
if any(particlePositions(:) < 0) || any(particlePositions(:) > 10)
%creates array that converts elements to 1 if particles out of bounds
outOfBounds = particlePositions < 0 | particlePositions > 10;
%finds index position of non-zero elements
indexPos = outOfBounds(:) > 0;
%uses index to invert velocities of particles out of bounds
particleVelocities(indexPos) = -particleVelocities(indexPos);
end
particlePositions = particlePositions + particleVelocities;
set(particles, 'XData', particlePositions(1,:),'YData', particlePositions(2,:), "LineStyle", "none");
%determines all separations from i particle to all N particles
for i = 1 : length(particles)
currentParticleXPos = get(particles(i), 'XData');
targetParticlesXPos(i, :) = particlePositions(1,:) - particlePositions(1,i);
currentParticleYPos = get(particles(i), 'YData');
targetParticlesYPos(i,:) = particlePositions(2,:) - particlePositions(2,i);
distanceBetweenParticles(i,:) = sqrt(targetParticlesXPos(i,:).^2 + targetParticlesYPos(i,:).^2);
end
%collision check and handling
if find(distanceBetweenParticles >= 0 & distanceBetweenParticles <= 0.5)
collisionCoordinates = triu((distanceBetweenParticles < 0.5 & distanceBetweenParticles > 0));
linearCollisionIndexValue = find(collisionCoordinates == 1);
[particle1,particle2] = ind2sub(size(collisionCoordinates), linearCollisionIndexValue);
particleVelocityX1 = particleVelocities(1,particle1);
particleVelocityX2 = particleVelocities(1,particle2);
particleVelocityY1 = particleVelocities(2,particle1);
particleVelocityY2 = particleVelocities(2,particle2);
if ~isequal(sign(particleVelocityX1), sign(particleVelocityX2))
particleVelocities(1,particle1) = -particleVelocityX1;
particleVelocities(1,particle2) = -particleVelocityX2;
elseif ~isequal(sign(particleVelocityY1), sign(particleVelocityY2))
particleVelocities(2,particle1) = -particleVelocityY1;
particleVelocities(2,particle2) = -particleVelocityY2;
end
end
pause(0.01);
end
end

Plotting animation results in blank graph

I have written this code and it will not plot. I run it with a 0:.5:100 and get a blank graph in Matlab. As well this is a inclined plane friction problem. I need to create an animation in Matlab that shows the blocks sliding based on inputs.
%Numerical Project Code 1
%mass ratio input
mratio = input('Enter the Mass Ratio(m/M): ');
%angle input
theta = input('Enter Angle in Degrees Between 0 and 90: ');
%static coeff input
mus = input('Enter Coefficient of Static Friction: ');
%kinetic coeff input
muk = input('Enter Coefficient of Kinetic Friction: ');
%constants
g = 9.81;
%interface formating
disp('--------------------------------------------');
disp('All Friction Forces are given in terms of the mass on the slope (M)');
%Loops
%NETUP
if mratio > sind(theta)
%static only
if mratio <= (sind(theta) + (mus*cosd(theta)))
ff = g*(mratio - sind(theta));
fprintf("Friction Force = %f M Newtons\n",ff);
fprintf("The Direction of the Friction Force is down the slope and the block not moving.\n")
%kinetic only
else
ff = muk * g * cosd(theta);
fprintf("Friction Force = %f M Newtons\n",ff);
fprintf("The Direction of the Friction Force is down the slope and the block is sliding up the slope.\n");
end
%NETDOWN
elseif mratio < sind(theta)
%static only
if sind(theta) <= (mratio + (mus*cosd(theta)))
ff = g*(sind(theta) - mratio);
fprintf("Friciton Force = %f M Newtons\n",ff);
fprintf("The Direction of the Friction Force is up the slope and the block is not moving.\n")
%kinetic only
else
ff = muk * g * cosd(theta);
fprintf("Friction Force = %f M Newtons\n",ff);
fprintf("The Direction of the Friction Force is up the slope and the block is sliding down the slope.\n");
end
%NETZERO
else
fprintf("Friction Force = 0 Newtons\n");
end
%graph
for i = 0:0.01:1
mratiog = i;
if mratiog > sind(theta)
if mratiog <= (sind(theta) + (mus*cosd(theta)))
ffg = g*(mratiog - sind(theta));
else
ffg = muk * g * cosd(theta);
end
elseif mratiog < sind(theta)
if sind(theta) <= (mratiog + (mus*cosd(theta)))
ffg = g*(sind(theta) - mratiog);
else
ffg = muk * g * cosd(theta);
end
else
ffg = 0;
end
plot (ffg,mratio, 'r:')
end
In addition to Erik's comments I see two other issues:
the plot changes its axis with each new iteration, so you can not see
the animation properly. It would work better if you calculate all
your points before plotting them. Then you can use min and max of data
dimensions to set axis limits. If you don't use 'hold on' you need to set the limits in each iteration.
you need to use 'pause' to slow down the
animation. otherwise you see only the last data point.
Here is a working example of the 'graph' part:
%graph
i_arr = 0:0.01:1;
n = numel(i_arr);
ffg_mratio_arr = zeros(n, 2);
for i = 1:n
mratiog = i_arr(i);
if mratiog > sind(theta)
if mratiog <= (sind(theta) + (mus*cosd(theta)))
ffg = g*(mratiog - sind(theta));
else
ffg = muk * g * cosd(theta);
end
elseif mratiog < sind(theta)
if sind(theta) <= (mratiog + (mus*cosd(theta)))
ffg = g*(sind(theta) - mratiog);
else
ffg = muk * g * cosd(theta);
end
else
ffg = 0;
end
ffg_mratio_arr(i,:) = [ffg mratio];
end
% calculating the axis limits
x_min = min(ffg_mratio_arr(:, 1))-1;
x_max = max(ffg_mratio_arr(:, 1))+1;
y_min = min(ffg_mratio_arr(:, 2))-1;
y_max = max(ffg_mratio_arr(:, 2))+1;
% animation
figure;
for i = 1:n
plot (ffg_mratio_arr(i,1), ffg_mratio_arr(i,2), 'ro');
xlim([x_min x_max]);
ylim([y_min y_max]);
pause(0.01);
end

How can I use an if-else statement to select elements in different matrices?

I have been stuck on the following issue for days and would appreciate any help. I have 2 different matrices, A and B. I have a starting x and y position (xpos, ypos), and want to first, identify whether there is an element within a certain range that has a value of "0" in the matrix B. If there is an element of such kind, I want the x and y position of this element to be the new x and y position. If there aren't any elements with a value of "0", I want to select the element with the highest value within a certain range from the starting positions in matrix A. The position and this element then becomes the new x and y position. I have the following code:
for i=1:5
range=5
xpos=100
ypos=100
xstart=xpos-range
ystart=ypos-range
for gg=1:10; %double the range
for hh=1:10;
if ystart+gg <= 652 && ystart+gg>0 && xstart+hh <= 653 && xstart+hh> 0 && B(xstart+hh,ystart+gg)== 0;
xpos = xstart+hh %this becomes the new xposition
ypos = ystart+gg
else
if ystart+gg <= 652 && ystart+gg >0 && xstart+hh <= 653 && xstart + hh >0 && B(xstart+hh,ystart+gg)~= 0;
if ystart+gg <= 652 && ystart +gg>0 && xstart+hh <= 653 && xstart+hh>0 && A(ystart + gg, xstart +hh) >= 0.0;
maxtreecover = A(ystart + gg, xstart + hh)
xpos = xstart + gg %this becomes the new xpos
ypos = ystart + hh %this becomes the new ypos
end
end
end
end
end
end
The problem with this is that it does not search ALL of the elements within the range for a "0" (in the B matrix) before moving into searching the A matrix. How can I modify this code to reflect what I intend it to do?
Here is a re-write of your code which avoids the double loop. I assume all of your if conditions are so that the index remains valid (between 1 and the size of the matrix), but they wouldn't work in their current form because the indexing is done in the same line anyway! I've also addressed this using min and max conditions on xpos and ypos.
This solution gets the submatrix which spans +/- the range from your xpos and ypos. Then it evaluates the 2 conditions you describe within that submatrix:
Get the position of the first element of B which is zero
Get the position of the maximum element of A
Code is commented for details:
% Create matrices A and B
n = 100; % Size of matrices
A = rand(n);
B = rand(n);
range = 5; % "radius" of submatrix
xpos = 50; ypos = 50; % start point
% Iterate
for ii = 1:5
% Get submatrices around xpos and ypos, using min and max to ensure valid
subx = max(1,xpos-range):min(n,xpos+range);
suby = max(1,ypos-range):min(n,ypos+range);
A_sub = A(suby, subx);
B_sub = B(suby, subx);
% Find first 0 in submatrix of B, re-assign xpos/ypos if so
[yidx, xidx] = find(B_sub == 0, 1);
if ~isempty(xidx)
xpos = subx(xidx);
ypos = suby(yidx);
else
% Get max from submatrix of A
[~, idx] = max(A_sub(:));
[xidx, yidx] = meshgrid(subx, suby);
xpos = xidx(idx);
ypos = yidx(idx);
end
end

How to plot temporal frequency as a function of spatial frequency from a MATLAB FFT2 output of a time-space image?

I'm a little bit of a FFT amateur (not trained in physics!) so I'm hoping someone around here has the expertise to give me a hint as to how I should go about doing this next step.
So I'm trying to generate the power spectra of time-space pattern via MATLAB from a visual stimulus as shown below. This is basically a plot of the movement trajectory of 10 dots (sine wave) within a time frame of 2 seconds with the distance labelled in degrees. (200x160 matrix - 10ms per frame on the y-axis and 0.1 degrees per frame on the x-axis).
I have done fft2, fftshift and a log transform on this stimulus and the resulting output is this.
First off, I am a little confused as to what this transformed image exactly represent? Is the centre displaying the high or low frequency data of the stimulus? And what do the x and y-axis now represents in this transformed plot?
I am actually hoping to convert the transformed image such that the y axis reflects temporal frequency between -30 to 30Hz and the x axis, spatial frequency between -30deg/cycle to 30deg/cycle. Perhaps someone could give me an idea of how I should go about doing this? (ie. is there a MATLAB function that is able to handle this sort of conversion?)
A sample of the codes to reproduce the plots are:-
function STotal = playINTOdotty (varargin)
deg_speed = 15.35; %dva/s
nr_of_dots = 10;
motion_type = 'const';
%Number of iterations
runs = 1;
stim_x = 160; %1 frame = 0.1d
stim_t = 200; %1 frame = 10ms
sin_cycle_dur = 80; %80;
max_speed = deg_speed/5.15; %This is very, very abstract. Basically plot out stim image and you'll see 5.15 is the best value.
sd = (sin_cycle_dur/2)/6;
mu = (sin_cycle_dur/2)/2;
sineTOTAL = 0;
counter = 1;
if nargin > 0
nr_of_dots = varargin{1};
end
if nargin > 1
deg_speed = varargin{2};
end
if nargin > 2
motion_type = varargin{3};
end
thisFTTOTAL = zeros(stim_t,stim_x);
stimTOTAL = zeros(stim_t,stim_x);
% initialize stim
stim = zeros(stim_t, stim_x) + .5;
%% define random dots for simulation/generation of position (before scaling to mean speed)
start_dot_pos = round(rand(1,nr_of_dots) .* stim_x);
dot_pos = zeros(stim_t, nr_of_dots);
dot_pos(1,:) = start_dot_pos;
%dot_pos(1,:) = 0;
dot_pos_sim = zeros(stim_t, nr_of_dots);
dot_pos_sim(1,:) = start_dot_pos;
%dot_pos_sim(1,:) = 0;
%% define random dots for neutral condition. dot_pos1 is for Sine and dot_pos2 for Constant
start_dot_pos1 = round(rand(1,nr_of_dots/2) .* stim_x);
dot_pos1 = zeros(stim_t, nr_of_dots/2);
dot_pos1(1,:) = start_dot_pos1;
dot_pos_sim1 = zeros(stim_t, nr_of_dots/2);
dot_pos_sim1(1,:) = start_dot_pos1;
start_dot_pos2 = round(rand(1,nr_of_dots/2) .* stim_x);
dot_pos2 = zeros(stim_t, nr_of_dots/2);
dot_pos2(1,:) = start_dot_pos2;
dot_pos_sim2 = zeros(stim_t, nr_of_dots/2);
dot_pos_sim2(1,:) = start_dot_pos2;
%% Mean of Constant speed
CTotal = max_speed*sin_cycle_dur;
Cmean = max_speed/2;
for q = 1:runs
%% Calculate position list to allow calculation of Gmean and Smean for scaling
for t = 2:stim_t
switch motion_type
case 'sine'
sine_speed = max_speed .* sin((t-1) / sin_cycle_dur *2*pi); %Sine formula
sineTOTAL = sineTOTAL + abs(sine_speed); %Add all sine generated values from Sine formula to get an overall total for mean calculation
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + max_speed .* sin((t-1) / sin_cycle_dur *2*pi); %Sine simulated matrix (before scaling)
case 'gaussian'
x = linspace((mu-4*sd),(mu+4*sd),sin_cycle_dur/2); %Gaussian formula part 1
y = 1/(2*pi*sd)*exp(-(x-mu).^2/(2*sd^2)); %Gaussian formula part 2
scalefactor = max_speed / (1/(2*pi*sd));
y = y*scalefactor;
y1 = y;
y2 = -y;
yTOTAL = [y,y2,y,y2,y,y2,y,y2,y,y2]; %y and y2 forms a full gaussian cycle. Two cycles here (80+80 frames) + 1 (Because stim_t is 161)
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + yTOTAL(:,t); %Gaussian simulated matrix (before scaling)
case 'const'
if t > 10 && t <= 30 %This is hard coding at its best. Need to change this some time. Basically definding dot positions based on the specified stim_t range.
con_speed = max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
elseif t > 50 && t <= 70
con_speed = -max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
elseif t > 90 && t <= 110
con_speed = max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
elseif t > 130 && t <= 150
con_speed = -max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
elseif t > 170 && t <= 190
con_speed = max_speed;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
else
con_speed = 0;
dot_pos_sim(t,:) = dot_pos_sim(t-1,:) + con_speed;
end
case 'neutral' %Fusion of Sine + Const codes (similar to above) to generate neutral.
sine_speed = max_speed .* sin((t-1) / sin_cycle_dur *2*pi);
sineTOTAL = sineTOTAL + abs(sine_speed);
dot_pos_sim1(t,:) = dot_pos_sim1(t-1,:) + max_speed .* sin((t-1) / sin_cycle_dur *2*pi);
if t > 10 && t <= 30
con_speed = max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
elseif t > 50 && t <= 70
con_speed = -max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
elseif t > 90 && t <= 110
con_speed = max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
elseif t > 130 && t <= 150
con_speed = -max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
elseif t > 170 && t <= 190
con_speed = max_speed;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
else
con_speed = 0;
dot_pos_sim2(t,:) = dot_pos_sim2(t-1,:) + con_speed;
end
end
end
yT = 0; %counter to sum up all of gaussian's speed to form a total from all frames
%% Calculate means
for y = 1:stim_t
switch motion_type
case 'sine'
Smean = sineTOTAL/stim_t;
case 'gaussian'
yT = sum(y1) + sum(abs(y2)) * 5; %5 cycles of y,y2
Gmean = yT/stim_t;
case 'neutral'
Smean = sineTOTAL/stim_t;
end
end
%% Scale positions to Cmean
for t = 1:stim_t
switch motion_type
case 'sine'
dot_pos(t,:) = dot_pos_sim(t,:) .* (Cmean/Smean);
case 'gaussian'
dot_pos(t,:) = dot_pos_sim(t,:) .* (Cmean/Gmean);
case 'const'
dot_pos(t,:) = dot_pos_sim(t,:);
case 'neutral'
dot_pos1(t,:) = dot_pos_sim1(t,:) .* (Cmean/Smean); %For Sine
dot_pos2(t,:) = dot_pos_sim2(t,:); %For Constant
end
end
%rounding
dot_pos = round(dot_pos);
dot_pos1 = round(dot_pos1);
dot_pos2 = round(dot_pos2);
%wrapping
dot_pos = mod(dot_pos,stim_x)+1;
dot_pos1 = mod(dot_pos1,stim_x)+1;
dot_pos2 = mod(dot_pos2,stim_x)+1;
%Dots given a value of 1 to the 0.5 stim matrix
for t = 1:stim_t
switch motion_type
case 'sine'
stim(t,dot_pos(t,:)) = 1;
case 'gaussian'
stim(t,dot_pos(t,:)) = 1;
case 'const'
stim(t,dot_pos(t,:)) = 1;
case 'neutral'
stim(t,dot_pos1(t,:)) = 1;
stim(t,dot_pos2(t,:)) = 1;
end
end
F = fft2(stim);
S = abs(F);
Fc = (fftshift(F));
S2 = abs(Fc); %If without log transform within iteration
%S2 = log(1+abs(Fc)); %Log transform within iteration
thisFTTOTAL = thisFTTOTAL + S2;
end
thisFTTOTAL = thisFTTOTAL/runs;
S2 = log(1+abs(thisFTTOTAL)); %If without log transform within iteration
%S2 = thisFTTOTAL; %If log transform within iteration
figure (1)
colormap('gray');
x=linspace(0,16,5);
y=linspace(0,2,10);
imagesc(x,y,stim);
xlabel('degrees');
ylabel('seconds');
xlim([0 16])
figure (2)
colormap('gray');
imagesc(S2);
**EDIT : Trying to recreate something along the lines of the following, where I only want the power-spectra plots within the range of -30 to 30 cycle/degree and -30 to 30Hz:-
Just to have an idea on how the fft works on a 2D space,you can have a look here and,more useful, here.
In other words, if you do an 2D fft of an image like this (please note that a row it is just a sin function, really easy to implement in matlab):
corresponds to:
Now, if you build a similar image but with a different period you will obtain a similar result but the point in the 2D fft will be closer. For example:
where the fft will be:
The orientation of the sinusoid correlates with the orientation of the peaks in the Fourier image relative to the central DC point. In this case a tilted sinusoidal pattern creates a tilted pair of peaks in the Fourier image:
You can try to combine the different image and observe the different pattern in the 2Dfft:
I strongly recommend you to have a look on the related link at the beginnig of the answer.

Matlab - Failures of function to detect collisions between line segments and circle

Many questions exist already covering how to detect collisions between a line segment and a circle.
In my code, I am using Matlab's linecirc function, then comparing the intersection points it returns with the ends of my line segments, to check that the points are within the line (linecirc assumes an infinite line, which I don't have/want).
Copying and adding some sprintf calls to the linecirc function shows that it is calculating points as intended. These seem to be being lost by my function.
My code is below:
function cutCount = getCutCountHex(R_g, centre)
clf;
cutCount = 0;
% Generate a hex grid
Dg = R_g*2;
L_b = 62;
range = L_b*8;
dx = Dg*cosd(30);
dy = 3*R_g;
xMax = ceil(range/dx); yMax = ceil(range/dy);
d1 = #(xc, yc) [dx*xc dy*yc];
d2 = #(xc, yc) [dx*(xc+0.5) dy*(yc+0.5)];
centres = zeros((xMax*yMax),2);
count = 1;
for yc = 0:yMax-1
for xc = 0:xMax-1
centres(count,:) = d1(xc, yc);
count = count + 1;
centres(count, :) = d2(xc, yc);
count = count + 1;
end
end
for i=1:size(centres,1)
centres(i,:) = centres(i,:) - [xMax/2 * dx, yMax/2 * dy];
end
hold on
axis equal
% Get counter for intersected lines
[VertexX, VertexY] = voronoi(centres(:,1), centres(:,2));
numLines = size(VertexX, 2);
for lc = 1:numLines
segStartPt = [VertexX(1,lc) VertexY(1,lc)];
segEndPt = [VertexX(2,lc) VertexY(2,lc)];
slope = (segEndPt(2) - segStartPt(2))/(segEndPt(1) - segStartPt(1));
intercept = segEndPt(2) - (slope*segEndPt(1));
testSlope = isinf(slope);
if (testSlope(1)==1)
% Pass the x-axis intercept instead
intercept = segStartPt(1);
end
[xInterceptionPoints, yInterceptionPoints] = ...
linecirc(slope, intercept, centre(1), centre(2), L_b);
testArr = isnan(xInterceptionPoints);
if (testArr(1) == 0) % Line intersects. Line segment may not.
interceptionPoint1 = [xInterceptionPoints(1), yInterceptionPoints(1)];
interceptionPoint2 = [xInterceptionPoints(2), yInterceptionPoints(2)];
% Test if first intersection is on the line segment
p1OnSeg = onSeg(segStartPt, segEndPt, interceptionPoint1);
p2OnSeg = onSeg(segStartPt, segEndPt, interceptionPoint2);
if (p1OnSeg == 1)
cutCount = cutCount + 1;
scatter(interceptionPoint1(1), interceptionPoint1(2), 60, 'MarkerFaceColor', 'r', 'MarkerEdgeColor', 'k');
end
% Test if second intersection point is on the line segment
if (interceptionPoint1(1) ~= interceptionPoint2(1) || interceptionPoint1(2) ~= interceptionPoint2(2)) % Don't double count touching points
if (p2OnSeg == 1)
cutCount = cutCount + 1;
scatter(interceptionPoint2(1), interceptionPoint2(2), 60, 'MarkerFaceColor', 'r', 'MarkerEdgeColor', 'k');
end
end
end
end
% Plot circle
viscircles(centre, L_b, 'EdgeColor', 'b');
H = voronoi(centres(:,1), centres(:,2));
for i = 1:size(H)
set(H(i), 'Color', 'g');
end
end
function boolVal = onSeg(segStart, segEnd, testPoint)
bvX = isBetweenOrEq(segStart(1), segEnd(1), testPoint(1));
bvY = isBetweenOrEq(segStart(2), segEnd(2), testPoint(2));
if (bvX == 1 && bvY == 1)
boolVal = 1;
else
boolVal = 0;
end
end
function boolVal = isBetweenOrEq(end1, end2, test)
if ((test <= end1 && test >= end2) || (test >= end1 && test <= end2))
boolVal = 1;
else
boolVal = 0;
end
end
It creates a hexagonal grid, then calculates the number of crossings between a circle drawn with a fixed radius (62 in this case) and a specified centre.
The scatter calls show the locations that the function counts.
Implementing sprintf calls within the if(p1OnSeg == 1) block indicates that my function has chosen fictitious intersection points (although it then deals with them correctly)
if (interceptionPoint1(1) > -26 && interceptionPoint1(1) < -25)
sprintf('p1 = [%f, %f]. Vx = [%f, %f], Vy = [%f, %f].\nxint = [%f, %f], yint = [%f, %f]',...
interceptionPoint1(1), interceptionPoint1(2), VertexX(1,lc), VertexX(2,lc), VertexY(1,lc), VertexY(2,lc),...
xInterceptionPoints(1), xInterceptionPoints(2), yInterceptionPoints(1), yInterceptionPoints(2))
end
Outputs
p1 = [-25.980762, 0.000000]. Vx = [-25.980762, -25.980762], Vy = [-15.000000, 15.000000].
xint = [-25.980762, -25.980762], yint = [0.000000, 0.000000]
A picture shows the strange points.
Sorry for the very long question but - why are these being detected. They don't lie on the circle (displaying values within a mylinecirc function detects the intersections at around (-25, 55) and (-25, -55) or so (as an infinite line would expect).
Moving the circle can remove these points, but sometimes this leads to other problems with detection. What's the deal?
Edit: Rotating my grid pattern created by [Vx, Vy] = voronoi(...) and then removing points with very large values (ie those going close to infinity etc) appears to have fixed this problem. The removal of 'large' value points seems to be necessary to avoid NaN values appearing in 'slope' and 'intercept'. My guess is this is related to a possible slight inclination due to rotation, coupled with then overflow of the expected intercept.
Example code added is below. I also edited in Jan de Gier's code, but that made no difference to the problem and so is not changed in the question code.
%Rotate slightly
RotAngle = 8;
RotMat = [cosd(RotAngle), -sind(RotAngle); sind(RotAngle), cosd(RotAngle)];
for i=1:size(centres,1)
centres(i,:) = centres(i,:) - [floor(xMax/2) * dx, floor(yMax/2) * dy]; %Translation
centres(i,:) = ( RotMat * centres(i,:)' ); %Rotation
end
% Get counter for intersected lines
[VertexX, VertexY] = voronoi(centres(:,1), centres(:,2));
% Filter vertices
numLines = size(VertexX, 2);
newVx = [];
newVy = [];
for lc = 1:numLines
testVec = [VertexX(:,lc) VertexY(:,lc)];
if ~any(abs(testVec) > range*1.5)
newVx = [newVx; VertexX(:,lc)'];
newVy = [newVy; VertexY(:,lc)'];
end
end
VertexX = newVx';
VertexY = newVy';
numLines = size(VertexX, 2);
Still appreciating answers or suggestions to clear up why this is/was occuring.
Example values that cause this are getCutCountHex(30, [0,0]) and ...(35, [0,0])
I cant reproduce your problem, but the thing I did notice is that your onSeg() function might be wrong: it returns true if the testpoint lies in the rectangle with two of the four corner points being segStart and segEnd.
A function that returns true iff a point is on (or more accurate: close enough to) the line segment (segStart,segEnd) could be:
function boolVal = onSeg(segStart, segEnd, testPoint)
tolerance = .5;
AB = sqrt((segEnd(1)-segStart(1))*(segEnd(1)-segStart(1))+(segEnd(2)-segStart(2))*(segEnd(2)-segStart(2)));
AP = sqrt((testPoint(1)-segEnd(1))*(testPoint(1)-segEnd(1))+(testPoint(2)-segEnd(2))*(testPoint(2)-segEnd(2)));
PB = sqrt((segStart(1)-testPoint(1))*(segStart(1)-testPoint(1))+(segStart(2)-testPoint(2))*(segStart(2)-testPoint(2)));
boolVal = abs(AB - (AP + PB)) < tolerance;
end
an approach that I found in one of the anwers here: Find if point lays on line segment. I hope that solves your problem.