We're given the coordinates of 4 points on the 2D plane. How can we find an order to join them with lines to form a quadilateral (whenever it's possible)?
Consider the partition of the plane obtained by drawing the three lines defined by the first three points. It defines 7 regions. You can easily find to which region the fourth point belongs by means of three signed area tests (algebraic area of the triangles ABD, BCD, CAD).
Drawing a quadrilateral in every case is straightforward (there can be one, two or three solutions per case).
In the example below, with D in the region -++, ADBC will do.
Actually two area evaluations are enough: if the first test returns - (regions -+-, -++ or --+), ADBC is a solution, else if the second test returns - (regions +-+ or +--), ABDC is a solution, else (regions ++- or +++) ABCD is a solution .
Consider the affine transform
Px = Ax + u (Bx - Ax) + v (Cx - Ax)
Py = Ay + u (By - Ay) + v (Cy - Ay)
It maps (0, 0) to A, (1, 0) to B and (0, 1) to C. (This puts the triangle ABC in a canonical position.)
Solving the 2x2 linear system
Dx = Ax + u (Bx - Ax) + v (Cx - Ax)
Dy = Ay + u (By - Ay) + v (Cy - Ay)
gives you the values of (u, v) corresponding to D.
Then,
if u < 0 => ABCD
else if v < 0 => BCAD
else => CABD
The resulting quadrilateral has the same orientation as the triangle ABC.
For the sake of clarity, I'm considering as a point p_n a point with coordinates (x_n, y_n).
In order to connect 4 points you could follow these steps:
Get the point p_1 with the smallest x.
Calculate the slope of the 3 lines that go from p_1 to each of the remaining points.
Connect p_1 with the point p_2 that composes the line with the greatest slope.
Connect p_1 with the point p_3 that composes the line with the smallest slope.
Connect the remaining point p_4 with p_2 and p_3.
Let me know if something is unclear.
Edit: This answer has already proven to be wrong.
Calculate the center of the four points.
Enumerate the points in the counter-clock wise direction (or clockwise).
Link them together.
EDIT: As pointed out in the comments, it only works in specific situations and thus is a bad answer.
The quadrilateral can be drawn by finding which pair of points amongst the 4 points is separated by the largest distance. Once this pair is found, the quadrileteral is drawn by linking the two remaining points with each point of the pair.
You can use a simplification of any of the well-known algorithms for convex hull. Jarvis would be easy. If the convex hull is a triangle, the quadrilateral is convex. Just insert the missing point anywhere in the edge list. If the convex hull is a line (2 endpoints), just sort all the points on either x- or y-coordinate to get a degenerate quadrilateral. (If closer to horizontal (abs delta x > abs delta y), use x for sorting, else use y.)
i think, you just need to join two points each time, we will get a line segment and check if the remaining other two points are on the same side of that line segments, if not thats not a line segment of required quadrilateral, if yes then proceed with different set of points (i.e on point can be one of the coordinate of this segment and other one would be form remaining two points) and check the same thing until u get 4 line segments
Look around for convex hull algorithms. One of them consists of two steps: building an ordinary polygon on a given set of vertices (which may be concave), then removing 'concave vertices' so that the remaining polygon becomes convex.
The first step is a solution for your problem.
Of course it is kind of overkill. For 4 vertices just set them in a sequence (in any order) then verify if line segments joining points 1-2 and 3-4 intersect; if so, swap points 2 and 3; or possibly edges 2-3 and 1-4 intersect – then swap points 3 and 4. Done.
To verify if segment AB and CD intersect test if points A and B are on opposite sides of the line CD and points C and D are on opposite sides of line AB.
To identify the side of a line PQ where a point K lies, calculate the Z-part of the PQ×PK vector product: (xq-xp)(yk-yp)-(yq-yp)(xk-xp). The expression is positive on one side of the line and negative on the other one (and zero on the line).
One can solve this problem very easily with the help of a function that says whether two line segments intersect.
Given points A, B, C, D, there's only three different orders to join up the vertices: ABCD, ABDC and ACBD (vertex A either connects to vertex B or it doesn't. If it does, there's two ways to order C and D. If it doesn't then A connects to both C and D and each of those must connect to B).
An ordering of the four points produces a quadrilateral if none of the edges intersect (except at the corners). That gives this procedure for finding a working order:
If AB intersects with CD then return ACBD.
If AD intersects with BC then return ABDC.
Otherwise return ABCD.
The proof that this works is easy:
both ABCD and ABDC include the edges AB and CD, so if those pair of edges intersect, the good order must be ACBD.
both ABCD and ACBD include the edges AD and BC, so if those pair of edges intersect, the good order must be ABDC.
if neither AB/CD and AD/BC intersect, then the order ABCD produces a quadrilateral.
The code for determining if two line segments intersect can be found online if you can't figure it out yourself.
Here is a trivial, non-mathematical way of doing it. I've tried it on several examples and could not prove it wrong. Please let me know if you do or know how to make it better:
Choose the two points that are on the right of the others, say point 1 and 2.
Link points 1-2 together, as well as points 3-4.
Each point must be linked to only two other points so there are only 2 possibilities: linking points 1-3 and 2-4, or points 1-4 and 2-3.
Check if each line intersects another.
That should do it.
Note: when choosing the first two points, here are some special cases:
Point 1 is on the right of all others, but points 2 and 3 are on the same x coordinate. Pick the point that's closest to point 1.
If three points 1,2 and 3 have the same x-coordinate, link the two that are closest to each other. If the points are evenly separated, pick one of the two possibilities.
A few stages, assuming the end result needs to be 4 couples of points (and/or the equation of the line between them):
Take any three points, and make a triangle.
If the forth point is inside the traingle, swap it with ny of the three.
Having only the last point left, calculate which two point you want to attach it to. This is done by playing with the line eqations and finding where the intersecting points are. Clarification below.
Return two sides of the triangle + the 2 lines between the forth point and the ones you chose from stage 2.
Step 2 clarification:
Let us say that point A is not in the triangle (which is BCD). Every line devides the plain into two sides. we want to find the point (B, C or D) s.t. the line between it and A runs between the other two (they are on opposite sides of the line). This is the point we DO NOT want to attach to A.
Example:
Given A(0,0), B(10,0), C(10,10) and D(0,10). We have the triangle BCD. The line BC leaves A & D on the same side of the plain. So does DC. The line AC leaves B & D on opposite sides of the plain - so we want to connect A to B & C.
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.