How to find transformation matrix aligning two lines? - animation

Given two points in 3D space, A and B, I get a line segment LS. Given two new points A' and B' yielding the line segment LS', I need to find the transformation matrix that transforms LS into LS'. The length of the line segments is assumed to be equal.
I have a theory on how to calculate the matrix, but I would really like some feedback from you excellent folks on whether it is a good theory or if there exists some better approach.
Here's my algorithm:
Let L and L' be the lines parallel to LS and LS'
Find the point P where L intersects L'
Find the angle V between L and L'
The final transformation matrix will be:
translate(-P) * rotate(V) * translate(p)
Some background for the curious: I'm building this in XNA, though the math problem should be quite general. The line segment is part of a larger structure of connected segments. For each segment I will pre-calculate a transformation matrix per animation frame.

Instead of finding intersection traslating A to A' would be sufficient, I guess. Then you probably will require 2 rotations, one to make both lines on the same plane, and the other to actually align them.

Related

Need a programming algorithm for a tight enclosure around line segments

I have a list of perhaps hundreds of unsorted 2D line segments at random angles as shown in blue above, and I need to determine the sorted sequence of points a – i from them as shown in red. Here are some constraints and observations:
The red polyline sequence a - i forms a tight enclosure of the outer
points of all the segments. It's like a convex hull, but "sucked"
into concavities. A tight enclosure of the entirety of all segments
would be a fine solution; I can delete the unused parts.
An enclosure does exist. The segments are not completely random.
The y value of one point of each segment is 0, and the other end is a
point on the enclosure.
The enclosure cannot cross any line segment or itself. This also
means that all segments are on one side of the enclosure polyline.
There will be either one or two enclosure segments connected to each
enclosure point a - i.
The points may be spaced widely differently, not as tidily as shown.
I should be able to determine point a or i if necessary to start off
the algorithm.
It seems like starting from a bounding box around all segments and shrinking it to form a tight enclosure would be a reasonable approach, but I can't come up with a decent algorithm. This will eventually be coded in C#, but an algorithm or pseudo-code would be fine.
History: this answer is now marked community wiki. The original conjecture was proven wrong by #dont talk just code. #Matt Timmermans provided the current conjecture in a comment under the question.
The following conjecture is submitted without proof: To compare the order of two line segments, compare the bottom x, and return the opposite if the segments cross.
If the conjecture holds, a comparator can be written for use with any comparison sort. Pseudocode for the comparator:
compare(segment A, segment B)
if A crosses B
return B.bottom.x - A.bottom.x
else
return A.bottom.x - B.bottom.x
In the event that two line segments have the same bottom.x, their order can be determined by comparing the angles that they make with the x-axis, e.g.
The segment with the larger angle is first. The atan2 function can be used to compute the angles.
Note that the conjecture definitely does not hold for all possible arrangements. The line segments must be constrained so that a solution exists. Here's an example where a solution does not exist (because C cannot be connected to A or B without crossing a blue line):
A and B cross, so reversing the x comparison, A is before B. C doesn't cross A or B, so comparing the x values at the bottom, B is before C and C is before A. This creates a rock-paper-scissors situation: A before B, B before C, and C before A. Such intransitive comparisons will cause a comparison sort to fail miserably.

Intersect n line segments (on integer grid)

I have a problem that looks almost like a classic CS problem of finding all intersection points of given line segments.
The slight modification is the fact that:
1. I need to split all the segments at their intersection points
2. The resulting split segments must have integer coordinates.
If I just apply standard sweepline algorithm to find all the intersection points, and than cast coordinates of these points to integers, I sometimes get new intersections caused by the moving of intersections points to the integer grid.
I may apply this algorithm repeatedly, and probably (I can't prove it), in limited number of steps reach a state where I find no new intersections.
But I'm convinced there must be a simpler, more elegant solution.
I was trying to find a paper about such algorithm, but somehow couldn't find one that would deal with exactly this problem.
Can you tell point me to such a pape, or a description of an algorithm used by graphics libraries (such as Boost Polygon Library)?
Thank you.
This is an interesting variation to Line-segment intersection problem. Original problem of finding co-ordinates of point of intersection of these segments can be done solved using Line Sweep algorithm.
Here is an in depth article talking about Line-Sweep technique implementation for above problem. With this, intersection can be found in O(n*logn) time.
Now, in order to find the integer co-ordinates, you can cast the intersection points. But here you need to make sure about the direction of casting (which will later help in convergence).
If C in an intersection point on line segment AB, then split it into AC and CB. In AC, you cast C in direction of A, while in CB you cast it in direction of B. (By direction, I don't mean along the line segment direction, but along the half plane containing another end point.) This ensures that length of line segment will be reduced after each intersection.
PROOF: Consider, M to be maximum length of an line segment. Every time an intersection point lies on it, the length of new line segment is reduced by at least one unit. So number of iterations is bounded by M
Thus, overall iterations of your algorithm cannot exceed M.
Complexity = O(M* n *logn)

segment intersecting a tetrahedron

I am trying to write C++ code to find the intersection points of a segment intersecting a tetrahedron. I reduced the problem like this:
For each face of the tetrahedron (a triangle), find the intersection point of the line segment. Then, I have three cases:
a) The segment doesn't intersect any face - thus either the segment is entirely in the tetrahedron or completely outside.
b) The segment only intersects one face. Then I just need to determine the side of the segment that is in the tetrahedron and I get the two points that are in the tetrahedron.
c) The segment intersects two faces.
I am having trouble implementing this algorithm. Here are the issues:
If the segment and triangle are in the same plane, how do I find the intersection points?
How can I determine if the segment lies on one of the edges of the tetrahedron?
Thanks.
Hint:
You can't avoid a complex case discussion. Here I introduce the planar case of a line segment and a triangle.
The sides of the triangle define three straight lines that partition the plane in 7 regions, one bounded and 6 unbounded. On the figure, they are designated by the signs obtained when you plug the coordinates of a point in the three implicit equations of these lines.
If you need to consider endpoints exactly on a side, you need to add 6 half-lines and 3 segments to the discussion.
Then take all possible combinations of the starting and ending regions.
Many of the cases are straightforward. When the two segment endpoint belong to the same region, the segment is wholly inside or outside; when one of the regions is +++ and the other is different, there is exactly one intersection...
In the case of the figure (--+ to ++-), you are sure to have one intersection with the bottom edge; but which is the other intersected side is unsure: to answer this, you need to tell on what side of the line segment the top vertex lies.
With some courage, you can discuss all 16 x 15 / 2 = 120 cases, many of which are identical to a permutation of the elements.
This is just an appetizer compared to the 3D problem.
"How can I determine if the segment lies on one of the edges of the tetrahedron?"
Write a function that computes the area of the triangle determined by three points in space. This can be computed from a determinant, as explained here and many other sites.
Then write a function that determines if two segments ab and cd are collinear.
They are if and only if the area of abc is zero, and the area of abd is zero.
Finally, write a function that determines if one point c lies on the segment ab. With all this, the remainder is easy.
To answer the general question, i.e. how to find the (up to two) intersections between a segment and a tetrahedron, I'd prefer to avoid the painful case-by-case analysis (mentioned in your problem reduction and in another answer).
I would use a variant of Sutherland-Hogdman's reentrant clipping (explained in 2D in [1]): the idea is to consider the tetrahedron as the intersection between four oriented half-spaces (limited by the support planes of the four faces of the tetrahedron).
Thus to compute the intersection between a segment and a tetrahedron, you can proceed as follows:
S := your segment
for f := 0 to 3 {
H := half_space(tet, f)
S := intersect(S, H)
}
H is just a plane equation (coefficients a,b,c,d of equation ax+by+cz+d=0,
[a,b,c] is the normal to the facet, oriented towards the interior of the tetrahedron. d is obtained by injecting a vertex incident to the facet into the equation).
The function intersect() is simple to implement (just test the sign of ax+by+cz+d at both vertices of the segment, if they differ, there is an intersection, that can be computed by injecting a parametric equation of S
x=x1+t(x2-x1), y=y1+t(y2-y1), z=z1+t(z2-z1) into (ax+by+cz+d=0) and solving for t, where (x1,y1,z1) and (x2,y2,z2) denote the two extremities of S.
In addition, the function intersect() may compute two booleans, to keep track of which vertex of S is a generated intersection.
[1] https://en.wikipedia.org/wiki/Sutherland%E2%80%93Hodgman_algorithm

Closest pair of points across a line

I have two sets of 2D points, separated from each other by a line in the plane. I'd like to efficiently find the pair of points, consisting of one point from each set, with the minimum distance between them. There's a really convenient looking paper by Radu Litiu, Closest Pair for Two Separated Sets of Points, but it uses an L1 (Manhattan) distance metric instead of Euclidean distance.
Does anyone know of a similar algorithm which works with Euclidean distance?
I can just about see an extension of the standard divide & conquer closest pair algorithm -- divide the two sets by a median line perpendicular to the original splitting line, recurse on the two sides, then look for a closer pair consisting of one point from each side of the median. If the minimal distance from the recursive step is d, then the companion for a point on one side of the median must lie within a box of dimensions 2d*d. But unlike with the original algorithm, I can't see any way to bound the number of points within that box, so the algorithm as a whole just becomes O(m*n).
Any ideas?
Evgeny's answer works, but it's a lot of effort without library support: compute a full Voronoi diagram plus an additional sweep line algorithm. It's easier to enumerate for both sets of points the points whose Voronoi cells intersect the separating line, in order, and then test all pairs of points whose cells intersect via a linear-time merge step.
To compute the needed fragment of the Voronoi diagram, assume that the x-axis is the separating line. Sort the points in the set by x-coordinate, discarding points with larger y than some other point with equal x. Begin scanning the points in order of x-coordinate, pushing them onto a stack. Between pushes, if the stack has at least three points, say p, q, r, with r most recently pushed, test whether the line bisecting pq intersects the separating line after the line bisecting qr. If so, discard q, and repeat the test with the new top three. Crude ASCII art:
Case 1: retain q
------1-2-------------- separating line
/ |
p / |
\ |
q-------r
Case 2: discard q
--2---1---------------- separating line
\ /
p X r
\ /
q
For each point of one set find closest point in other set. While doing this, keep only one pair of points having minimal distance between them. This reduces given problem to other one: "Algorithm to find for all points in set A the nearest neighbor in set B", which could be solved using sweep line algorithm over (1) one set of points and (2) Voronoi diagram for other set.
Algorithm complexity is O((M+N) log M). And this algorithm does not use the fact that two sets of points are separated from each other by a line.
well what about this:
determine on which side any point is:
let P be your points (P0,...Pi,...Pn)
let A,B be the separator line start-end points
so: side(Pi) = signum of ((B-A).(Pi-A))
this is based on simple fact that signum of scalar vector multiplication (dot product) depends on the order of points (see triangle/polygon winding rule for more info)
find minimal distance of any (Pi,Pj) where side(Pi)!=side(pj)
so first compute all sides for all points O(N)
then cycle through all Pi and inside that for
cycle through all Pj and search for min distance.
if the Pi and Pj groups aprox. equal size tahn it is O((N/2)^2)
you can further optimize the search by 'sort' the points Pi,Pj by 'distance' from AB
you can use another dot product to do that, this time instead (B-A)
use perpendicular vector to it let say (C-A)
discard all points from Pi2 (and similar Pj2 also)
where ((B-A).(P(i1)-A)) is close to ((B-A).(P(i2)-A))
and |((C-A).(P(i1)-A))| << |((C-A).(P(i2)-A))|
beacuese that means that Pi2 is behind Pi1 (farer from AB)
and close to the normal of AB going near Pi1
complexity after this optimization strongly depend on the dataset.
should be O(N+(Ni*Nj)) where Ni/Nj is number of remaining points Pi/Pj
you need 2N dot products, Ni*Nj distance comparision (do not neet to be sqrt-ed)
A typical approach to this problem is a sweep-line algorithm. Suppose you have a coordinate system that contains all points and the line separating points from different sets. Now imagine a line perpendicular to the separating line hopping from point to point in ascending order. For convenience, you may rotate and translate the point set and the separating line such that the separating line equals the x-axis. The sweep-line is now parallel with the y-axis.
Hopping from point to point with the sweep-line, keep track of the shortest distance of two points from different sets. If the first few points are all from the same set, it's easy to find a formula that will tell you which one you'll have to remember until you hit the first point from the other set.
Suppose you have a total of N points. You will have to sort all points in O(N*log(N)). The sweep-line algorithm itself will run in O(N).
(I'm not sure if this bears any similarity to David's idea...I only saw it now after I logged in to post my thoughts.) For the sake of the argument, let's say we transposed everything so the dividing line is the x axis and sorted our points by the x coordinate. Assuming N is not too large, if we scan along the x-axis (that is, traverse our sorted list of a's and b's), we can keep a record of the overall minimum and two lists of passed points. The current point in the scan is tested against each passed point from the other list while the distance from the point in the list to (x-coordinate of our scan,0) is greater than or equal to the overall min. In the example below, when reaching b2, we can stop testing at a2.
scan ->
Y
| a2
|
| a1 a3
X--------------------------
| b1 b3
| b2

Given two lines on a plane, how to find integer points closest to their intersection?

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.)

Resources