4.2 KiB
Distance
TODO
Approximations
Computing Euclidean distance requires multiplication and most importantly square root which is usually a pretty slow operation, therefore many times we look for simpler approximations.
Two very basic and rough approximations of Euclidean distance, both in 2D and 3D, are taxicab (also Manhattan) and Chebyshev distances. Taxicab distance simply adds the absolute coordinate differences along each principal axis (dx, dy and dz) while Chebyshev takes the maximum of them. In C (for generalization to 3D just add one coordinate of course):
int distTaxi(int x0, int y0, int x1, int y1)
{
x0 = x1 > x0 ? x1 - x0 : x0 - x1; // dx
y0 = y1 > y0 ? y1 - y0 : y0 - y1; // dy
return x0 + y0;
}
int distCheb(int x0, int y0, int x1, int y1)
{
x0 = x1 > x0 ? x1 - x0 : x0 - x1; // dx
y0 = y1 > y0 ? y1 - y0 : y0 - y1; // dy
return x0 > y0 ? x0 : y0;
}
Both of these distances approximate a circle in 2D with a square or a sphere in 3D with a cube, the difference is that taxicab is an upper estimate of the distance while Chebyshev is the lower estimate. For speed of execution (optimization) it may also be important that taxicab distance only uses the operation of addition while Chebyshev may result in branching (if) in the max function which is usually not good for performance.
A bit more accuracy can be achieved by averaging the taxicab and Chebyshev distances which in 2D approximates a circle with an 8 segment polygon and in 3D approximates a sphere with 24 sided polyhedron. The integer-only C code is following:
int dist8(int x0, int y0, int x1, int y1)
{
x0 = x1 > x0 ? x1 - x0 : x0 - x1; // dx
y0 = y1 > y0 ? y1 - y0 : y0 - y1; // dy
return (x0 + y0 + (x0 > y0 ? x0 : y0)) / 2;
}
{ The following is an approximation I came up with when working on tinyphysicsengine. While I measured the average and maximum error of the taxi/Chebyshev average in 3D at about 16% and 22% respectively, the following gave me 3% and 12% values. ~drummyfish }
Yet more accurate approximation of 3D Euclidean distance can be made with a 48 sided polyhedron. The principle is following: take absolute values of all three coordinate differences and order them by magnitude so that dx >= dy >= dz >= 0. This gets us into one of 48 possible slices of space (the other slices have the same shape, they just differ by ordering or signs of the coordinates but the distance in them is of course equal). In this slice we'll approximate the distance linearly, i.e. with a plane. We do this by simply computing the distance of our point from a plane that goes through origin and whose normal is approximately {0.8728,0.4364,0.2182} (it points in the direction that goes through the middle of space slice). The expression for the distance from this plane simplifies to simply 0.8728 * dx + 0.4364 * dy + 0.2182 * dz. The following is an integer-only implementation in C (note that the constants above have been converted to allow division by 1024 for possible optimization of division to a bit shift):
int32_t dist48(
int32_t x0, int32_t y0, int32_t z0,
int32_t x1, int32_t y1, int32_t z1)
{
x0 = x1 > x0 ? x1 - x0 : x0 - x1; // dx
y0 = y1 > y0 ? y1 - y0 : y0 - y1; // dy
z0 = z1 > z0 ? z1 - z0 : z0 - z1; // dz
if (x0 < y0) // order the coordinates
{
if (x0 < z0)
{
if (y0 < z0)
{ // x0 < y0 < z0
int32_t t = x0; x0 = z0; z0 = t;
}
else
{ // x0 < z0 < y0
int32_t t = x0; x0 = y0; y0 = t;
t = z0; z0 = y0; y0 = t;
}
}
else
{ // z0 < x0 < y0
int32_t t = x0; x0 = y0; y0 = t;
}
}
else
{
if (y0 < z0)
{
if (x0 < z0)
{ // y0 < x0 < z0
int32_t t = y0; y0 = z0; z0 = t;
t = x0; x0 = y0; y0 = t;
}
else
{ // y0 < z0 < x0
int32_t t = y0; y0 = z0; z0 = t;
}
}
}
return (893 * x0 + 446 * y0 + 223 * z0) / 1024;
}
TODO: this https://www.flipcode.com/archives/Fast_Approximate_Distance_Functions.shtml