check if two segments on the same circle overlap / intersect - algorithm

Given two circle segments of the same circle: A=[a1, a2] and B=[b1, b2], with:
a1, a2, b1, b2 values in degree between -inf and +inf
a1 <= a2 ; b1 <= b2
a2-a1<=360; b2-b1<=360
How can I find out if these two circle segments overlap?
(i.E. if they intersect or touch in at least one point)
Examples:
A=[ -45°, 45°]; B=[ 10°, 20°] ==> overlap
A=[ -45°, 45°]; B=[ 90°, 180°] ==> no overlap
A=[ -45°, 45°]; B=[ 180°, 360°] ==> overlap
A=[ -405°, -315°]; B=[ 180°, 360°] ==> overlap
A=[-3600°, -3601°]; B=[ 3601°, 3602°] ==> overlap (touching counts as overlap)
A=[ 3600°, 3601°]; B=[-3601°,-3602°] ==> overlap (touching counts as overlap)
A=[ -1°, 1°]; B=[ 3602°, 3603°] ==> no overlap
This looks like a deceptively simple problem but I cannot wrap my head around it.
I currently have a basic idea for a solution which involves splitting each segment into two if it crosses 0°, but I am not sure if that covers all cases, and I was wondering if there is an elegant formula.

As #admaoldak mentioned, normalize the degrees first:
a1_norm = a1 % 360
a2_norm = a2 % 360
b1_norm = b1 % 360
b2_norm = b2 % 360
Now to check if b1 is within (a1,a2),
def intersect(b, as, ae
Intersect = False
If as > ae:
if b >= as or b <= ae:
return True
Else:
if b>=as and b<=ae:
return True
return False
Final answer is:
intersect(b1_norm,a1_norm,a2_norm)||intersect(b2_norm,a1_norm,a2_norm)||
intersect(a1_norm,b1_norm,b2_norm)||intersect(a2_norm,b1_norm,b2_norm)

For an interval [i.X , i.Y] , let's define the normalization i_norm = normalize(i) so that :
1. 0 <= i_norm.X < 360
2. i_norm.X <=i_norm.Y
then we define another operation i_slide = slide(i) so that :
1. i_slide.X = i.X + 360
2. i_slide.Y = i.Y + 360
we can prove that,
for your input A and B , A circle-overlaps with B if and only if :
normalize(A) interval-overlaps with normalize(B)
or
normalize(A) interval-overlaps with slide( normalize(B))
interval-overlaps is defined in the same way as "intersection" in adamoldak's post.
and both operations normalize() and slide() are easy to be implemented.
take your example: A=[-45°,45°]; B=[10°,20°] , we have
normalize(A) = [315,405]
normalize(B) = [10,20]
slide( normalize(B) ) = [370,380]
and [315,405] interval-overlaps with [370,380]

I have a similar problem with a game engine with rectangles overlapping in a looping map. I've thought about it a lot and have looked at some of you guys' answers. If you're looking for performance, this is the best you can get (until someone proves me wrong :P):
#assume the angles are already normalised
def overlap(a1, a2, b1, b2):
if a2 - a1 + b2 - b1 > 360: #must overlap
return True
return (b1 > a2) ^ (b2 > a1) ^ (a2 < a1) ^ (b2 < b1)
Elegant. Elegant and kinda ugly.

How about normalizing each degree value to 0-360:
a1_norm = a1 % 360
a2_norm = a2 % 360
b1_norm = b1 % 360
b2_norm = b2 % 360
Then you just check for intersection:
(a1_norm <= b2_norm) and (a2_norm<= b1_norm)

Related

Writing a vector sum in MATLAB

Suppose I have a function phi(x1,x2)=k1*x1+k2*x2 which I have evaluated over a grid where the grid is a square having boundaries at -100 and 100 in both x1 and x2 axis with some step size say h=0.1. Now I want to calculate this sum over the grid with which I'm struggling:
What I was trying :
clear all
close all
clc
D=1; h=0.1;
D1 = -100;
D2 = 100;
X = D1 : h : D2;
Y = D1 : h : D2;
[x1, x2] = meshgrid(X, Y);
k1=2;k2=2;
phi = k1.*x1 + k2.*x2;
figure(1)
surf(X,Y,phi)
m1=-500:500;
m2=-500:500;
[M1,M2,X1,X2]=ndgrid(m1,m2,X,Y)
sys=#(m1,m2,X,Y) (k1*h*m1+k2*h*m2).*exp((-([X Y]-h*[m1 m2]).^2)./(h^2*D))
sum1=sum(sys(M1,M2,X1,X2))
Matlab says error in ndgrid, any idea how I should code this?
MATLAB shows:
Error using repmat
Requested 10001x1001x2001x2001 (298649.5GB) array exceeds maximum array size preference. Creation of arrays greater
than this limit may take a long time and cause MATLAB to become unresponsive. See array size limit or preference
panel for more information.
Error in ndgrid (line 72)
varargout{i} = repmat(x,s);
Error in new_try1 (line 16)
[M1,M2,X1,X2]=ndgrid(m1,m2,X,Y)
Judging by your comments and your code, it appears as though you don't fully understand what the equation is asking you to compute.
To obtain the value M(x1,x2) at some given (x1,x2), you have to compute that sum over Z2. Of course, using a numerical toolbox such as MATLAB, you could only ever hope to compute over some finite range of Z2. In this case, since (x1,x2) covers the range [-100,100] x [-100,100], and h=0.1, it follows that mh covers the range [-1000, 1000] x [-1000, 1000]. Example: m = (-1000, -1000) gives you mh = (-100, -100), which is the bottom-left corner of your domain. So really, phi(mh) is just phi(x1,x2) evaluated on all of your discretised points.
As an aside, since you need to compute |x-hm|^2, you can treat x = x1 + i x2 as a complex number to make use of MATLAB's abs function. If you were strictly working with vectors, you would have to use norm, which is OK too, but a bit more verbose. Thus, for some given x=(x10, x20), you would compute x-hm over the entire discretised plane as (x10 - x1) + i (x20 - x2).
Finally, you can compute 1 term of M at a time:
D=1; h=0.1;
D1 = -100;
D2 = 100;
X = (D1 : h : D2); % X is in rows (dim 2)
Y = (D1 : h : D2)'; % Y is in columns (dim 1)
k1=2;k2=2;
phi = k1*X + k2*Y;
M = zeros(length(Y), length(X));
for j = 1:length(X)
for i = 1:length(Y)
% treat (x - hm) as a complex number
x_hm = (X(j)-X) + 1i*(Y(i)-Y); % this computes x-hm for all m
M(i,j) = 1/(pi*D) * sum(sum(phi .* exp(-abs(x_hm).^2/(h^2*D)), 1), 2);
end
end
By the way, this computation takes quite a long time. You can consider either increasing h, reducing D1 and D2, or changing all three of them.

Are two circular buffers equal? (While ignoring shift)

I have two circular buffers -- how can I tell if one is just a shift of another?
For example with B1 = 1,1,2,1,8,1,5,7, B2 = 2,1,8,1,5,7,1,1 we can say that B1 and B2 are equal, because I can rotate one of them to get the other.
What is the best algorithm to test this equality? Obvious test is in O(n^2) (just compare the buffers - in n steps - starting in each of their n element) but I believe I've seen a linear time algorithm. Can you please point me to it?
Assuming B1 and B2 have the same length, your question is equivalent to asking "is B2 a substring of B1 + B1" (B1 concatenated with itself).
For example: 4-element string is a rotation of 1234 if and only if it is a substring of 12341234.
Checking if one string is a substring of another can be done in linear time using KMP algorithm.
if your buffer is of integers, I was thinking that maybe you could use the fact that addition as the commutative property (a+b == b+a)
that means that the start of the list doesn't matter. but on the other hand nether the order of items (1+2+3+4) = (3+1+2+4) so to make sure the items are in proper order we may associate them in pairs or trios. to better ensure a chaining order.. (12+23+34+41) or something like this..
var B1 = [1,1,2,1,8,1,5,7]
var B2 = [2,1,8,1,5,7,1,1]
var B3 = [2,1,8,1,5,7,1,4]
function checksum(buff)
{
var sum = 0
for(var i = 0 ; i < buff.length;i++)
{
var a = buff[i];
var b = buff[(i+1)%buff.length];
var c = buff[(i+2)%buff.length];
var n = ((a*100) + (b * 10) + c);
sum += n;
}
return sum;
}
console.log(checksum(B1) == checksum(B2))//true
console.log(checksum(B1) == checksum(B3))//false
this is a "order sensitive" circular checksum
but like every hash functions there will be collisions and false positives so you will still need a secondary method of comparison..
don't know if i'm on the right track.. hope someone can help to make it better or totally disprove..

Calculating radial velocity from an inverse linear scale

I have a user input that can have an integer value of 1 through 50.
i have, let's imagine, a needle that turns, like it was a clock.
The speed of that turn is determined by the delta in radians it moves every frame.
So, if i have a speed of PI/2, the needle turns a half circle every frame.
I have come to the conclusion that the possible speed, should be between PI/8 (the fastest) and PI/256 (the slowest).
I am trying to build an algorithm that will translate the user input of 1 (the slowest) and 50 (the fastest) into PI/256 and PI/8 (the max value 50 is arbitrary, can be something else); obviously the numbers between should be in reverse correspondence.
what i need would be a formula like:
delta = userInput * (.............)
I'have been trying for hours, if someone could help me out would be very much appreciated.
Solve the line equation: y = m * x + b. I.e., plug in your two points to get two equations with m and b as unknowns, then solve for m and b.
(See my answer here for a more detailed explanation of why this works.)
This was just slightly too long for a comment. Whether the two scales agree in direction doesn't matter, although yours do: you said 1 (the slowest) corresponds to pi/256 (the slowest), and 50 (the fastest) corresponds to pi/8 (the fastest). 1 < 50, and pi/256 < pi/8.
So if that's the right ordering:
>>> a0, a1 = 1., 50.
>>> b0, b1 = pi/256, pi/8
>>> def rescale(x):
... return ((x-a0)/(a1-a0)) * (b1-b0) + b0
...
>>> rescale(1)
0.01227184630308513
>>> rescale(1) == pi/256
True
>>>
>>> rescale(50)
0.39269908169872414
>>> rescale(50) == pi/8
True
with 25 somewhere close to the middle:
>>> rescale(25)
0.198603553435643
If you want 1 to correspond to the fastest speed instead, then simply flip b0 and b1:
>>> a0, a1 = 1., 50.
>>> b0, b1 = pi/8, pi/256
>>> def rescale(x):
... return ((x-a0)/(a1-a0)) * (b1-b0) + b0
...
>>> rescale(1)
0.39269908169872414
>>> rescale(50)
0.012271846303085143
The formula continues to apply.

Algorithm to map an interval to a smaller interval

I tried searching, but due to the nature of my question, I was unable to find something satisfactory.
My problem is the following: I am trying to map numbers ranging from 0 to 2000 (though ideally the upper limit would be adjustable) to the much smaller interval ranging from 10 to 100. The upper limits would map (2000->100) and the lower limits as well. Other than that, an entry that is bigger than another entry in the interval [0;2000] would ideally be bigger than that mapped entry in [0;100]
I'm thinking that this question is not language specific, but in case you are wondering, I'm working with Javascript today.
To map
[A, B] --> [a, b]
use this formula
(val - A)*(b-a)/(B-A) + a
as correctly mentioned in the other answer it's linear mapping.
Basically
y = m*x + c
c = intersection at y-axis
m = slope determined by two known point (A, a), (B, b) = (b-a)/(B-A)
I think that instead of giving you a formula of direct mapping, a better approach would be to explain the idea behind it:
Suppose we want to map an interval [0,1] to interval [1,3], which can be seen as the problem of finding f(x) = Ax + B such that giving any x from interval [0,1], will result in f(x) being/resulting in interval [1,3].
From this perspective, we already know some values:
x = 0 & f(0) = 1 => f(0) = A*0 + B = 1 => B = 1
x = 1 & f(1) = 3 => f(1) = A*1 + B = 3 <=> A + 1 = 3 => A=2
From (1) and (2), we may conclude that the function that maps interval [0,1] to [1,3] is f(x) = 2x + 1.
In you very case, you now should have all necessary knowledge to be able to map [0,2000] interval to [10,100].
// Given a value from intervalA, returns a mapped value from intervalB.
function intervalicValueMap(intervalA, intervalB, valueIntervalA) {
var valueIntervalB = (valueIntervalA - intervalA[0]) * (intervalB[1] - intervalB[0])
/ (intervalA[1] - intervalA[0]) + intervalB[0];
valueIntervalB = Math.round(valueIntervalB); // Ommit rounding if not needed.
return valueIntervalB;
}
var intervalA = [100, 200];
var intervalB = [1, 10];
var valueIntervalA = 170;
var valueIntervalB = intervalicValueMap(intervalA, intervalB, valueIntervalA);
console.log(valueIntervalB); // Logs 7
A simple linear mapping would map x to x*90/2000+10.
Here could be an optimized way to map your x data,
This pseudo code shows you the main idea for a map function
that:
Avoid problems with x values out of b1 - b2's range.
Deals with array mapping
function map(var x, var b1, var b2, var s1, var s2)
{
var i;
var result;
i = 0;
while(i < sizeof(s2))
if(x < b1)
result[i++] = s1;
else if (x > b2)
result[i++] = s2;
else
result[i] = (x - b1) / (b2 - b1 ) * (s2[i] - s1[i]) + s1[i++];
return (result);
}
An answer using Numpy from Python:
import numpy as np
# [A, B]: old interval
# [a, b] new interval
new_value = np.interp(old_value, [A, B], [a, b])
print(new_value)

Find If 4 Points Form a Quadrilateral

Can someone please show me an algorithm to write a function that returns true if 4 points form a quadrilateral, and false otherwise? The points do not come with any order.
I've tried to check all permutations of the 4 points and see if there's 3 points that forms a straight line. If there's 3 points that forms a straight line than it's not quadrilateral. But then I realize that there's no way to tell the order. And then I struggle for several hours of thinking and googling with no result :(
I've read these questions:
find if 4 points on a plane form a rectangle?
Determining ordering of vertices to form a quadrilateral
But still find no solution. In the case of 1, it can't detect another kind of quadrilateral, and in 2 it assumes that the points are quadirateral already. Are there any other way to find out if 4 points form a quadirateral?
Thanks before.
EDIT FOR CLARIFICATION:
I define quadrilateral as simple quadrilateral, basically all shapes shown in this picture:
except the shape with "quadrilateral" and "complex" caption.
As for problems with the "checking for collinear triplets" approach, I tried to check the vertical, horizontal, and diagonal lines with something like this:
def is_linear_line(pt1, pt2, pt3):
return (pt1[x] == pt2[x] == pt3[x] ||
pt1[y] == pt2[y] == pt3[y] ||
slope(pt1, pt2) == slope(pt2, pt3))
And realize that rectangle and square will count as linear line since the slope of the points will be all the same. Hope this clears things out.
This is for checking if a quadrilateral is convex. Not if it is a simple quadrilateral.
I did like this in objective-c https://github.com/hfossli/AGGeometryKit/
extern BOOL AGQuadIsConvex(AGQuad q)
{
BOOL isConvex = AGLineIntersection(AGLineMake(q.bl, q.tr), AGLineMake(q.br, q.tl), NULL);
return isConvex;
}
BOOL AGLineIntersection(AGLine l1, AGLine l2, AGPoint *out_pointOfIntersection)
{
// http://stackoverflow.com/a/565282/202451
AGPoint p = l1.start;
AGPoint q = l2.start;
AGPoint r = AGPointSubtract(l1.end, l1.start);
AGPoint s = AGPointSubtract(l2.end, l2.start);
double s_r_crossProduct = AGPointCrossProduct(r, s);
double t = AGPointCrossProduct(AGPointSubtract(q, p), s) / s_r_crossProduct;
double u = AGPointCrossProduct(AGPointSubtract(q, p), r) / s_r_crossProduct;
if(t < 0 || t > 1.0 || u < 0 || u > 1.0)
{
if(out_pointOfIntersection != NULL)
{
*out_pointOfIntersection = AGPointZero;
}
return NO;
}
else
{
if(out_pointOfIntersection != NULL)
{
AGPoint i = AGPointAdd(p, AGPointMultiply(r, t));
*out_pointOfIntersection = i;
}
return YES;
}
}
There is no way to determine both vertex order and presence of a quadrilateral in the same operation unless you use operations that are far more expensive than what you're already performing.
Checking for collinear triplets (like you did) will exclude cases where the four points form triangles or straight lines.
To exclude also the complex quadrilateral (with crossing edges):
A quadrilateral formed by the points A, B, C and D is complex, if the intersection of AB and CD (if any) lies between the points A and B, and the same applies for BC and DA.
Do you have any more inputs than the 4 points? because if 4 points succeed to your test, they can always form 3 different quadrilaterals, sometime of different family. For example, Take a square, add 2 diagonal and remove the side.
So with only 4 points as input, you cannot do better than what you are already doing.
Let A, B, C and D be the four points. You have to assume that the edges are A-B, B-C, C-D, and D-A. If you can't make that assumption, the four points will always form a quadrilateral.
if (A-B intersects C-D) return false
if (B-C intersects A-D) return false
return true
First, find all side and diagonal sizes using the distance formula:
This.
Next, find all angles using this formula:
This
reference from: https://algorithmdotcpp.blogspot.com/2022/01/find-type-of-quadrilateral-with-given-points.html
Code in python:
# array
# read 8 number
points = list(map(int,input().split()))
# create separate variable for coordinates
xa = points[0]
ya = points[1]
xb = points[2]
yb = points[3]
xc = points[4]
yc = points[5]
xd = points[6]
yd = points[7]
# edge of quadrilateral using edge formula
# finding edge using distance formula.
a = math.sqrt((xb - xa) * (xb - xa) + (yb - ya) * (yb - ya))
b = math.sqrt((xc - xb) * (xc - xb) + (yc - yb) * (yc - yb))
c = math.sqrt((xd - xc) * (xd - xc) + (yd - yc) * (yd - yc))
d = math.sqrt((xa - xd) * (xa - xd) + (ya - yd) * (ya - yd))
# diagonal of quadrilateral
# find diagonals.
diagonal_ac = math.sqrt((xc - xa) * (xc - xa) + (yc - ya) * (yc - ya))
diagonal_bd = math.sqrt((xd - xb) * (xd - xb) + (yd - yb) * (yd - yb))
# angles
# angles of quadrilateral
# find angle using angle formula.
A = math.acos((a * a + d * d - diagonal_bd * diagonal_bd) / (2 * a * d))
B = math.acos((b * b + a * a - diagonal_ac * diagonal_ac) / (2 * b * a))
C = math.acos((c * c + b * b - diagonal_bd * diagonal_bd) / (2 * c * b))
D = math.acos((d * d + c * c - diagonal_ac * diagonal_ac) / (2 * d * c))
Now we can determine whether the type of quadrilateral or quadrilateral is not found using if-else conditions.
# if angles are equal means(90*)
if (A == B and A == C and A == D):
# if edge size are equal
if (a == b and a == c and a == d):
# square
print("Quadrilateral is square...\n")
print("area of square :", a * a)
# else
else:
# rectangular
print("Quadrilateral is rectangular...\n")
print("area of square :", a * b)
# angles are not equal but edges are equal
elif (a == b and a == c and a == d):
# diamond
print("Quadrilateral is diamond(Rhombus)...\n")
# opposite edges(sides) are equal
elif (a == c and b == d):
# parallelogram
print("Quadrilateral is parallelogram...")
else:
print("Quadrilateral is just a quadrilateral...\n")

Resources