It is roughly an equation with two unknowns, here is how I did it personally:
Code: Select all
local ax, ay = 120, 50
local bx, by = 210, 110
local cx, cy = 170, 150
local distAD = 90
local distBD = 110
local distCD = 70
-- Determination of the position of D --
local a = 2 * (bx - ax)
local b = 2 * (by - ay)
local c = distAD^2 - distBD^2 - ax^2 + bx^2 - ay^2 + by^2
local d = 2 * (cx - bx)
local e = 2 * (cy - by)
local f = distBD^2 - distCD^2 - bx^2 + cx^2 - by^2 + cy^2
local dx = (c*e - b*f) / (a*e - b*d)
local dy = (c*d - a*f) / (b*d - a*e)
-- Verification with given distances --
print("distAD: ", math.sqrt((ax-dx)^2+(ay-dy)^2), distAD)
print("distBD: ", math.sqrt((bx-dx)^2+(by-dy)^2), distBD)
print("distCD: ", math.sqrt((cx-dx)^2+(cy-dy)^2), distCD)
-- Shows points --
function love.draw()
love.graphics.setColor(1,0,0)
-- POINT A
love.graphics.circle("fill", ax,ay,4)
-- POINT B
love.graphics.circle("fill", bx,by,4)
-- POINT C
love.graphics.circle("fill", cx,cy,4)
-- POINT D --
love.graphics.setColor(0,1,0)
love.graphics.circle("fill", dx,dy,4)
end
Return:
Code: Select all
distAD: 86.857482246622 90
distBD: 107.44404228352 110
distCD: 65.910714016935 70
I don't know if it will be the most precise method (because of all evidence several answers would be possible in this case) but it at least has the advantage of giving a not too imprecise answer
Here is an explanation of the calculations performed:
Code: Select all
-- Calculation of coefficients for trilateration --
local a = 2 * (bx - ax) -- Coefficient a
local b = 2 * (by - ay) -- Coefficient b
local c = distAD^2 - distBD^2 - ax^2 + bx^2 - ay^2 + by^2 -- Coefficient c
local d = 2 * (cx - bx) -- Coefficient d
local e = 2 * (cy - by) -- Coefficient e
local f = distBD^2 - distCD^2 - bx^2 + cx^2 - by^2 + cy^2 -- Coefficient f
-- Solving linear equations to determine the position of D --
local dx = (c*e - b*f) / (a*e - b*d) -- Approximate x coordinate of D
local dy = (c*d - a*f) / (b*d - a*e) -- Approximate y coordinate of D
Surely by using another iterative method the result could be more precise but obviously much less efficient.
Also obviously by drawing circles with the given distances in radius, the point sought should be the intersection of the circles if there are any, here is another possible method, more precise, very surely not optimized and which may fail but which represents well what we would do by hand (check only two dists):
Code: Select all
local ax, ay = 120, 50
local bx, by = 210, 110
local cx, cy = 170, 150
local distAD = 90
local distBD = 110
local distCD = 70
-- Check intersection (function & perform) --
local function getIntersection(x1, y1, r1, x2, y2, r2)
local d = math.sqrt((x2-x1)^2 + (y2-y1)^2)
if d > r1 + r2 or d < math.abs(r1-r2) then
return nil
end
local a = (r1^2 - r2^2 + d^2) / (2*d)
local h = math.sqrt(r1^2 - a^2)
local x3 = x1 + a*(x2-x1)/d
local y3 = y1 + a*(y2-y1)/d
local x4_1 = x3 + h*(y2-y1)/d
local x4_2 = x3 - h*(y2-y1)/d
local y4_1 = y3 - h*(x2-x1)/d
local y4_2 = y3 + h*(x2-x1)/d
return x4_1, y4_1, x4_2, y4_2
end
local dx1, dy1, dx2, dy2 = getIntersection(ax, ay, distAD, bx, by, distBD)
if dx1 and dy1 then
dx, dy = dx1, dy1
elseif dx2 and dy2 then
dx, dy = dx2, dy2
else
dx, dy = nil, nil
end
-- Check distances with original --
if dx and dy then
print("distAD: ", math.sqrt((ax-dx)^2+(ay-dy)^2), distAD)
print("distBD: ", math.sqrt((bx-dx)^2+(by-dy)^2), distBD)
print("distCD: ", math.sqrt((cx-dx)^2+(cy-dy)^2), distCD)
else
print("The circles do not intersect.")
end
-- Show the points --
function love.draw()
love.graphics.setColor(1, 0, 0)
love.graphics.circle("fill", ax, ay, 4)
love.graphics.circle("fill", bx, by, 4)
love.graphics.circle("fill", cx, cy, 4)
if dx and dy then
love.graphics.setColor(0, 1, 0)
love.graphics.circle("fill", dx, dy, 4)
end
end
Return:
Code: Select all
distAD: 90 90
distBD: 110 110
distCD: 151.19637965115 70
Because I like the problem I made a more complete version of my second example which verifies all possible intersections and returns the best
(code to be optimized obviously):
Code: Select all
local ax, ay = 120, 50
local bx, by = 210, 110
local cx, cy = 170, 150
local distAD = 90
local distBD = 110
local distCD = 70
-- Check intersection (function & perform) --
local function getIntersection(x1, y1, r1, x2, y2, r2)
local d = math.sqrt((x2-x1)^2 + (y2-y1)^2)
if d > r1 + r2 or d < math.abs(r1-r2) then
return nil
end
local a = (r1^2 - r2^2 + d^2) / (2*d)
local h = math.sqrt(r1^2 - a^2)
local x3 = x1 + a*(x2-x1)/d
local y3 = y1 + a*(y2-y1)/d
local x4_1 = x3 + h*(y2-y1)/d
local x4_2 = x3 - h*(y2-y1)/d
local y4_1 = y3 - h*(x2-x1)/d
local y4_2 = y3 + h*(x2-x1)/d
return x4_1, y4_1, x4_2, y4_2
end
-- Check intersection between all circles --
local all_circles = {
{ax, ay, distAD},
{bx, by, distBD},
{cx, cy, distCD}
}
local intersections = {}
for i = 1, #all_circles do
for j = i + 1, #all_circles do
local x1, y1, r1 = unpack(all_circles[i])
local x2, y2, r2 = unpack(all_circles[j])
local dx1, dy1, dx2, dy2 = getIntersection(x1, y1, r1, x2, y2, r2)
if dx1 and dy1 then
table.insert(intersections, {dx1, dy1})
end
if dx2 and dy2 then
table.insert(intersections, {dx2, dy2})
end
end
end
-- Find the intersection closest to all the points --
local min_distance = math.huge
local best_intersection = nil
for _, point in ipairs(intersections) do
local distance = 0
for _, circle in ipairs(all_circles) do
local x, y, r = unpack(circle)
distance = distance + math.abs(math.sqrt((x-point[1])^2 + (y-point[2])^2) - r)
end
if distance < min_distance then
min_distance = distance
best_intersection = point
end
end
-- Check distances with original --
if best_intersection then
local dx, dy = best_intersection[1], best_intersection[2]
print("distAD: ", math.sqrt((ax-dx)^2+(ay-dy)^2), distAD)
print("distBD: ", math.sqrt((bx-dx)^2+(by-dy)^2), distBD)
print("distCD: ", math.sqrt((cx-dx)^2+(cy-dy)^2), distCD)
else
print("The circles do not intersect.")
end
-- Show the points --
function love.draw()
love.graphics.setColor(1, 0, 0)
for _, circle in ipairs(all_circles) do
love.graphics.circle("fill", circle[1], circle[2], 4)
end
if best_intersection then
love.graphics.setColor(0, 1, 0)
love.graphics.circle("fill", best_intersection[1], best_intersection[2], 4)
end
end
Return:
Code: Select all
distAD: 90 90
distBD: 112.49549421694 110
distCD: 70 70