How can I programmatically detect whether or not two triangles touch each other, given their vertices on a 2D coordinate plane? This includes touching points or edges, as well as if one triangle is completely inside the other one.
You can prove that the two triangles do not collide by finding an edge (out of the total 6 edges that make up the two triangles) that acts as a separating line where all the vertices of one triangle lie on one side and the vertices of the other triangle lie on the other side. If you can find such an edge then it means that the triangles do not intersect otherwise the triangles are colliding.
Here is a Matlab implementation of the triangle collision function. You can find the theory of the sameside function here: http://www.blackpawn.com/texts/pointinpoly/default.html
function flag = triangle_intersection(P1, P2)
% triangle_test : returns true if the triangles overlap and false otherwise
% P1, P2: a 3 by 2 array (each), describing the vertices of a triangle,
% the first column corresponds to the x coordinates while the second column
% corresponds to the y coordinates
function flag = sameside(p1,p2,a,b)
% sameside : returns true if the p1,p1 lie on same sides of the
% edge ab and false otherwise
p1(3) = 0; p2(3) = 0; a(3) = 0; b(3) = 0;
cp1 = cross(b-a, p1-a);
cp2 = cross(b-a, p2-a);
if(dot(cp1, cp2) >= 0)
flag = true;
else
flag = false;
end
end
% Repeat the vertices for the loop
P1(4:5,:) = P1(1:2,:);
P2(4:5,:) = P2(1:2,:);
flag = true;
% Testing all the edges of P1
for i=1:3
if(~sameside(P1(i,:), P2(1,:), P1(i+1,:), P1(i+2,:)) ...
&& sameside(P2(1,:), P2(2,:), P1(i+1,:), P1(i+2,:)) ...
&& sameside(P2(2,:), P2(3,:), P1(i+1,:), P1(i+2,:)))
flag = false; return;
end
end
% Testing all the edges of P2
for i=1:3
if(~sameside(P2(i,:), P1(1,:), P2(i+1,:), P2(i+2,:)) ...
&& sameside(P1(1,:), P1(2,:), P2(i+1,:), P2(i+2,:)) ...
&& sameside(P1(2,:), P1(3,:), P2(i+1,:), P2(i+2,:)))
flag = false; return;
end
end
end
In short, Hassan's answer is fastest.
https://jsfiddle.net/eyal/gxw3632c/
This is the fastest code in javascript:
// check that all points of the other triangle are on the same side of the triangle after mapping to barycentric coordinates.
// returns true if all points are outside on the same side
var cross2 = function(points, triangle) {
var pa = points.a;
var pb = points.b;
var pc = points.c;
var p0 = triangle.a;
var p1 = triangle.b;
var p2 = triangle.c;
var dXa = pa.x - p2.x;
var dYa = pa.y - p2.y;
var dXb = pb.x - p2.x;
var dYb = pb.y - p2.y;
var dXc = pc.x - p2.x;
var dYc = pc.y - p2.y;
var dX21 = p2.x - p1.x;
var dY12 = p1.y - p2.y;
var D = dY12 * (p0.x - p2.x) + dX21 * (p0.y - p2.y);
var sa = dY12 * dXa + dX21 * dYa;
var sb = dY12 * dXb + dX21 * dYb;
var sc = dY12 * dXc + dX21 * dYc;
var ta = (p2.y - p0.y) * dXa + (p0.x - p2.x) * dYa;
var tb = (p2.y - p0.y) * dXb + (p0.x - p2.x) * dYb;
var tc = (p2.y - p0.y) * dXc + (p0.x - p2.x) * dYc;
if (D < 0) return ((sa >= 0 && sb >= 0 && sc >= 0) ||
(ta >= 0 && tb >= 0 && tc >= 0) ||
(sa+ta <= D && sb+tb <= D && sc+tc <= D));
return ((sa <= 0 && sb <= 0 && sc <= 0) ||
(ta <= 0 && tb <= 0 && tc <= 0) ||
(sa+ta >= D && sb+tb >= D && sc+tc >= D));
}
var trianglesIntersect4 = function(t0, t1) {
return !(cross2(t0,t1) ||
cross2(t1,t0));
}
I wrote the above fiddle to test a few different techniques and compare the speed. All the techniques are based on some combination of three different tools:
Barycentric point-in-triangle test: Convert a point from x,y space to u,v space where u,v are two sides of the triangle. Then test if the point is inside the triangle (0,0) (0,1) (1,0), which is easy.
Same-side point-in-triangle test: This test tells you if an angle is more or less than 180 degrees. If the triangle is a,b,c and your point is p, you check if the angle pab and bac are both more or both less than 180. You need to do this for ab, bc, and ca. If any are true, the point is outside. This test is slower than barycentric for one point.
Line segment intersection: Check if the line segment a,b intersects line segment c,d. To do that, you find the point where the two lines cross and then check that those lines are in the bounding box of a,b and b,c. This is about as fast as Barycentric.
Those are the tools. Now to find out if triangles intersect, there are 3 ways that I tested:
8 line intersection and 2 point-in-triangle: You only need 8 line intersection and not all 9 because there can't be just 1 intersection. After that, you need 2 point-in-triangle in case 1 triangle is entirely inside the other.
6 line intersection and 4 point-in-triangle: If you draw it out, you can see that you can completely ignore one side of one of the triangles for line intersection and then just do all the other triangles points for point-in-triangle. Because line intersection and barycentric are about the same speed, this isn't much better than #1. But if you used same-side, it will be faster because same side point-in-triangle is slower.
9 same-side point-in-triangle tests: You can use either barycentric or same side. For either, you modify the point-in-triangle to test 3 points at the same time and you don't want to just test that they are all three outside the triangle, you need to test that they are all 3 outside on the same side. Because we're re-using a lot of information, we can save time in calculations. Again, barycentric seems slightly faster than same side here, too.
Use Line Line intersection
https://www.topcoder.com/community/data-science/data-science-tutorials/geometry-concepts-line-intersection-and-its-applications/#line_line_intersection
Also consider the possibility that some vertex might be touching one of the sides of the other triangle.
http://www.blackpawn.com/texts/pointinpoly/default.html
function SameSide(p1,p2, a,b)
cp1 = CrossProduct(b-a, p1-a)
cp2 = CrossProduct(b-a, p2-a)
if DotProduct(cp1, cp2) >= 0 then return true
else return false
function PointInTriangle(p, a,b,c)
if SameSide(p,a, b,c) and SameSide(p,b, a,c)
and SameSide(p,c, a,b) then return true
else return false
Or look at this link and scroll down
http://compsci.ca/v3/viewtopic.php?t=6034
Related
I am new to the Julia language and need to draw a circular sector on an image (2-dimensional UInt8 array for gray version or 3-dimensional UInt8 array for an RGB version). Afterwards this image is to be used as a mask to select data in other arrays, so I need the result, not as an image object, but as an array of booleans or integers.
There is the way to draw a circle by means of the ImageDraw package:
draw!(img, Ellipse(CirclePointRadius(350,200,100), fill = tue))
but found no way to provide a start and end angle.
You can use Luxor.jl's pie or sector function:
julia> begin
img = readpng("/path/Images/deepam.png")
Drawing(img.width, img.height, "sector-on-img.png")
placeimage(img)
origin()
sethue("orange")
pie(0, 0, 100, π/2, π, :fill)
sethue("olive")
sector(25, 125, 3π/2, 0, 15, :fill)
finish()
end
true
Result:
(Original png image scaled down, for comparison:
)
I think Julia is a great language, because (among other things) all libraries are implemented in the same language and you have ease acces to their sources.
And in this way, I have been able to modify the ellipse2d.jl script of the ImageDraw library.
The modification consits of adding another definition of the draw! funciton for ellipse objects (multiple dispatch of Julia is also great) that accepts a start and end angle.
I think the best way could be to define new objects, ellipse_sector and circle_sector, which would be the same as the ellipse and circle objects but with two more members: start_angle and end_angle. Then the correspondent drawing functions should be implemented. I would like to write to the ImageDraw package developers in order to make this suggestion or even offer me to make these changes, but I do not know the manage of github.
My solution, instead, does not modify any existing object, just adds a method to the draw! function that accpets two more arguments: startAngle and endAngle.
Here is the code, to be copied to the end of the ellipse2d.jl script:
function draw!(img::AbstractArray{T, 2}, ellipse::Ellipse, startAng::Real, endAng::Real, color::T) where T<:Colorant
# Solution to find out if an angle lies between two given ones, borrowed from:
# https://stackoverflow.com/questions/11406189/determine-if-angle-lies-between-2-other-angles/11412077#11412077
# Make all angles to lie in [0, 2π)
# rem2pi(ϕ, RoundNearest) returns the remainder of the division by 2π in the range [−π,π]
# mod2pi returns the remainder of the division by 2π in the range [0,2π)
Angle1 = mod2pi(startAng)
Angle2 = mod2pi(endAng)
# make the angle from angle1 to angle2 to be <= 180 degrees
rAngle = mod2pi( mod2pi(Angle2 - Angle1) + 2π)
if rAngle >= π
Angle1, Angle2 = Angle2, Angle1 # Swaps the values
end # if
ys = Int[]
xs = Int[]
break_point = 0
if ellipse.fill == false
break_point = ((ellipse.ρy - ellipse.thickness) / ellipse.ρy) ^ 2 + ((ellipse.ρx - ellipse.thickness) / ellipse.ρx) ^ 2
end
for i in ellipse.center.y - ellipse.ρy : ellipse.center.y + ellipse.ρy
for j in ellipse.center.x - ellipse.ρx: ellipse.center.x + ellipse.ρx
y = i - ellipse.center.y
x = j - ellipse.center.x
val = (x / ellipse.ρy) ^ 2 + (y / ellipse.ρx) ^ 2
# atan(y, x) returns the angle in the correct quadrant [−π,π], not like atan(y/x)
# But make it to be in the range [0, 2π)by means of mod2pi()
ang = mod2pi( atan(y, x) )
# Test if the angle lies betwen the startAngle and the endAngle
if (Angle1 <= Angle2)
AngleIsBetween = ang >= Angle1 && ang <= Angle2
else
AngleIsBetween = ang >= Angle1 || ang <= Angle2
end # if
if val < 1 && val >= break_point && AngleIsBetween
push!(ys, i)
push!(xs, j)
end
end
end
for (yi, xi) in zip(ys, xs)
drawifinbounds!(img, yi, xi, color)
end
img
end
Hello, I've got a question which I cannot solve so I need a bit help.
In the picture above you can see an Oriented Bounding Box specified by 4 points (A, B, C, D). There is also a point in space called P. If I cast a ray from P against the OBB the ray is going to intersect the OBB at some point. This point of intersection is called Q in the picture. By the way the ray is always going to be x-axis aligned which means its directional vector is either (1, 0) or (-1,0) if normalized. My goal is to find the point of intersection - Q. Is there a way (if possible computationaly inexpensive) to do so?
Thanks in advance.
One way to do this is to consider each side of the bounding box to be a linear equation of the form y = ax + b, where a is the slope and b is the y-intercept. Then consider the ray from P to be an equation of the form y = c, where c is a constant. Then compare this equation to each of the four other equations to see where it intersects each one. One of these intersections will be our Q, if a Q exists; it's possible that the ray will miss the bounding box entirely. We will need to do a few checks:
Firstly, eliminate all potential Q's that are on the wrong side of P.
Secondly, check each of the four intersections to make sure they are within the bounds of the lines that they represent, and eliminate the ones that are not.
Finally, if any potential Q's remain, the one closest to P will be our Q. If no potential Q's remain, this means that the ray from P misses the bounding box entirely.
For example...
The line drawn from D to B would have a slope equal to (B.y - D.y) / (B.x - D.x) and a y-intercept equal to B.y - B.x * slope. Then the entire equation is y = (B.y - D.y) / (B.x - D.x) * x + B.y - B.x * (B.y - D.y) / (B.x - D.x). Set this equation equal to y = P.y and solve for x:
x = (P.y - B.y + B.x*(B.y - D.y)/(B.x - D.x))*(B.x - D.x)/(B.y - D.y)
The result of this equation will give you the x-value of the intersection. The y-value is P.y. Do this for each of the other 3 lines as well: A-B, A-C, C-D. I will refer to these intersections as Q(D-B), Q(A-B), Q(A-C), and Q(C-D) respectively.
Next, eliminate candidate-Q's that are on the wrong side of P. In our example, this eliminates Q(A-B), since it is way off to the right side of the screen. Mathematically, Q(A-B).x > P.x.
Then eliminate all candidate-Q's that are not on the line they represent. We can do this check by finding the lowest and highest x-values and y-values given by the two points that represent the line. For example, to check that Q(A-C) is on the line A-C, check that C.x <= Q(A-C).x <= A.x and C.y <= Q(A-C).y <= A.y. Q(A-C) passes the test, as well as Q(D-B). However, Q(C-D) does not pass, as it is way off to the left side of the screen, far from the box. Therefore, Q(C-D) is eliminated from candidacy.
Finally, of the two points that remain, Q(A-C) and Q(D-B), we choose Q(D-B) to be our winner, because it is closest to P.
We now can say that the ray from P hits the bounding box at Q(D-B).
Of course, when you implement this in code, you will need to account for divisions by zero. If a line is perfectly vertical, there does not exist a point-slope equation of the line, so you will need to create a separate formula for this case. If a line is perfectly horizontal, it's respective candidate-Q should be automatically eliminated from candidacy, as the ray from P will never touch it.
Edit:
It would be more efficient to only do this process with lines whose two points are on vertically opposite sides of point P. If both points of a line are above P, or they are both below P, they would be eliminated from candidacy from the beginning.
Find the two sides that straddle p on Y. (Test of the form (Ya < Yp) != (Yb < Yp)).
Then compute the intersection points of the horizontal by p with these two sides, and keep the first to the left of p.
If the ray points to the left(right) then it must intersect an edge that connects to the point in the OOB with max(min) x-value. We can determine which edge by simply comparing the y-value of the ray with the y value of the max(min) point and its neighbors. We also need to consider OBBs that are actually axis-aligned, and thus have two points with equal max(min) x-value. Once we have the edge it's simple to confirm that the ray does in fact intersect the OBB and calculate its x-value.
Here's some Java code to illustrate (ideone):
static double nearestX(Point[] obb, int y, int dir)
{
// Find min(max) point
int n = 0;
for(int i=1; i<4; i++)
if((obb[n].x < obb[i].x) == (dir == -1)) n = i;
// Determine next or prev edge
int next = (n+1) % 4;
int prev = (n+3) % 4;
int nn;
if((obb[n].x == obb[next].x) || (obb[n].y < y) == (obb[n].y < obb[next].y))
nn = next;
else
nn = prev;
// Check that the ray intersects the OBB
if(Math.abs(y) > Math.abs(obb[nn].y)) return Double.NaN;
// Standard calculation of x from y for line segment
return obb[n].x + (y-obb[n].y)*(obb[nn].x-obb[n].x)/(obb[nn].y-obb[n].y);
}
Test:
public static void main(String[] args)
{
test("Diamond", new Point[]{p(0, -2), p(2, 0), p(0, 2), p(-2,0)});
test("Square", new Point[]{p(-2, -2), p(2, -2), p(2, 2), p(-2,2)});
}
static void test(String label, Point[] obb)
{
System.out.println(label + ": " + Arrays.toString(obb));
for(int dir : new int[] {-1, 1})
{
for(int y : new int[] {-3, -2, -1, 0, 1, 2, 3})
System.out.printf("(% d, % d) = %.0f\n", y , dir, nearestX(obb, y, dir));
System.out.println();
}
}
Output:
Diamond: [(0,-2), (2,0), (0,2), (-2,0)]
(-3, -1) = NaN
(-2, -1) = 0
(-1, -1) = 1
( 0, -1) = 2
( 1, -1) = 1
( 2, -1) = 0
( 3, -1) = NaN
(-3, 1) = NaN
(-2, 1) = 0
(-1, 1) = -1
( 0, 1) = -2
( 1, 1) = -1
( 2, 1) = 0
( 3, 1) = NaN
Square: [(-2,-2), (2,-2), (2,2), (-2,2)]
(-3, -1) = NaN
(-2, -1) = 2
(-1, -1) = 2
( 0, -1) = 2
( 1, -1) = 2
( 2, -1) = 2
( 3, -1) = NaN
(-3, 1) = NaN
(-2, 1) = -2
(-1, 1) = -2
( 0, 1) = -2
( 1, 1) = -2
( 2, 1) = -2
( 3, 1) = NaN
I am new to d3js and I'm just starting out.
I am trying the cluster layout example written by Mike in one of his blocks.
https://bl.ocks.org/mbostock/7882658
I got it to work on my machine with my code but I really don't like just blindly copying code without understanding it.
However I am having a tough time understanding the math behind the 'cluster()' and 'collide()' functions and as to how they function.
Could anyone please explain it? Thanks for your help !!
Let's look at each method and I'll comment it as best I can.
Cluster
First the caller:
function tick(e) {
node
.each(cluster(10 * e.alpha * e.alpha)) //for each node on each tick call function returned by cluster function
//pass in alpha cooling parameter to collide
...
I won't rehash an explanation here about how the tick event works. The documentation is clear.
The function:
// returns a closure wrapping the cooling
// alpha (so it can be used for every node on the tick)
function cluster(alpha) {
return function(d) { // d here is the datum on the node
var cluster = clusters[d.cluster]; // clusters is a hash-map, the key is an index of the 10 clusters, the value is an object where d.cluster is the center node in that cluster
if (cluster === d) return; // if we are on the center node, do nothing
var x = d.x - cluster.x, // distance on x of node to center node
y = d.y - cluster.y, // distance on y of node to center node
l = Math.sqrt(x * x + y * y), // distance of node to center node (Pythagorean theorem)
r = d.radius + cluster.radius; // radius of node, plus radius of center node (the center node is always the largest one in the cluster)
if (l != r) { // if the node is not adjacent to the center node
l = (l - r) / l * alpha; //find a length that is slightly closer, this provides the illusion of it moving towards the center on each tick
d.x -= x *= l; // move node closer to center node
d.y -= y *= l;
cluster.x += x; // move center node closer to node
cluster.y += y;
}
};
}
Collide
The collide function is a bit more complicated. Before we dive into it, you need to understand what a QuadTree is and why Bostock is using it. If you want to determine if two elements are colliding the naive algorithm would be to loop the elements both outer and inner to compare each one against every other one. This is, of course, computationally expensive especially on every tick. This is the problem QuadTrees are trying to solve:
A quadtree recursively partitions two-dimensional space into squares, dividing each square into four equally-sized squares. Each distinct point exists in a unique leaf node; coincident points are represented by a linked list. Quadtrees can accelerate various spatial operations, such as the Barnes–Hut approximation for computing many-body forces, collision detection, and searching for nearby points.
What does that mean? First, take a look at this excellent explanation. In my own simplified words it means this: take a 2-d space and divide it into four quadrants. If any quadrant contains 4 or less nodes stop. If the quadrant contains more than four nodes, divide it again into four quadrants. Repeat this until each quadrant/sub-quadrant contains 4 or less nodes. Now when we look for collisions, our inner loop no longer loops nodes, but instead quadrants. If the quadrant doesn't collide then move to the next one. This is a big optimization.
Now onto the code:
// returns a closure wrapping the cooling
// alpha (so it can be used for every node on the tick)
// and the quadtree
function collide(alpha) {
// create quadtree from our nodes
var quadtree = d3.geom.quadtree(nodes);
return function(d) { // d is the datum on the node
var r = d.radius + maxRadius + Math.max(padding, clusterPadding), // r is the radius of the node circle plus padding
nx1 = d.x - r, // nx1, nx2, ny1, ny2 are the bounds of collision detection on the node
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) { // visit each quadrant
if (quad.point && (quad.point !== d)) { // if the quadrant is a point (a node and not a sub-quadrant) and that point is not our current node
var x = d.x - quad.point.x, // distance on x of node to quad node
y = d.y - quad.point.y, // distance on y of node to quad node
l = Math.sqrt(x * x + y * y), // distance of node to quad node (Pythagorean theorem)
r = d.radius + quad.point.radius + (d.cluster === quad.point.cluster ? padding : clusterPadding); // radius of node in quadrant
if (l < r) { // if there is a collision
l = (l - r) / l * alpha; // re-position nodes
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
// This is important, it determines if the quadrant intersects
// with the node. If it does not, it returns false
// and we no longer visit and sub-quadrants or nodes
// in our quadrant, if true it descends into it
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
This is a language-agnostic question. Given a rectangle's dimensions with l,t,w,h (left, top, width, height) and a point x,y, how do I find the nearest point on the perimeter of the rectangle to that point?
I have tried to resolve it in Lua, but any other language would do. So far this is my best effort:
local function nearest(x, a, b)
if a <= x and x <= b then
return x
elseif math.abs(a - x) < math.abs(b - x) then
return a
else
return b
end
end
local function getNearestPointInPerimeter(l,t,w,h, x,y)
return nearest(x, l, l+w), nearest(y, t, t+h)
end
This works for a point outside of the perimeter or in the perimeter itself. But for points inside of the perimeter it fails (it just returns x,y)
My gut tells me that the solution should be simple, but I don't seem to find it.
This time I'm trying to catch the minimum distance of the point toward any side of the rectangle.
local abs, min, max = math.abs, math.min, math.max
local function clamp(x, lower, upper)
return max(lower, min(upper, x))
end
local function getNearestPointInPerimeter(l,t,w,h, x,y)
local r, b = l+w, t+h
x, y = clamp(x, l, r), clamp(y, t, b)
local dl, dr, dt, db = abs(x-l), abs(x-r), abs(y-t), abs(y-b)
local m = min(dl, dr, dt, db)
if m == dt then return x, t end
if m == db then return x, b end
if m == dl then return l, y end
return r, y
end
Let C1,C2,C3,C4 be the vertices of the rectangle.
From the given point A which you have, draw the 2 lines which are
perpendicular to the sides of the rectangle. Let B1, B2, B3, B4
be their intersecting points with the lines determined by the
sides of the rectangle (some of these Bk may coincide too
e.g. if A = Ck for some k). Your solution is one of the points Bk
or one of the points Ck, just brute-force check the 8 points
(again, some of these 8 points may coincide but that doesn't matter).
Another possible algorithm (similar to my 1st answer) can be found here - the one from Dinre.
Calculating the distance between polygon and point in R
Looks quite simple, actually it is a simplified (maybe better) version of my 1st answer here.
Find the two nearest rectangle vertices Ci and Cj to the given point A.
Find the point M where the perpendicular line from A to the line (Ci,Cj) crosses the line (Ci,Cj).
Your solution is either Ci or Cj or M.
Seems to me like this works for all cases (no matter where the point A lies in the plane).
Are you looking for something like this?
Inspired by Keeper's code:
local function getNearestPointInPerimeter(l,t,w,h, x,y)
-- x axis increases to the right
-- y axis increases down
local r = l + w
local b = t + h
local inside = true -- unless later proven otherwise
-- if the point (x,y) is outside the rectangle,
-- push it once to the nearest point on the perimeter, or
-- push it twice to the nearest corner.
if x < l then x = l; inside = false; end
if x > r then x = r; inside = false; end
if y < t then y = t; inside = false; end
if y > b then y = b; inside = false; end
-- if the point (x,y) is inside the rectangle,
-- push it once to the closest side.
if inside then
local dt = math.abs (y - t)
local db = math.abs (y - b)
local dl = math.abs (x - l)
local dr = math.abs (x - r)
if dt <= db and dt <= dl and dt <= dr then
y = t
elseif db <= dl and db <= dr then
y = b
elseif dl <= dr then
x = l
else
x = r
end
end
return x,y
end
Thanks for the question and answers! Here is my python-translated version of the chosen answer in case anyone needs it. The only custom part is the clamp in-line function definition using lambda.
I used this successfully in a GUI with Qt's QRect and QPoint to make sure something showed up in a QGraphcsView.
def getNearestPointInPerimeter(self, left, top, width, height, x, y):
right = left + width
bottom = top + height
clamp = lambda value, minv, maxv: max(min(value, maxv), minv)
x = clamp(x, left, right)
y = clamp(y, top, bottom)
dl = abs(x - left)
dr = abs(x - right)
dt = abs(y - top)
db = abs(y - bottom)
m = min(dl, dr, dt, db)
if m == dt:
result = (x, top)
elif m == db:
result = (x, bottom)
elif m == dl:
result = (left, y)
else:
result = (right, y)
return result
For those looking for the Keeper's answer in C#
public static Point GetNearestPointInPerimeter(Point point, Rectangle rectangle)
{
point.X = Math.Max(rectangle.Left, Math.Min(rectangle.Right, point.X));
point.Y = Math.Max(rectangle.Top, Math.Min(rectangle.Bottom, point.Y));
var dl = Math.Abs(point.X - rectangle.Left);
var dr = Math.Abs(point.X - rectangle.Right);
var dt = Math.Abs(point.Y - rectangle.Top);
var db = Math.Abs(point.Y - rectangle.Bottom);
var m = new[] { dl, dr, dt, db }.Min();
if (m == dt) return new Point(point.X, rectangle.Top);
if (m == db) return new Point(point.X, rectangle.Bottom);
if (m == dl) return new Point(rectangle.Left, point.Y);
return new Point(rectangle.Right, point.Y);
}
I am working with Latitude / Longitude coordinates in a google map.
I have two lines :
Line A : 48.31508162629726, -2.591741396838972 to 48.40216156645915, -2.2218462112093404
Line B : 48.383816077371215, -2.274292940053768 to 48.66103546935337, -1.7066197241571377
I then use the following formula to find the point where they cross.
var XAsum = A.LngStart - A.LngEnd;
var XBsum = B.LngStart - B.LngEnd;
var YAsum = A.LatStart - A.LatEnd;
var YBsum = B.LatStart - B.LatEnd;
var LineDenominator = XAsum * YBsum - YAsum * XBsum;
if(LineDenominator == 0.0)
return false;
var a = A.LngStart * A.LatEnd - A.LatStart * A.LngEnd;
var b = B.LngStart * B.LatEnd - B.LatStart * B.LngEnd;
var x = (a * XBsum - b * XAsum) / LineDenominator;
var y = (a * YBsum - b * YAsum) / LineDenominator;
This tells me that the lines do indeed cross and returns the x and y values.
However, when I plot the returned point, it is offset (not much) from the real intersection.
Is there a better and just as fast algorithm I could use which will return me the correct intersection point ?
It needs to be fast as I am iterating over a large number of lines (~1000).
EDIT : Note this is giving me an error offset of around 7.5 meters
I'm assuming the algorithm you're using is the one for finding line intersections on a Cartesian coordinate system (i.e. a plane). Unfortunately, the earth is not a plane (or even a sphere) so using that algorithm will introduce error. Google Maps uses an ellipsoid (specifically WGS84) to approximate the surface of the earth, so you'll need an algorithm for finding the intersections of arcs on an ellipsoid.
This page might contains some helpful information:
http://mathhelpforum.com/calculus/90196-point-intersection-two-lines.html
After searching and trying to use your code, I discover the main problem came from the fact you're not checking for intersection of segment. In many case the coord of the intersection are outside of the segment, meanig you're checking for intersection of line.
Here is a piece of PHP code. Assuming you have 2 lines in GPS Coord (x1y1-x2y2 for the first one and x3y3-x4y4 for the second one)
$x1 = deg2rad($x1);
$y1 = deg2rad($y1);
$x2 = deg2rad($x2);
$y2 = deg2rad($y2);
$x3 = deg2rad($x3);
$y3 = deg2rad($y3);
$x4 = deg2rad($x4);
$y4 = deg2rad($y4);
$denom = (($y4 - $y3) * ($x2 - $x1)) - (($x4 - $x3) * ($y2 - $y1));
// Id denom = 0 -> parallele line
if ($denom != 0)
{
$ua = (($x4 - $x3) * ($y1 - $y3) - ($y4 - $y3) * ($x1 - $x3))/$denom;
$ub = (($x2 - $x1) * ($y1 - $y3) - ($y2 - $y1) * ($x1 - $x3))/$denom;
if ($ua >= 0 && $ua <= 1 && $ub >= 0 && $ub <= 1)
{
$intersection_x = ($x1 + $ua*($x2 - $x1));
$intersection_y = ($y1 + $ua*($y2 - $y1));
echo "<br><b>Intersection: ".round(rad2deg($intersection_x),6).",".round(rad2deg($intersection_y),6)."</b>";
}
else
{
echo "<br>No intersection";
}
}