Related
Given is n (n <= 1000) circles ((x;y), r) where (x;y) = coordinates of circle center, r = radius. x, y, r <= 10^6. x, y, r, can be real numbers.
The problem is to find point(x;y) covered by all circles, or determine that there are no such points. Coordinates of point can be real numbers too.
No ideas how to do it, could anyone help, please?
Assuming that by circle you mean what mathematicians would call a closed disk, there's an O(n²)-time algorithm with simple data structures.
For k from 1 to n, the algorithm finds a point in the intersection of the first k disks, assuming that such a point exists. Start with the center of the first disk. For each disk after the first, check whether the current point belongs to that disk. If so, great. If not, then either the intersection is empty or the intersection contains a point on the boundary of the current disk (the line segment from the current point to any point in the intersection of all of the disks must cross the boundary). In this case, find a new point by intersecting the boundary (a circle) with each of the previous disks, an easier 1D problem.
This might go even faster in expectation if we randomize the order of the disks, but I haven't worked out a proof. With n ≤ 1000, hopefully O(n²) is fast enough.
Sharir ("Intersection and Closest-Pair Problems for a Set of Planar Discs", 1985) may have given an O(n log² n)-time algorithm, but I can't tell from the abstract.
Let's say I have a contour shape defined by two functions x(p) and y(p), where p is the distance traveled along the perimeter of the shape, normalized between 0 and 1. For example, a unit circle about the origin would be defined as x(p) = sin(2 * pi * p) and y(p) = cos(2 * pi * p).
A simple test to determine if a point is within that circle would be to calculate the point's distance from the origin, and then detect if that distance is less than or equal to 1.
But what if my shape has multiple crossings, and is much more complicated than a circle?
There exists a point in polygon test for a discrete shape defined by a set of points. That algorithm is pretty easy to find, since it's used in a lot of places. But if I don't want to use a discrete definition of my shape, what algorithm can I use to determine the winding number, with the assumption that the shape is defined at compile time?
Generalizing the point in polygon test, you can find all solutions of y(p)=0 such that x(p)>0 and use the parity of their numbers.
In the case of the circle, cos(2πp)=0 for p=(k+1/2)π, and only one value of p in range [0,1) makes sin(2πp)>0.
So far so good if you can solve the y equation analytically. Otherwise you will need a reliable numerical solver, able to spot all roots. An alternative is to flatten the curve (draw it as a polyline, ensuring a maximum deviation tolerance), and apply the polygon algorithm.
For the sake of a second example, let us consider Pascal's limaçon with equation r = 0.5 + cos Θ, and some test point along X.
y = (0.5 + cos Θ) sin Θ = 0
for Θ=0, 2π/3, π, 4π/3. The corresponding abscissas are 1.5, 0, 0.5 and 0.
You can conclude that the interior points on the X axis are between 0.5 and 1.5 (also at 0, in a degenerate way).
If you know a point that is inside (or outside) the shape and the shape is smooth and non-intersecting, then you can connect the known point to any other point and count the number of times it crosses the boundary. An even number of times means the unknown point is similarly inside (or outside). An odd number means the opposite.
This is based on the Jordan Curve Theorem. As I recall, winding number is needed for the proof of the theorem, but the application is quite easy.
How about computing the total curvature of the closed curve (assuming it is everywhere differentiable), then dividing it by 2PI? See Wiki's Total Curvature page here.
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
Given a 2 dimensional plane in which there are n points. I need to generate the equation of a line that divides the plane such that there are n/2 points on one side and n/2 points on the other.
I have assumed the points are distinct, otherwise there might not even be such a line.
If points are distinct, then such a line always exists and is possible to find using a deterministic O(nlogn) time algorithm.
Say the points are P1, P2, ..., P2n. Assume they are not all on the same line. If they were, then we can easily form the splitting line.
First translate the points so that all the co-ordinates (x and y) are positive.
Now suppose we magically had a point Q on the y-axis such that no line formed by those points (i.e. any infinite line Pi-Pj) passes through Q.
Now since Q does not lie within the convex hull of the points, we can easily see that we can order the points by a rotating line passing through Q. For some angle of rotation, half the points will lie on one side and the other half will lie on the other of this rotating line, or, in other words, if we consider the points being sorted by the slope of the line Pi-Q, we could pick a slope between the (median)th and (median+1)th points. This selection can be done in O(n) time by any linear time selection algorithm without any need for actually sorting the points.
Now to pick the point Q.
Say Q was (0,b).
Suppose Q was collinear with P1 (x1,y1) and P2 (x2,y2).
Then we have that
(y1-b)/x1 = (y2-b)/x2 (note we translated the points so that xi > 0).
Solving for b gives
b = (x1y2 - y1x2)/(x1-x2)
(Note, if x1 = x2, then P1 and P2 cannot be collinear with a point on the Y axis).
Consider |b|.
|b| = |x1y2 - y1x2| / |x1 -x2|
Now let the xmax be the x-coordinate of the rightmost point and ymax the co-ordinate of the topmost.
Also let D be the smallest non-zero x-coordinate difference between two points (this exists, as not all xis are same, as not all points are collinear).
Then we have that |b| <= xmax*ymax/D.
Thus, pick our point Q (0,b) to be such that |b| > b_0 = xmax*ymax/D
D can be found in O(nlogn) time.
The magnitude of b_0 can get quite large and we might have to deal with precision issues.
Of course, a better option is to pick Q randomly! With probability 1, you will find the point you need, thus making the expected running time O(n).
If we could find a way to pick Q in O(n) time (by finding some other criterion), then we can make this algorithm run in O(n) time.
Create an arbitrary line in that plane. Project each point onto that line a.k.a for each point, get the closest point on that line to that point.
Order those points along the line in either direction, and choose a point on that line such that there is an equal number of points on the line in either direction.
Get the line perpendicular to the first line which passes through that point. This line will have half the original points on either side.
There are some cases to avoid when doing this. Most importantly, if all the point are themselves on a single line, don't choose a perpendicular line which passes through it. In fact, choose that line itself so you don't have to worry about projecting the points. In terms of the actual mathematics behind this, vector projections will be very useful.
This is a modification of Dividing a plane of points into two equal halves which allows for the case with overlapping points (in which case, it will say whether or not the answer exists).
If number of points is odd, return "impossible".
Pick a random line (two random points)
Project all points onto this line (`O(N)` operation)
(i.e. we pretend this line is our new X'-axis, and write down the
X'-coordinate of each point)
Perform any median-finding algorithm on the X'-coordinates
(`O(N)` or faster-if-desired operation)
(returns 2 medians if no overlapping points)
Return the line perpendicular to original random line that splits the medians
In rare case of overlapping points, repeat a few times (it would take
a pathological case to prevent a solution from existing).
This is O(N) unlike other proposed solutions.
Assuming a solution exists, the above method will probably terminate, though I don't have a proof.
Try the above algorithm a few times unless you detect overlapping points. If you detect a high number of overlapping points, you may be in for a rough ride, but there is a terribly inefficient brute-force solution that involves checking all possible angles:
For every "critical slope range", perform the above algorithm
by choosing a line with a slope within the range.
If all critical slope ranges fail, the solution is impossible.
A critical angle is defined as the angle which could possibly change the result (imagine the solution to a previous answer, rotate the entire set of points until one or more points swaps position with one or more other points, crossing the dividing line. There are only finitely many of these, and I think they are bounded by the number of points, so you're probably looking at something in the range O(N^2)-O(N^2 log(N)) if you have overlapping points, for a brute-force approach.
I'd guess that a good way is to sort/sequence/order the points (e.g. from left to right), and then choose a line which passes through (or between) the middle point[s] in the sequence.
There are obvious cases where no solution is possible. E.g. when you have three heaps of points. One point at location A, Two points at location B, and five points at location C.
If you expect some decent distribution, you can probably get a good result with tlayton's algorithm. To select the initial line slant, you could determine the extent of the whole point set, and choose the angle of the largest diagonal.
The median equally divides a set of numbers in the manner similar to what you're trying to accomplish, and it can be computed in O(n) time using a selection algorithm (the writeup in Cormen et al is better, so you may want to look there instead). So, find the median of your x values Mx (or your y values My if you prefer) and set x = Mx (or y = My) and that line will be axially aligned and split your points equally.
If the nature of your problem requires that no more than one point lies on the line (if you have an odd number of points in your set, at least one of them will be on the line) and you discover that's what's happened (or you just want to guard against the possibility), rotate all of your points by some random angle, θ, and compute the median of the rotated points. You then rotate the median line you computed by -θ and it will evenly divide points.
The likelihood of randomly choosing θ such that the problem manifests itself again is very small with a finite number of points, but if it does, try again with a different θ.
Here is how I approach this problem (with the assumption that n is even and NO three points are collinear):
1) Pick up the point with smallest Y value. Let's call it point P.
2) Take this point as the new origin point, so that all other points will have positive Y values after this transformation.
3) For every other point (there are n - 1 points remaining), think it under the polar coordinate system. Each other point can be represented with a radius and angle. You could ignore the radius and just focus on the angle.
4) How can you find a line that split the points evenly? Find the median of (n - 1) angles. The line from point P to the point with that median angle will split the points evenly.
Time complexity for this algorithm is O(n).
I dont know how useful this is I have seen a similar problem...
If you already have the directional vector (aka the coefficients of the dimensions of your plane).
You can then find two points inside that plane, and by simply using the midpoint formula you can find the midpoint of that plane.
Then using the coefficients of that plane and the midpoint you can find a plane that is from equal distance from both points, using the general equation of a plane.
A line then would constitute in expressing one variable in terms of the other
so you would find a line with equal distance between both planes.
There are different methods of doing this such as projection using the distance equation from a plane but I believe that would complicate your math a lot.
To add to M's answer: a method to generate a Q (that's not so far away) in O(n log n).
To begin with, let Q be any point on the y-axis ie. Q = (0,b) - some good choices might be (0,0) or (0, (ymax-ymin)/2).
Now check if there are two points (x1, y1), (x2, y2) collinear with Q. The line between any point and Q is y = mx + b; since b is constant, this means two points are collinear with Q if their slopes m are equal. So determine the slopes mi for all points and check if there are any duplicates: (amoritized O(n) using a hash-table)
If all the m's are distinct, we're done; we found Q, and M's algorithm above generates the line in O(n) steps.
If two points are collinear with Q, we'll move Q up just a tiny amount ε, Qnew = (0, b + ε), and show that Qnew will not be collinear with two other points.
The criterion for ε, derived below, is:
ε < mminΔ*xmin
To begin with, our m's look like this:
mi = yi/xi - b/xi
Let's find the minimum difference between any two distinct mi and call it mminΔ (O(n log n) by, for instance, sorting then comparing differences between mi and i+1 for all i)
If we fudge b up by ε, the new equation for m becomes:
mi,new = yi/xi - b/xi - ε/xi
= mi,old - ε/xi
Since ε > 0 and xi > 0, all m's are reduced, and all are reduced by a maximum of ε/xmin. Thus, if
ε/xmin < mminΔ, ie.
ε < mminΔ*xmin
is true, then two mi which were previously unequal will be guaranteed to remain unequal.
All that's left is to show that if m1,old = m2,old, then m1,new =/= m2,new. Since both mi were reduced by an amount ε/xi, this is equivalent to showing x1 =/= x2. If they were equal, then:
y1 = m1,oldx1 + b = m2,oldx2 + b = y2
Contradicting our assumption that all points are distinct. Thus, m1, new =/= m2, new, and no two points are collinear with Q.
I picked up the idea from Moron and andand and
continued to form a deterministic O(n) algorithm.
I also assumed that the points are distinct and
n is even (thought the algorithm can be
changed so that uneven n with one point
on the dividing line are also supported).
The algorithm tries to divide the points with a vertical line between them. This only fails if the points in the middle have the same x value. In that case the algorithm determines how many points with the same x value have to be on the left and lower site and and accordingly rotates the line.
I'll try to explain with an example.
Let's asume we have 16 points on a plane.
First we need to get the point with the 8th greatest x-value
and the point with the 9th greatest x-value.
With a selection algorithm this is possible in O(n),
as pointed out in another answer.
If the x-value of that points is different, we are done.
We create a vertical line between that two points and
that splits the points equal.
Problematically now is if the x-values are equal. So we have 3 sets of points.
That on the left side (x < xa), in the middle (x = xa)
and that on the right side (x > xa).
The idea now is to count the points on the left side and
calculate how many points from the middle needs to go there,
so that half of the points are on that side. We can ignore the right side here
because if we have half of the points on the left side, the over half must be on the right side.
So let's asume we have we have 3 points (c=3) on the left side,
6 in the middle and 7 on the right side
(the algorithm doesn't know the count from the middle or right side,
because it doesn't need it, but we could also determine it in O(n)).
We need 8-3=5 points from the middle to go on the left side.
The points we already got in the first step are useless now,
because they are only determined by the x-value
and can be any of the points in the middle.
We want the 5 points from the middle with the lowest y-value on the left side and
the point with the highest y-value on the right side.
Again using the selection algorithm, we get the point with the 5th greatest y-value
and the point with the 6th greatest y-value.
Both points will have the x-value equal to xa,
else we wouldn't get to this step,
because there would be a vertical line.
Now we can create the point Q in the middle of that two points.
Thats one point from the resulting line.
Another point is needed, so that no points from the left or right side are divided.
To get that point we need the point from the left side,
that has the lowest angle (bh) between the the vertical line at xa
and the line determined by that point and Q.
We also need that point from the right side (with angle ag).
The new point R is between the point with the lower angle
and a point on the vertical line
(if the lower angle is on the left side a point above Q
and if the lower angle is on the right side a point below Q).
The line determined by Q and R divides the points in the middle
so that there are a even number of points on both sides.
It doesn't divide any points on the left or right side,
because if it would that point would have a lower angle and
would have been choosen to calculate R.
From the view of a mathematican that should work well in O(n).
For computer programs it is fairly easy to find a case
where precision becomes a problem. An example with 4 points would be
A(0, 100000000), B(0, 100000001), C(0, 0), D(0.0000001, 0).
In this example Q would be (0, 100000000.5) and R (0.00000005, 0).
Which gives B and C on the left side and A and D on the right side.
But it is possible that A and B are both on the dividing line,
because of rounding errors. Or maybe only one of them.
So it belongs to the input values if this algorithm suits to the requirements.
get that two points Pa(xa, ya) and Pb(xb, yb)
which are the medians based on the x values > O(n)
if xa != xb you can stop here
because a y-axis parallel line between that two points is the result > O(1)
get all points where the x value equals xa > O(n)
count points with x value less than xa as c > O(n)
get the lowest point Pc based on the y values from the points from 3. > O(n)
get the greatest point Pd based on the y values from the points from 3. > O(n)
get the (n/2-c)th greatest point Pe based on the y values from the points from 3. > O(n)
also get the next greatest point Pf based on the y values from the points from 3. > O(n)
create a new point Q (xa, (ye+yf)/2)
between Pe and Pf > O(1)
for all points Pi calculate
the angle ai between Pc, Q and Pi and
the angle bi between Pd, Q and Pi > O(n)
get the point Pg with the lowest angle ag (with ag>0° and ag<180°) > O(n)
get the point Ph with the lowest angle bh (with bh>0° and bh<180°) > O(n)
if there aren't any Pg or Ph (all points have same x value)
create a new point R (xa+1, 0) anywhere but with a different x value than xa
else if ag is lower than bh
create a new point R ((xc+xg)/2, (yc+yg)/2) between Pc and Pg
else
create a new point R ((xd+xh)/2, (yd+yh)/2) between Pd and Ph > O(1)
the line determined by Q and R divides the points > O(1)
I can't solve it:
You are given 8 integers:
A, B, C representing a line on a plane with equation Ax + By = C
a, b, c representing another line
x, y representing a point on a plane
The two lines are not parallel therefore divide plane into 4 pieces.
Point (x, y) lies inside of one these pieces.
Problem:
Write a fast algorithm that will find a point with integer coordinates in the same piece as (x,y) that is closest to the cross point of the two given lines.
Note:
This is not a homework, this is old Euler-type task that I have absolutely no idea how to approach.
Update:
You can assume that the 8 numbers on input are 32-bit signed integers.
But you cannot assume that the solution will be 32 bit.
Update 2:
Difficult case - when lines are almost parallel - is the heart of the problem
Update 3:
Author of the problem states that the solution is linear O(n) algorithm. Where n is the size of the input (in bits). Ie: n = log(A) + log(B) + ... + log(y)
But I still can't solve it.
Please state complexities of published algorithms (even if they are exponential).
alt text http://imagebin.ca/img/yhFOHb.png
Diagram
After you find intersection of lines L1:Ax+By=C and L2:ax+by=c i.e. point A(x1,y1).
Define two more lines y = ceil(y1) and y = floor(y1) parallel to X-axis and find their intersection with L1 and L2 i.e. Points B(x2,y2) and C(x3,y3).
Then point you require is D or E whichever is closer to point A. Similar procedure applies to other parts of the plane.
D ( ceil(x2), ceil(y1) )
E ( ceil(x3), floor(y1) )
This problem falls into the category of Integer Convex Optimization.
Presented here is a mathematical way to approach the problem. I don't expect you to actually use it - a lot of complicated techniques are required, and other algorithmic approaches (such as "searching" for the appropriate point) will likely do just fine. However, interest has been expressed in the "true" solution, so here it is.
It can be solved in three stages:
First, determine which side of each line the answer will be on, as illustrated by TheMachineCharmer's answer.
Once that is known, the problem can be rewritten as a convex optimization problem (see Wikipedia for details). The function to be optimized is minimizing (x - x0)^2 + (y - y0)^2, with x0 and y0 the coordinates of the intersection of the two lines. The two lines each become a linear inequality, e.g. "x+y >= 0", together forming the convex region the answer can be found in. I will note that the solution will be (x=x0, y=y0) - what you need from this stage a way of expressing the problem, analagous to a feasible tableau for the simplex method.
Third, an integer solution can be found by repeatedly adding cuts to further constrain the feasible region until the solution to the convex optimization problem is integral. This stage may take a lot of iterations in the general case, but looking at the problem presented, and in particular the 2D nature of it, I believe it will be solved with at most two cuts.
I show here how a "difficult" instance of this problem can be solved. I think this method can be generalized. I have put another simpler instance in the comments of the original post.
Consider the two lines:
10000019 * X - 10000015 * Y + 909093 >= 0 (L1)
-10000022 * X + 10000018 * Y + 1428574 >= 0 (L2)
A = 10000019, B = -10000015, C = -909093
The intersection point is H:
Hx = -5844176948071/3, Hy = -5844179285738/3
For a point M(X,Y), the squared distance HM^2 is:
HM^2 = (9*X^2+35065061688426*X
+68308835724213587680825685
+9*Y^2+35065075714428*Y)/9
g = gcd(A,B) = 1: the equation of L1 A*X+B*Y+909093
can take any integer value.
Bezout coefficients, U=2500004 and V=2500005 verify:
A * U + B * V = 1
We now rewrite the problem in the "dual" basis (K,T) defined by:
X = T*U - K*B
Y = T*V + K*A
After substitution, we get:
T+909093 >= 0
2*T+12*K+1428574 >= 0
minimize 112500405000369*T^2
+900003150002790*T*K
+1800006120005274*K^2
+175325659092760325844*T
+701302566240903900522*K
+Constant
After further translating (first on T, then on K to minimize the
constant in the second equation), T=T1-909093, K=K1+32468:
T1 >= 0
2*T1+4+12*K1 >= 0
minimize 112500405000369*T1^2
+300001050000930*T1
+900003150002790*T1*K1
+1200004080003516*K1
+1800006120005274*K1^2
+Constant
The algorithm I proposed is to loop on T1. Actually, we don't need to
loop here, since the best result is given by T1=K1=0, corresponding to:
X = -1948055649352, Y = -1948056428573
My initial post below.
Here is another idea of algorithm. It may work, but I did not implement it...
With appropriate change of signs to match the position of (x,y), the problem can be written:
A*X+B*Y>=C (line D)
a*X+b*Y>=c (line d)
minimize the distance between M(X,Y) and H, the intersection point
A*b != a*B (intersection is defined)
A,B,C,a,b,c,X,Y all integers
The set of all values reached by (AX+BY) is the set of all multiples of g=gcd(A,B), and there exist integers (u,v) such that Au+Bv=g (Bezout theorem). From a point with integer coordinates (X0,Y0), all points with integer coordinates and the same value of AX+BY are (X0-KB/g,Y0+KA/g), for all integers K.
To solve the problem, we can loop on lines parallel to D at increasing distance from H, and containing points with integer coordinates.
Compute g,u,v, and H (the coordinates of H are probably not needed, we only need the coefficients of the quadratic form corresponding to the distance).
T0 = ceil(C/g)
Loop from T = T0
a. Find K the smallest (or largest, depending on the sign of aB-bA) integer verifying a*(Tu-KB/g)+b*(Tv+KA/g)>=c
b. Keep point (Tu-KB/g,Tv+KA/g) if closer to H
c. Exit the loop when (T-T0) corresponds to a distance from D larger than the best result so far, otherwise continue with T+=1
I have researched the problem in the past (both because it's fun and because I ran into something related at a place where I worked).
To my knowledge, there is no efficient (FPTIME) algorithm for this problem.
The only known (to me) solution is to basically enumerate integer coordinates (starting from around the intersection) until you find the one you want. This is of course not at all efficient when the angle between the two lines is very small. You can do some pruning to improve efficiency and, when the slope is small, efficiency is decent.
A generalization of this (ILP - integer linear programming) is known to be NP-complete.
The more I think about this, the more it seems like it turns into Integer Linear Programming, which is NP-complete in the general case. http://en.wikipedia.org/wiki/Linear_programming#Integer_unknowns
My line of reasoning started out like TheMachineCharmer's answer until I reached that point. The problem there is that the approach of examining the lines along the ceil/floor of the point of intersection only works if the section is aligned with the vertical or horizontal axis though the intersection point. More likely, the thin section will be inclined at some angle away from the axis and the ceil/floor neighbors will not intersect the section on integer coordinates.
At that point we're looking for some integer combination of the natural unit vectors that satisfies the inequalities that define our selected section and also minimizes the distance to the point of intersection. To me, that seems like an integer linear programming problem.
There are special cases of integer linear programming that are easier than NP-hard and this problem could easily be one of them since it seems like its more constrained than the general linear programming case. The Wikipedia article links to a few methods, but that's beyond my math level to apply.
As a few others have pointed out, this is a problem in integer linear programming (aka linear Diophantine inequalities).
Check out this reference: ABS Algorithm For Solving a Class Of Linear Diophantine Inequalities and Integer LP Problems. The authors claim to be able to solve systems like
max(cTx) for Ax≤b, x∈Zn, where c∈Zn, b∈Zm, A∈Zm,n, m≤n.
In particular, setting m=2, n=2, we get the problem of finding
max(cTx) for Ax ≤ b, x∈Z2, where c∈Z2, b∈Z2, A∈Z2,2.
Here, A is a 2x2 matrix, and b, c and x are 2x1 column vectors.
The problem stated by the OP can be restated in this fashion (if asked, I'll try to spell this out in more detail).
The matrix algorithm presented in the paper may look hairy to the uninitiated, but matrix algorithms are like that. Not that I've gone through it line by line, or understand it, but it looks pretty tame compared to some stuff I've seen.
This seems to be something in the general class of ABS methods, which appear to be gaining traction in several problem domains.
The last sentence of section 2 of the paper also refers to another solution method.
As #Alan points out, whereas the general ILP problem is NP-Hard, the problem stated here may not be. I'm not sure why that is, but it may be because the matrix A is 2x2 (rather than nx2), and because the constraints can be expressed as integers.
Edit1: the complexity of the algorithm appears to be O(1) (It appears to be O(d), where d is the dimension of the lattice. In this case, d=2). My surprise at this is O(!!) and understanding and implementing this is still O(??), although I've gone through it a few times now and it is looking more straightforward than I thought.
Here's a partial idea which may be useful in getting a full solution. Imagine the two lines are very, very close to each other. Then any integral solution between them would also be an integral point which is very close to each line. Let's try to find close integral points to the line ax+by=c. Since y = (c - ax)/b, we need to have y very close to an integer, so b approximately divides c-ax. In other words, c-ax+D == 0 mod b for a small integer D.
We can solve c-ax+D == 0 mod b for x: x = a^-1(c+D) mod b (a^-1 will exist if a and b are relatively prime, not sure if that is the case here).
So the algorithm is to evaluate x = a^-1(c+D) mod b for D=0,+1,-1,+2,-2,... and try the resulting x's to see if they work. If there are close integral points to the intersection of the two lines, they should show up early in this iteration. Of course, you may have to reach D=b-1 in the worst case...
Actually it may be possible to solve this with a modified Bresenham's line drawing algorithm.
It is usually used to do scan conversion of lines, and only requires increments of some step inside a loop if you know the end points of the line.
Once you have worked out which sector the point is in, move the origin to the intersection keeping note of the non integer error. Work out the slope of the line from the intersection to the bottom line, then do a normal to the horizontal at an integer x value (if the slope is small) or a normal from the y (is the slope is high) and find where it intersects the other axis an an integer point.
You should be able to check each integer step in one axis to determine if the point you are testing is above or between your two lines (make a new vector to that spot from the intersection and determine the slope). If the point is above increment your integer step. Becuse you are testing from the smallest gradient differnece from one of the lines it should be O(n). In Bresenhams algorithm their are 8 sectors not just 4.
You guys are missing the point! haha, sorry, couldn't resist.
Hey, let's imagine a slightly simpler situation.
You've got one line emanating from the origin forming an angle of less than 90 degrees with the x axis. Find the nearest integer point.
The problem with just searching lattice points until you hit one that's in the quadrant we want is that one doesn't know how far out to search. In the case of a very, very acute angle, we could consider a bazillion points before we hit one that's in our region.
Solution:
Solve: (Slope of Line) * Delta(x) = 1.
i.e. Delta(x) = 1/(Slope of Line), is where we start searching. Subject to the constraint Delta(x) > 1.
In other words, we go just far out enough that there must have been at least an integer difference between x and y coordinates.
In our problem we'd have to transform appropriately and tweedle the numbers to give an appropriate error range. Delta(x) >= 2, Delta(x) = 2/(Slope of Line) I think will do it off of the top of my head, but I don't have a pencil.
No?
Well, it depends on what is considered as fast enough.
Let's name the point [x,y] P. Also I'll call points with integer coordinates 'integer points'.
Algorithm I propose:
Find the point Q where these two lines intersect. (Q=[x_q, y_q])
Get the function of the line between Q and P, y=f(x) or inverse x=g(y);
Determine if QP more vertical or horizontal according to its angle. Let's say it's vertical to simplify following solution (if it's horizontal, the axes would simply invert and where I write x it'd be y and vice versa).
Take the first integer coordinate y_1 we get going along the line from Q to P.
Calculate second coordinate of that point: x_1=f(y_1). That point is in our segment.
Find if the surrounding integer points with coordinates [floor(x_1);y_1] and [floor(x_1+1);y1] are in the segment we're interested in.
6.1 If yes, then we iterate through horizontal line x_3=f(y_1) to find the integer point which is still in our segment and has (x_3-x_q) -> min. That point is our answer.
6.2 If not, then increment y_1 by one and repeat from step 5.
I think there are 3 pieces to this.
calculate the intersection of the 2 lines, and hold on to the X and Y coordinates of that point
find the section that the given point is in. This should be easy enough, because you have the slope of the 2 lines, and the slope of the line created by the given point and the point of intersection. Call them m_line1, m_line2 and m_intersect. If m_intersect There's a formula to figure out the section using these values and the location of the given point.
find the closest integer. There is also a straightforward calculation of this once you know the values from #1 above, and the slopes from #2. You can brute-force it, but there is an elegant mathematical solution.
These are the steps I took, at least.
Updated to add more substance
OK, I'll start us off with a discussion on #2.
If you calculate the slope of the given point and the intersection point, then you arrive at m_intersection. This is the slope of a line that passes through the intersection point. Let's assume that m_line1 is the greater of the 2 slopes, so that line1 is "above" line2 as x increases after the intersection. It makes it easier to think about for section names. We'll call section A the section given by the sliver between line1 and line2 for x larger than the intersection coordinate x, and then we'll name the other 3 sections clockwise, so that A and C are opposite each other.
If m_intersection is between m_line1 and m_lin2, then it must be in one of the 2 sections A or C. Which section is a simple test of the x coordinate value against the intersection's x coordinate. We defined A to be the section with greater value. A similar calculation can be made if the slope is outside m_line1 or m_line2.
This gives you the section that your point lies in. All you did was calculate 1 intersection (5 multiplications, 2 divisions and a handful of subtractions if you do it the traditional way), 3 slopes, and then a couple integer comparisons.
Edit #3 - back by (un)popular demand!
So here is how I calculated #3, the closest integer point to the intersection. It may not be the best, but it uses binary search, so it's O(log n) where n is related to the inverse of the difference of the line slopes. The closer they are together, the larger n is.
First, take the difference between the slopes of the two lines. Say it's 1/8. This means that from the point of intersection, you have to go out 8 units along the x axis before you are guaranteed that there is a whole integer on the y axis in between the two lines (it may be on one of the lines). Now, if the intersection itself is not on an integer x coordinate, then you'll need to step out further to guarantee that your starting point is on an integer x coordinate, but it is bounded. If the intersection is at x = 1.2, then in the above example, at worst you'd start at x = 41, then move down ~5 units along the y axis. Choose either the ceil or floor of the y value that you get. It's not terribly critical.
Now that you have a starting point, the closest point can be approximated by binary search. Your new line segment is between the intersection and the starting point, and your units of movement are multiples of that line segment's slope. Calculate the midpoint of the line segment and see if it lies in between the two lines. Add or subtract 1 to it if it is not a direct hit, and if either of those hits, cut the remaining distance in half and do it again. Otherwise search the further half of the segment.
If you don't have a slope difference < 1, I think the problem may be simpler (brute force the space around the intersection). But it's just a special case of the search above, where you don't need to step out that far to find a starting point.
I was doing something similar when I had to find a point for labeling of a polygon.
The final result was 70000 polygons in 5 seconds on pentium 3 in Autocad. So that's about 3 seconds if you exclude Autocad.
First you need to find an intersection point.
Next thing you have to find where your point (x, y) lies and draw a horizontal or vertical line through it, so that your 2 lines (A, B, C) and (a, b, c) and a new horizontal/verical line form a triangle.
How to find if it's vertical or horizontal line:
Draw both horizontal and vertical lines through your (x, y) point and then check:
-for horizontal:
- if intersections for line A,B,C and your horizontal line and line a,b,c make this equation work (intersection with A,B,C).x < x < (intersection with a,b,c).x, then you know your inside. (you can switch A,B,C and a,b,c, just as long x is inside.
- similar for y, just check for y and not x.
So now you have a triangle and you know where it is (left, right, up, down).
for example if it's a right triangle (like the graph above). You take the x of intersection point and you ceil it (if it's on the left you floor it)..similar for y coordinate if you have up/down triangle.
Then you draw a scanline through it, that's paralel to your scanline through your (x,y) point and check if you have a point inside of the intersections (similar to x < x < x above, but with a new line).
If you don't have an integer inside, then you have to move your ceil point further away from intersection point. You should calculate apropriate 'step' based on the angle between your two lines (if the lines are paralel and very close to each other then the angle will be small, so you have to increse the step, if the angle is wide, small step is required.
When you find a point, it may not be the closest one. So you'll have to do a bisection between the last not good point (when you're increasing step) and the last point (where you found an integer).
The problem of checking whether a point is part of a mathematical cone is fairly simple. Given 2 vectors, v, w, any point in the cone defined by (v, w) will be on the form: z = a***v** + b***w**, where a,b >= 0. Note, for this to work, you will have to move Origo to the intersection of the 2 lines. Since we cannot assume finite precision of the intersection, you will have to do floating point math and decide whether something is close enough to what you want.
Find vectors defining the 4 cones (there's infinitely many of them, we just need 2 for each cone), that are defined by the 2 lines.
Find out which cone contains our point, call that cone for C.
Take the 2 vectors defining C, and find the median vector (the vector that would split C in 2 identical cones), call it m.
Now is time to initiate the loop. For simplicity sake I'm going to assume that we limit ourself to n-bits on the x and y axis. Note you'll need an integer larger than n-bits for the length of m. Now do a binary search along the length of m, and check the 2 rings around every time (I suspect 1 ring around will be enough). When you've found the smallest length that do contain points C, check which of those points are the closest.
The worst case growth would be O(log(sqrt(2*n^2)), where n is the length we use to represent the x and y axis.
It is possible to do a "reverse binary search" so to speak, if you don't know the length of *m. Just keep doubling the the length you go out until you find points in C. Then you know 2 points on m and you can do a binary search between them.
The main problem with all this is precision, so keep this in mind. Alternative ways to pursue could include the 2 halfplanes that make up a cone. Each cone above are defined by the intersection of 2 halfplanes, and it is possible that checking whether a point is member of a halfplane is simple enough, I'm not sure.
Edit: it is indeed a whole lot easier with the half planes, the 2 lines divide R^2 into 2 half planes each, this gives the 4 combinations that would be the 4 cones. So every time you want to check if a point is member of a cone, you have to check if it's a member of the 2 half planes that make up that particular cone. How to do so are explained here:
http://www.mathsteacher.com.au/year9/ch04_linear_graphs/07_half/planes.htm
and would be easier than moving Origo and fiddling around with precision. Replacing the method of checking membership and keeping everything else the same, you arrive at the same growth.
Here is a linear time (i.e., O(# bits of A, B, C, etc.), assuming the bits fit into O(1) words of memory) solution using line-side tests and binary search:
Suppose w.l.o.g. that B != 0 (else we swap A with a, B with b, and C with c). Perform a line-side test to see which side of line (A, B, C) the point is on. Assume w.l.o.g. that the point is below (or on) the line.
Note that for an arbitrary x-coordinate x', we can compute the smallest y' such that (x', y') is above the line (A, B, C) in O(1) time via y' = (C - A * x') / B.
Now, assume w.l.o.g. that the input point (x, y) is to the right of (a, b, c), or below in the case of a horizontal line. We can then perform a line-side test of (x', y') relative to line (a, b, c) and determine whether we need to increase x' or decrease x' to find the minimum x' such that (x', y') falls on the correct side of (a, b, c).
Binary searching for this point takes at most O(w) time where w is the number of bits in A, B, etc. Note that because the input coordinates x and y each fit in an integer, so will the return value. Even if x and y were not necessarily within these bounds and the lines were nearly parallel, a suitable x will be found within O(w) time because the difference in slopes is A / B - a / b = (Ab - aB) / Bb <= 1 / 2^(2w), so the x-coordinate of the answer will fit within O(1) words of memory. We still need to find the y-coordinate of the answer, which can also be found via binary search.
I suspect this is a mathematical optimization problem that can be solved with a Lagrange multiplier...
Of those four pieces of the plane, one is to the left of both lines, one is to the right of both lines, one is to the right of one and to the left of the other line, and the last one is to the left of one and to the right of the other line. It's easier to see if you draw it.
The relative position of a point from a line depends on the result of this determinant:
[1 p1x p1y; 1 p2x p2y; 1 p3x p3y], where p1 and p2 are two arbitrary points in the line and p3 is the given point.
If it equals zero, the point is in the line, if it's greater of lower than zero, it's to a side, the side depends on the relative position of p1 and p2 in the line and what you consider left and right on the plane.
The problem is choosing two points that follow the same criteria in both lines, so that results are consistent, maybe p1 always has lower value of x coordinate than p2 (y coordinate if the line is vertical).
When you have both points for each line, calculate both determinants and you are done.
EDIT
Ups, this solves the problem partially. Anyway you can calculate the side the XY point is in with this, calculate the intersection, and then calucate the relative position of all valid points (floor(x), floor(y)), (floor(x), ciel(y)), ...
line 1 is defined as y1 = m1 * x1 + b1.
line 2 is defined as y2 = m2 * x2 + b2.
m1, m2, b1, b2 are all known values [constants].
make sure m1 <> m2.
find point of intersection, ie where y1 == y2 and x1 == x2 , defined as (X,Y).
Y = ((m1*b2)/m2)/(m1/m2-1)
X = (Y-b1)/m1
the nearest point can be found by rounding X and Y to the nearest integers. you decide what to do with .5
My proposal is this. Assume that the section of the plane which contains our target point spans entirely in lower left quadrant, looking from the cross point of two lines (other quadrants are analogous, and case when section of plane spans more than one quadrant will be considered later).
Let the two given lines be l1 and l2 (l1 is 'less steep' than l2)
find X = (a, b), the cross point of l1 and l2.
let k = 0
let vk be vertical line with the x coordinate xk = floor(a-k)
find cross points of vk with l1 and l2 (points V1 = (x1, y1), V2 = (x2, y2)).
if floor(y1) != floor(y2), target point is (x1, floor(y1)) END.
if floor(y1) == floor(y2), increment k and go to step 3.
Since l1 and l2 are not parallel, abs(y1 - y2) must grow with k. When abs(y1 - y2) gets larger than 1, algorithm will surely stop (it might stop earlier though).
Now let us consider the (easy) case when our section of plane spans more than one quadrant, looking from the cross point of two lines (it may span two or three quadrants).
find X = (a, b), the cross point of l1 and l2.
find A, the set of four closest points to X that have integer coordinates
find B, the set of points from A which are in the target section of the plane.
point from B that is closest to the cross point of l1 and l2 is the target point
(This case runs in constant time.)
I have a list of two-dimensional points and I want to obtain which of them fall within a semi-circle.
Originally, the target shape was a rectangle aligned with the x and y axis. So the current algorithm sorts the pairs by their X coord and binary searches to the first one that could fall within the rectangle. Then it iterates over each point sequentially. It stops when it hits one that is beyond both the X and Y upper-bound of the target rectangle.
This does not work for a semi-circle as you cannot determine an effective upper/lower x and y bounds for it. The semi-circle can have any orientation.
Worst case, I will find the least value of a dimension (say x) in the semi-circle, binary search to the first point which is beyond it and then sequentially test the points until I get beyond the upper bound of that dimension. Basically testing an entire band's worth of points on the grid. The problem being this will end up checking many points which are not within the bounds.
Checking whether a point is inside or outside a semi-circle (or a rectangle for that matter) is a constant-time operation.
Checking N points lie inside or outside a semi-circle or rectangle is O(N).
Sorting your N points is O(N*lg(N)).
It is asymptotically faster to test all points sequentially than it is to sort and then do a fast culling of the points based on a binary search.
This may be one of those times where what seems fast and what is fast are two different things.
EDIT
There's also a dead-simple way to test containment of a point in the semi-circle without mucking about with rotations, transformations, and the like.
Represent the semi-circle as two components:
a line segment from point a to b representing the diameter of the semi-circle
an orientation of either left-of or right-of indicating that the semi-circle is either to the left or right of line segment ab when traveling from a to b
You can exploit the right-hand rule to determine if the point is inside the semicircle.
Then some pseudo-code to test if point p is in the semi-circle like:
procedure bool is_inside:
radius = distance(a,b)/2
center_pt = (a+b)/2
vec1 = b - center_pt
vec2 = p - center_pt
prod = cross_product(vec1,vec2)
if orientation == 'left-of'
return prod.z >= 0 && distance(center_pt,p) <= radius
else
return prod.z <= 0 && distance(center_pt,p) <= radius
This method has the added benefit of not using any trig functions and you can eliminate all square-roots by comparing to the squared distance. You can also speed it up by caching the 'vec1' computation, the radius computation, center_pt computation, and reorder a couple of the operations to bail early. But I was trying to go for clarity.
The 'cross_product' returns an (x,y,z) value. It checks if the z-component is positive or negative. This can also be sped up by not using a true cross product and only calculating the z-component.
First, translate & rotate the semi-circle so that one end is on the negative X-axis, and the other end is on the positive X-axis, centered on the origin (of course, you won't actually translate & rotate it, you'll just get the appropriate numbers that would translate & rotate it, and use them in the next step).
Then, you can treat it like a circle, ignoring all negative y-values, and just test using the square root of the sum of the squares of X & Y, and see if it's less than or equal to the radius.
"Maybe they can brute force it since they have a full GPU dedicated to them."
If you have a GPU at your disposal, then there are more ways to do it. For example, using a stencil buffer:
clear the stencil buffer and set the stencil operation to increment
render your semicircle to the stencil buffer
render your points
read back the pixels and check the values at your points
the points that are inside the semicircle would have been incremented twice.
This article describes how stencil buffers can be used in OpenGL.
If there's a standard algorithm for doing this, I'm sure someone else will come up with it, but if not: you could try sorting the points by distance from the center of the circle and iterating over only those whose distance is less than the semicircle's radius. Or if computing distance is expensive, I'd just try finding the bounding box of the semicircle (or even the bounding square of the circle of which the semicircle is part) and iterating over the points in that range. To some extent it depends on the distribution of the points, i.e. do you expect most of them or only a small fraction of them to fall within the semicircle?
You can find points in a circle and points on one side of a given slope, right?
Just combine the two.
Here's part of a function I wrote do get a cone firing arc for a weapon in a tile based game.
float lineLength;
float lineAngle;
for(int i = centerX - maxRange; i < centerX + maxRange + 1; i++){
if(i < 0){
continue;
}
for(int j = centerY - maxRange; j < centerY + maxRange + 1; j++){
if(j < 0){
continue;
}
lineLength = sqrt( (float)((centerX - i)*(centerX - i)) + (float)((centerY - j)*(centerY - j)));
lineAngle = lineAngles(centerX, centerY, forwardX, forwardY, centerX, centerY, i, j);
if(lineLength < (float)maxRange){
if(lineAngle < arcAngle){
if( (float)minRange <= lineLength){
AddToHighlightedTiles(i,j);
}
}
}
}
}
The variables should be self explanatory and the line angles function takes 2 lines and finds the angle between them. The forwardX and forwardY is just one tile in the correct direction from the center X and Y based on what angle you're pointing in. Those can be gotten easily with a switch statement.
float lineAngles(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4){
int a = x2 - x1;
int b = y2 - y1;
int c = x4 - x3;
int d = y4 - y3;
float ohSnap = ( (a * c) + (b * d) )/(sqrt((float)a*a + b*b) * sqrt((float)c*c + d*d) );
return acos(ohSnap) * 180 / 3.1415926545f;
}
It would appear that a simple scheme will work here.
Reduce the number of points in the set, by first computing the convex hull. Only the points on the convex hull will contribute to any interaction with any convex bounding shape. So retain only the subset of points on the perimeter of the hull.
It can easily be argued that the minimal radius bounding semi-circle must have one edge (two points) of the convex hull coincident along the diameter of the semi-circle. I.e., if some edge of the hull does not lie in the diameter, then there exists a different semi-circle with smaller diameter that contains the same set of points.
Test each edge in sequence. (A convex hull often has relatively few edges, so this will go quickly.) Now it becomes a simple 1-d minimization problem. If we choose to assume the edge in question lies on the diameter, then we merely need to find the center of the sphere. It must lie along the current line which we are considering to be the diameter. So as a function of the position of the point along the current diameter, just find the point which lies farthest away from the nominal center. By minimizing that distance, we find the radius of the minimal semi-circle along that line as a diameter.
Now, just choose the best of the possible semi-circles found over all edges of the convex hull.
If your points have integer co-ordinates, the fastest solution may be a lookup table. Since a semicircle is convex, for each y co-ordinate, you get a fixed range of x, so each entry in your lookup table gives maximum and minimum X co-ordinates.
Of course you still need to precalculate the table, and if your semicircle isn't fixed, you may be doing that a lot. That said, this is basically one part of what would once have been done to render a semicircle - the full shape would be rendered as a series of horizontal spans by repeatedly calling a horizontal line drawing function.
To calculate the spans in the first place (if you need to do it repeatedly), you'd probably want to look for an old copy of Michael Abrash's Zen of Graphics Programming. That described Bresenhams well-known line algorithm, and the not-so-well-known Hardenburghs circle algorithm. It shouldn't be too hard to combine the span-oriented versions of the two to quickly calculate the spans for a semi-circle.
IIRC, Hardenburgh uses the x^2 + y^2 = radius^2, but exploits the fact that you're stepping through spans to avoid calculating square roots - I think it uses the fact that (x+1)^2 = x^2 + 2x + 1 and (y-1)^2 = y^2 - 2y + 1, maintaining running values for x, y, x^2 and (radius^2 - y^2), so each step only requires a comparison (is the current x^2 + y^2 too big) and a few additions. It's done for one octant only (the only way to ensure single-pixel steps), and extended to the full circle through symmetry.
Once you have the spans for the full circle, it should be easy to use Bresenhams to cut off the half you don't want.
Of course you'd only do all this if you're absolutely certain that you need to (and that you can work with integers). Otherwise stick with stbuton.
translate the center of the arc to the origin
compute angle between ordinate axis and end points of radii of semi-cirlce
translate the point in question by same dx,dy
compute distance from origin to translated x,y of point, if d > radius of circle/arc eliminate
compute angle between ordinate axis and end point
if angle is not between starting and ending arc of semi-cirlce, eliminate
points remaning should be inside semi-circle
I guess someone found the same solution as me here but I have no code to show as its pretty far in my memory...
I'd do it by steps...
1. I'd look if i'm within a circle... if yes look on which side of the circle.
2. By drawing a normal vector that come from the vector made by the semi-sphere. I could know if I'm behind or in front of the vector...and if you know which side is the semi sphere and which side is the void...It will be pretty damn easy to find if you're within the semi sphere. You have to do the dot product.
I'm not sure if it's clear enough but the test shouldn't be that hard to do...In the end you have to look for a negative or positive value...if it's 0 you're on the vector of the semisphere so it's up to you to say if it's outside or inside the semi-sphere.
The fastest way to do this will depend on your typical data. If you have real-world data to look at, do that first. When points are outside the semi-circle, is it usually because they are outside the circle? Are your semi-circles typically thin pie slices?
There are several ways to do this with vectors. You can scale the circle to a unit circle and use cross-products and look at the resultant vectors. You can use dot-products and see how the prospective point lands on the other vectors.
If you want something really easy to understand, first check to make sure it's inside the circle, then get the angle and make sure it's between the angle of the two vectors that dictate the semi-circle.
Edit: I had forgotten that a semicircle is always half a circle. I was thinking of any arbitrary section of a circle.
Now that I have remembered what a semicircle is, here's how I would do that. It's similar to stbuton's solution, but it represents the semicircle differently.
I'd represent the semicircle as the unit vector that bisects the semicircle. You can easily get that from either one of the vectors that indicate the boundary of the semicircle (because they are 90 degrees away from the representation) by swapping x and y and negating one of them.
Now you just cross the vector created by subtracting the point to be tested from the circle's center. The sign of z tells you whether the point is in the semicircle, and the length of z can be compared against the radius.
I did all the physics for Cool Pool (from Sierra Online). It's all done in vectors and it's filled with dots and crosses. Vectors solutions are fast. Cool Pool was able to run on a P60, and did reasonable breaks and even spin.
Note: For solutions where you're checking sqrt(xx+yy), don't even do the sqrt. Instead, keep the square of the radius around and compare against that.