How do I properly reverse particle velocities after a collision? - matlab

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

Related

Collision issues in 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.

Simulating random walkers which can not collide into each other in Matlab

I have written a code to simulate the motion of circular particles in a 2d box. Whenever they move out of the box, I put them inside the box and near the wall. I want to add the diameter (2R) of particles in the code, which means when the distance between the center of two circles become less than 2R, they separate along the line connecting their centers so that the distance between the centers of the circles becomes equal to 2R.
Could anyone suggest a code to perevent the overlapping of particles?
This is my code in which overlap is not considered:
clear all
close all
l = 224; nn = 800; %number of particles
time = 1000; dd = 1;
x= l*rand(1,nn);
y= l*rand(1,nn);
for t = 1:time;
x= x + rand(1,nn)-0.5* ones(1,nn);
y=y+rand(1,nn)-0.5* ones (1,nn);
index = (x < 0); x(index) = abs(normrnd(0,1,1,nnz(index)));
index = (y < 0); y(index) = abs(normrnd(0,1,1,nnz(index)));
index = (x > l); x(index) = l-abs(normrnd(0,1,1,nnz(index)));
index = (y > l); y(index) = l-abs(normrnd(0,1,1,nnz(index)));
end
Here is some commented code which does what you want. Notably:
psize is some defined particle size for interaction.
point-to-point distances found using pdist2.
points that are too close are moved away from each other by some amount (dp times their current distances, if dp=1/2 then their x and y distances double) until there are no clashes.
See comments for details.
clear; close all;
l = 224; nn = 800; % number of particles
time = 100;
x = l*rand(1,nn); y = l*rand(1,nn);
psize = 2; % Particle size for interaction
dp = 0.1;
figure; hold on; axis([0 l 0 l]);
for t = 1:time;
% Random movement
movement = 2*rand(2,nn)-1;
x = x + movement(1,:);
y = y + movement(2,:);
index = (x < 0); x(index) = abs(normrnd(0,1,1,nnz(index)));
index = (y < 0); y(index) = abs(normrnd(0,1,1,nnz(index)));
index = (x > l); x(index) = l-abs(normrnd(0,1,1,nnz(index)));
index = (y > l); y(index) = l-abs(normrnd(0,1,1,nnz(index)));
% Particle interaction. Loop until there are no clashes. For
% robustness, some max iteration counter should be added!
numclash = 1;
while numclash > 0
dists = pdist2([x;y]', [x;y]'); % Distances between all particles
dists(dists < psize) = NaN; % Those too close are assigned NaN
tooclose = isnan(tril(dists,-1)); % All NaNs identified by logical
[clash1,clash2] = find(tooclose); % Get particles which are clashing
numclash = numel(clash1); % Get number of clashes
% All points where there was a clash, move away from each other
x(clash1) = x(clash1) + (x(clash1)-x(clash2))*dp;
x(clash2) = x(clash2) - (x(clash1)-x(clash2))*dp;
y(clash1) = y(clash1) + (y(clash1)-y(clash2))*dp;
y(clash2) = y(clash2) - (y(clash1)-y(clash2))*dp;
end
% Plot to visualise results. Colour fade from dark to bright green over time
scatter(x,y,'.','markeredgecolor',[0.1,t/time,0.4]);
drawnow;
end
hold off
Result:
Edit:
For a clearer diagram, you could initialise some colour matrix C = rand(nn,3); and plot using
scatter(x,y,[],C*(t/time),'.'); % the (t/time) factor makes it fade from dark to light
This would give each particle a different colour, which also fade from dark to light, rather than just fading from dark to light as before. The result would be something like this:

Random Walk, simplifying the code

I am currently practicing modelling "random walks" and population modelling. I have a working code that works for a 1D random walk, in the future I'd like to add extra dimensions and I think the code I have at the moment would make it more difficult. I need to plot the first two positions before I can start the for loop. Ideally I would like to get rid of that issue and have it start from the first step.
numjumps = 20; %number of steps
R = 0.5; %probability of step to right
more off
prev_position = [0 0]; %first position
x = rand(1); %generate random number 0-1
if x >= R;
step = 1; %take a step to the right
elseif x < R
step = -1; %take a step to the left
end
position = [1 prev_position(2) + step];
close all;
figure;
hold on;
plot([prev_position(1) position(1)], [prev_position(2) position(2)]);
axis([0 numjumps -5 5])
for i = (2:numjumps);
prev_position(i) = position(i-1);
x = rand(1); %generate random number 0-1
if x >= R;
step = 1; %take a step to the right
elseif x < R
step = -1; %take a step to the left
end
position(i) = [prev_position(i) + step]; %add step to position
plot(position);
axis([0 numjumps -5 5])
end
total = sum(position);
mean_position = total/numjumps;
disp(mean_position);
Any help would be greatly appreciated.
You shouldn't use a for loop, just vectorize your code:
numjumps = 20; %number of steps
R = 0.5; %probability of step to right
x = rand (numjumps, 1);
step = 2 * (x >= R) - 1;
position = [0; cumsum(step)];
plot (position)
now it's easy to make it 2D:
numjumps = 200; %number of steps
R = 0.5; %probability of step to right
x = rand (numjumps, 2);
step = 2 * (x >= R) - 1;
position = [0, 0; cumsum(step)];
plot (position(:, 1), position (:, 2))
gives

Finger peak detection using MATLAB

I have to create an algorithm with Matlab that, with a image of a hand, can know the form of the hand by the number of raised fingers and the presence or absence of the thumb. So far, the algorithm is almost complete but I don't know what more I can do that could find the peaks that represents the fingers. We tried a lot of things but nothing works. The idea is to find when there is a sudden increasement but as the pixels are never completely aligned, nothing that we tried worked. Someone has any idea? Here is the code so far.
The image that he is reading is this one:
To know if the finger is relevant or not, we already have an idea that might work... but we need to find the fingers first.
clear all
close all
image=imread('mao2.jpg');
YCBCR = rgb2ycbcr(image);
image=YCBCR;
cb = image(:,:,2);
cr = image(:,:,3);
imagek(:,1) = cb(:);
imagek(:,2) = cr(:);
imagek = double(imagek);
[IDX, C] = kmeans(imagek, 2, 'EmptyAction', 'singleton');
s=size(image);
IDX= uint8(IDX);
C2=round(C);
imageNew = zeros(s(1),s(2));
temp = reshape(IDX, [s(1) s(2)]);
for i = 1 : 1 : s(1)
for j = 1 : 1 : s(2)
imageNew(i,j,:) = C2(temp(i,j));
end
end
imageNew=uint8(imageNew);
[m,n]=size(imageNew);
for i=1:1:m
for j = 1:1:n
if(imageNew(i,j)>=127)
pretobranco(i,j)=0;
else
pretobranco(i,j)=1;
end
end
end
I2=imfill(pretobranco);
imshow(I2);
imwrite(I2, 'mao1trab.jpg');
[m,n]=size(I2);
B=edge(I2);
figure
imshow(B);
hold on;
stats=regionprops(I2,'BoundingBox');
rect=rectangle('position', [stats(1).BoundingBox(1), stats(1).BoundingBox(2), stats(1).BoundingBox(3), stats(1).BoundingBox(4)], 'EdgeColor', 'r');
stats(1).BoundingBox(1)
stats(1).BoundingBox(2)
stats(1).BoundingBox(3)
stats(1).BoundingBox(4)
figure
Bound = B( stats(1).BoundingBox(2): stats(1).BoundingBox(2)+stats(1).BoundingBox(4)-1, stats(1).BoundingBox(1):stats(1).BoundingBox(1)+stats(1).BoundingBox(3)-1);
imshow(Bound)
y1 = round(stats(1).BoundingBox(2))
y2 = round(stats(1).BoundingBox(2)+stats(1).BoundingBox(4)-1)
x1 = round(stats(1).BoundingBox(1))
x2 = round(stats(1).BoundingBox(1)+stats(1).BoundingBox(3)-1)
% Bounding box contida em imagem[M, N].
[M,N] = size(Bound)
vertical=0;
horizontal=0;
if M > N
vertical = 1 %imagem vertical
else
horizontal = 1 %imagem horizontal
end
%Find thumb
MaoLeft = 0;
MaoRight = 0;
nPixelsBrancos = 0;
if vertical==1
for i = x1:1:x2
for j= y1:1:y2
if I2(j,i) == 1
nPixelsBrancos = nPixelsBrancos + 1; %Numero de pixels da mão
end
end
end
for i=x1:1:x1+30
for j=y1:1:y2
if I2(j,i) == 1
MaoLeft = MaoLeft + 1; %Number of pixels of the hand between the 30 first colums
end
end
end
for i=x2-30:1:x2
for j=y1:1:y2
if I2(j,1) == 1
MaoRight = MaoRight + 1; %Number of pixels of the hand between the 30 last colums
end
end
end
TaxaBrancoLeft = MaoLeft/nPixelsBrancos
TaxaBrancoRight = MaoRight/nPixelsBrancos
if TaxaBrancoLeft <= (7/100)
if TaxaBrancoRight <= (7/100)
Thumb = 0 %Thumb in both borders is defined as no Thumb.
else
ThumbEsquerdo = 1 %Thumb on left
end
end
if TaxaBrancoRight <= (7/100) && TaxaBrancoLeft >= (7/100)
ThumbDireito = 1 %Thumb on right
end
end
if horizontal==1
for i = x1:1:x2
for j= y1:y2
if I2(i,j) == 1
nPixelsBrancos = nPixelsBrancos + 1; %Numero de pixels da mão
end
end
end
for i=x1:1:x2
for j=y1:1:y1+30
if I2(i,j) == 1
MaoLeft = MaoLeft + 1; %Numero de pixels da mão entre as 30 primeiras colunas
end
end
end
for i=x1:1:x2
for j=y2-30:1:y2
if I2(j,1) == 1
MaoRight = MaoRight + 1; %Numero de pixels da mão entre as 30 ultimas colunas
end
end
end
TaxaBrancoLeft = MaoLeft/nPixelsBrancos
TaxaBrancoRight = MaoRight/nPixelsBrancos
if TaxaBrancoLeft <= (7/100)
if TaxaBrancoRight <= (7/100)
Thumb = 0 %Polegar nas duas bordas. Definimos como sem polegar.
else
ThumbEsquerdo = 1 %Polegar na borda esquerda
end
end
if TaxaBrancoRight <= (7/100) && TaxaBrancoLeft >= (7/100)
ThumbDireito = 1 %Polegar na borda direita
end
end
figure
imshow(I2);
%detecção da centroid
Ibw = im2bw(I2);
Ilabel = bwlabel(Ibw);
stat = regionprops(Ilabel,'centroid');
figure
imshow(I2); hold on;
for x = 1: numel(stat)
plot(stat(x).Centroid(1),stat(x).Centroid(2),'ro');
end
centroid = [stat(x).Centroid(1) stat(x).Centroid(2)] %coordenadas x e y da centroid
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Seemed like an interesting problem, so I gave it a shot. Basically you start with a Sobel filter to find the edges in your image (after slight denoising). Then clean up the resulting lines, use them to separate regions within your binary mask of the hand, use a watershed transform to find the wrist, some distance transforms to find other landmarks, then remove the palm. What you're left with is separate regions for each finger and thumb. You can count those regions easily enough or find which way they are pointing, or whatever you'd like.
imgURL = 'https://encrypted-tbn2.gstatic.com/imgs?q=tbn:ANd9GcRQsqJtlrOnSbJNTnj35Z0uG9BXsecX2AXn1vV0YDKodq-zSuqnnQ';
imgIn=imread(imgURL);
gaussfilt = fspecial('gaussian', 3, .5); % Blur starting image
blurImg = imfilter(double(img(:,:,1)), gaussfilt);
edgeImg = edge(blurImg, 'sobel'); % Use Sobel edge filter to pick out contours of hand + fingers
% Clean up contours
edgeImg = bwmorph(edgeImg, 'close', 1);
edgeImg = bwmorph(edgeImg, 'thin', Inf);
% Clean up rogue spots in corners
edgeImg([2 end-1], 2) = 0;
edgeImg([2 end-1], end-1) = 0;
% Extend lines to edge of image (correct for 'close' operation above
edgeImg([1 end],:) = edgeImg([2 end-1],:);
edgeImg(:, [1 end]) = edgeImg(:, [2 end-1]);
% Remove all but the longest line
regs = regionprops(edgeImg, 'Area', 'PixelIdxList');
regs(vertcat(regs.Area) ~= max(vertcat(regs.Area))) = [];
lineImg = false(size(edgeImg, 1), size(edgeImg, 2));
lineImg(regs.PixelIdxList) = 1;
fillImg = edgeImg;
% Close in wrist
if any(fillImg(1,:))
fillImg(1,:) = 1;
end
if any(fillImg(end,:))
fillImg(end,:) = 1;
end
if any(fillImg(:,1))
fillImg(:,1) = 1;
end
if any(fillImg(:,end))
fillImg(:,end) = 1;
end
fillImg = imfill(fillImg, 'holes');
fillImg([1 end], :) = 0;
fillImg(:, [1 end]) = 0;
fillImg([1 end],:) = fillImg([2 end-1],:);
fillImg(:, [1 end]) = fillImg(:, [2 end-1]);
% Start segmenting out hand + fingers
handBin = fillImg;
% Set lines in above image to 0 to separate closely-spaced fingers
handBin(lineImg) = 0;
% Erode these lines to make fingers a bit more separate
handBin = bwmorph(handBin, 'erode', 1);
% Segment out just hand (remove wrist)
distImg = bwdist(~handBin);
[cDx, cDy] = find(distImg == max(distImg(:)));
midWrist = distImg;
midWrist = max(midWrist(:)) - midWrist;
midWrist(distImg == 0) = Inf;
wristWatershed = watershed(imerode(midWrist, strel('disk', 10)));
whichRegion = wristWatershed(cDx, cDy);
handBin(wristWatershed ~= whichRegion) = 0;
regs = regionprops(handBin, 'Area', 'PixelIdxList');
regs(vertcat(regs.Area) ~= max(vertcat(regs.Area))) = [];
handOnly = zeros(size(handBin, 1), size(handBin, 2));
handOnly(regs.PixelIdxList) = 1;
% Find radius of circle around palm centroid that excludes wrist and splits
% fingers into separate regions.
% This is estimated as D = 1/3 * [(Centroid->Fingertip) + 2*(Centroid->Wrist)]
% Find Centroid-> Wrist distance
dist2w = wristWatershed ~= whichRegion;
dist2w = bwdist(dist2w);
distToWrist = dist2w(cDx, cDy);
% Find Centroid-> Fingertip distance
dist2FE = zeros(size(handOnly, 1), size(handOnly, 2));
dist2FE(cDx, cDy) = 1;
dist2FE = bwdist(dist2FE).*handOnly;
distToFingerEnd = max(dist2FE(:));
circRad = mean([distToFingerEnd, distToWrist, distToWrist]); % Estimage circle diameter
% Draw circle
X = bsxfun(#plus,(1:size(handOnly, 1))',zeros(1,size(handOnly, 2)));
Y = bsxfun(#plus,(1:size(handOnly, 2)),zeros(size(handOnly, 1),1));
B = sqrt(sum(bsxfun(#minus,cat(3,X,Y),reshape([cDx, cDy],1,1,[])).^2,3))<=circRad;
% Cut out binary mask within circle
handOnly(B) = 0;
% Label separate regions, where each now corresponds to a separate digit
fingerCount = bwlabel(handOnly);
% Display overlay image
figure()
imshow(imgIn)
hold on
overlayImg = imshow(label2rgb(fingerCount, 'jet', 'k'));
set(overlayImg, 'AlphaData', 0.5);
hold off
Results:
http://imgur.com/ySn1fPy

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.