Distance check between two circles without sqrt - c++11

I'm trying to do distance checks between two circles without using square roots. According to many sources online (such as this one), the way to do this is to subtract the square of the sum of the circles' radii from the squared distance (using Pythagoras's theorem).
However, this doesn't seem to work. According to Wolfram Alpha and my own tests, the check doesn't work like the sqrt version.
Here's the two equations compared in Wolfram: http://www.wolframalpha.com/input/?i=sqrt%28d%29+-+%28r1%2Br2%29+%3C+y+%3D%3D+d+-+%28r1%2Br2%29%5E2+%3C+y
As well as the relevant code that fails to comply:
T DistanceTo(Point p) const {
return sqrt((p.x - x)*(p.x - x) + (p.y - y)*(p.y - y));
}
T DistanceToSq(Point p) const {
return (p.x - x)*(p.x - x) + (p.y - y)*(p.y - y);
}
float Unit::GetDistanceTo(Unit * tgt) const {
auto dist = _pos.DistanceTo(tgt->GetPos());
dist -= GetRadius() + tgt->GetRadius();
return dist;
}
float Unit::GetDistanceToSq(Unit * tgt) const {
auto dist = _pos.DistanceToSq(tgt->GetPos());
auto radii = (GetRadius() + tgt->GetRadius());
dist -= radii * radii;
return dist;
}
template<typename Func>
void ForEachRange(Unit * owner, float range, Func func = [](Unit * tgt)) {
auto range_sq = range * range;
for(Unit * p : m_players) {
if(owner == p || owner->GetDistanceToSq(p) >= range_sq) {
if(owner != p && owner->GetDistanceTo(p) < range)
assert(0);
continue;
}
assert(owner->GetDistanceTo(p) < range);
func(p);
}
}
Am I doing something wrong or is the formula simply incorrect?

You asked Wolfram Alpha if sqrt(d) - (r1+r2) < y is equivalent to d - (r1+r2)^2 < y and Alpha said “no”.
Let's take the first inequality from your query and eliminate the square root using algebra:
sqrt(d) - (r1 + r2) < y
sqrt(d) < y + r1 + r2
d < (y + r1 + r2)²
Do you see how this is different from your second inequality d - (r1+r2)^2 < y?
You can follow your gut or you can follow the rules of algebra, but one of them gives better answers. ;^)

Your for loop doesn't end when you find something within range. I think you want a break instead of continue. You should also have something for when the loop failed to find anything within range.

Related

Drawing ellipse with Bresenham's algorithm

Hello,
I'm trying to draw an ellipse, which is parallel to the orthogonal system, using Bresenham's algorithm. I want to draw the top-left (W,SW,S) quarter of the ellipse, and then deduce others.
To do this, i'm using an incremental algorithm with the second-order logic. I did it from another algorithm that draw the top-right quarter first, but what i'm doing isn't working.
The problem appears when the 2nd region is drawing, and I don't know where it comes from.
You can see what I have (black), and what I expect (green):
(center of the ellipse (xc, yc) and the upper right button (x2,y2) that is ~(xc+30,yc+20) in this example)
(a is abs(x2-xc), and b is abs(y2-yc))
The first parameter is the middle of the ellipse (xc, yc), the second is the upper right point established the x and y radius. You can see the ellipse goes too far (2 points on the left and on the right). You can see an other example
(center of the ellipse (xc, yc) and the upper right button (x2,y2) that is ~(xc+15,yc+18) in this example)
The algorithm is deduced from the incremental algorithm with the second-order logic.
Here is my code, (a is abs(x2-xc), and b is abs(y2-yc))
ellipse(int a, int b, int xc, int yc) {
int a2 = a*a, b2 = b*b;
int x = 0, y = b; //Starting point
int incSW = b2*2 + a2*2;
int deltaW = b2*(-2*x + 3); //deduced from incremental algorithm with the second-order logic
int deltaS = a2*(-2*y + 3);
int deltaSW = deltaW + deltaS;
int d1 = b2 - a2*b + a2/4; //dp starting value in the first region
int d2 = b2*(x - 0.5)*(x - 0.5) + a2*(y - 1)*(y - 1) - a2*b2; //dp starting value in the second region
//First region
while(a2*(y-0.5) >= b2*(-x-1)) {
DrawPixel(g,-x+xc, -y+yc); // 1st case
DrawPixel(g,-x+xc, y+yc); // 2nd case
DrawPixel(g,x+xc, y+yc); // 3rd case
DrawPixel(g,x+xc, -y+yc); // 4th case
if(d1>0) {
d1+=deltaSW;
deltaW+=b2*2;
deltaSW+=incSW;
y--;
}
else {
d1+=deltaW;
deltaW+=2*b2;
deltaSW+=2*b2;
}
x--;
}
deltaSW = b2*(2 - 2*x) + a2*(-2*y + 3);
//Second region
while(y>=0) {
DrawPixel(g,-x+xc, -y+yc); // 1st case
DrawPixel(g,-x+xc, y+yc); // 2nd case
DrawPixel(g,x+xc, y+yc); // 3rd case
DrawPixel(g,x+xc, -y+yc); // 4th case
if(d2>0) {
d2+=deltaS;
deltaS+=a2*2;
deltaSW+=a2*2;
}
else {
d2+=deltaSW;
deltaSW+=incSW;
deltaS+=a2*2;
x--;
}
y--;
}
}
I hope you can help me, thanks.
Using the error term e = a x^2 + b y^2 - r^2, it's pretty easy to show that a step from (x,y) to (x,y+1) changes the error by 2by + b, a step to (x+1,y+1) by 2ax + a + 2by + b, and a step to (x+1,y) by 2ax + a.
Starting from a point (-x0, 0), choose the least absolute error step from these three. The first two cases are the norm for the "first region" as you call it.
The first time a step right, (x,y) to (x+1,y), produces least error, you know you're in the second region. At this point the first case is no longer needed. The quarter ellipse can be finished using only the second two cases.
Note this check avoids the floating point operations you've used. The whole point of Bresenham-ish algorithms is to avoid floating point.
The last bit to notice is that you don't want to compute 2ax or 2by each iteration. The multiplications can be avoided by maintaining variables, say dx=2ax and dy=2by, and updating them. A step from x to x+1 increments dx by 2a, a constant. Similarly a step from y to y+1 increments dy by 2b.
Putting all this together, you get the (rough) code below.
Note that you can check the incremental error computation by verifying it against the original error term. If (x0,0) is the initial point, then you know x0^2 = r^2. So the actual error in every iteration is a * x^2 + b * y^2 - x0^2. This ought to equal e in the code below, and it does.
import static java.lang.Math.abs;
import java.util.Arrays;
import java.util.function.BiConsumer;
public class EllipseTracer {
static char [] [] raster = new char[51][101];
static void trace(int x, int y, int a, int b, BiConsumer<Integer, Integer> emitter) {
emitter.accept(x, y);
int e = 0;
int dx = 2 * a * x;
int dy = 2 * b * y;
// First region: stepping north and northeast.
while (x < 0) {
int dxa = dx + a;
int dyb = dy + b;
int eUp = e + dyb;
int eRt = e + dxa;
int eDg = e + dxa + dyb;
if (abs(eUp) < abs(eDg)) {
emitter.accept(x, ++y);
e = eUp;
dy += 2 * b;
} else {
if (abs(eRt) < abs(eDg)) {
// Step east is least error. Found second region.
emitter.accept(++x, y);
e = eRt;
dx += 2 * a;
break;
}
emitter.accept(++x, ++y);
e = eDg;
dy += 2 * b;
dx += 2 * a;
}
}
// Second region: step northeast and east.
while (x < 0) {
int dxa = dx + a;
int dyb = dy + b;
int eRt = e + dxa;
int eDg = e + dxa + dyb;
if (abs(eRt) < abs(eDg)) {
emitter.accept(++x, y);
e = eRt;
dx += 2 * a;
} else {
emitter.accept(++x, ++y);
e = eDg;
dy += 2 * b;
dx += 2 * a;
}
}
}
static void emit(int x, int y) {
raster[y][x + 100] = '*';
}
public static void main(String [] args) {
for (int i = 0; i < raster.length; ++i) {
Arrays.fill(raster[i], ' ');
}
trace(-100, 0, 1, 4, EllipseTracer::emit);
for (int i = 0; i < raster.length; ++i) {
System.out.println(raster[i]);
}
}
}
You can add more tricks to avoid the absolute values, but I'll let you look for those.

Finding the point on a simplex closest to the origin using GJK's distance subalgorithm

I'm trying to implement the Gilbert–Johnson–Keerthi distance algorithm (GJK), but I'm having problems with the "distance subalgorithm", also known as "Johnson's Algorithm", which is used to determine the point on a simplex that is closest to the origin. I'm getting incorrect results but I can't find any bugs in my code, so the problem must be in my interpretation of the algorithm.
In Johnson’s Algorithm (as described in Gino van den Bergen's book Collision Detection in Interactive 3D Environments), the point on the affine hull of a simplex X = {yi : i ∈ Ix} closest to the origin is given by:
Where the Δi^X values are determined recursively in order of increasing cardinality of X:
... and Δ^X is given by:
For two dimensions, I find the closest point to the origin using:
Point ClosestPointToOrigin(Simplex simplex)
{
float dx = 0;
for (int i = 0; i < simplex.size(); ++i)
dx += dj(simplex, i);
Point closest_point(0,0);
for (int i = 0; i < simplex.size(); ++i)
closest_point += dj(simplex, i) / dx * simplex[i];
return closest_point;
}
In which the Δi values are determined by:
float dj(Simplex simplex, int j)
{
if (j == 0)
{
return 1;
}
else
{
float d = 0;
for (int i = 0; i < j; ++i)
d += dj(simplex, i) * (simplex[0] - simplex[j]).dotProduct(simplex[i]);
return d;
}
}
For a simplex X = {y1, y2} where y1 = (1,1), y2 = (1,-1), the above code returns (1.0, -0.333333), when the closest point is, in fact, (1, 0).
I must be doing something wrong, but I can't figure out what that is.
Your error is the dj function, maybe you have misunderstood the dxi equation or you did not write what you want.
I will try to explain myself, do not hesitate to comment if you do not understand something (I am writing pseudo python code but it should be easily understandable).
Assume I have the following Simplex:
S = Simplex({
1: Point (1, 1) # y1
2: Point (1,-1) # y2
})
I can immediately compute 2 deltas values:
Then, I can compute 2 others deltas values:
Hopefully by now you'll start to see your mistake: The Δ values are index based, so for each Simplex X of dimension n, you have n Δ values. One of your mistake was to assume that you can compute ΔX0 and ΔXi regardless of the content of X, which is false.
Now the last Δ:
Notice that:
Once you are here:
Here is a code written in Python, if you do not understand it, I'll try to write one in a language you understand:
import numpy
class Point(numpy.ndarray):
def __new__(cls, x, y):
return numpy.asarray([x, y]).astype(float).view(cls)
def __str__(self):
return repr(self)
def __repr__(self):
return "Point ({}, {})".format(self.x, self.y)
x = property(fget=lambda s: s[0])
y = property(fget=lambda s: s[1])
class Simplex(dict):
def __init__(self, points):
super(Simplex, self).__init__(enumerate(points))
def __str__(self):
return repr(self)
def __repr__(self):
return "Simplex <" + dict.__repr__(self) + ">"
def closest_point(s):
dx = sum(dj(s, i) for i in range(len(s)))
return sum(dj(s, i) / dx * v for i, v in s.items())
def dj(s, j):
if len(s) == 0 or (len(s) == 1 and j not in s):
print(s, j)
raise ValueError()
if len(s) == 1:
return 1
ts = s.copy()
yj = s[j]
del ts[j]
return sum(
dj(ts, i) * (ts[list(ts.keys())[0]] - yj).dot(v)
for i, v in ts.items()
)
S = Simplex([Point(1, 1), Point(1, -1)])
print(closest_point(S))

Optimizing / simplifying a path

Say I have a path with 150 nodes / verticies. How could I simplify if so that for example a straight line with 3 verticies, would remove the middle one since it does nothing to add to the path. Also how could I avoid destroying sharp corners? And how could I remove tiny variations and have smooth curves remaining.
Thanks
For every 3 vertices, pick the middle one and calculate its distance to the line segment between the other two. If the distance is less than the tolerance you're willing to accept, remove it.
If the middle vertex is very close to one of the endpoints, you should tighten the tolerance to avoid removing rounded corners for instance.
The simpler approach. Take 3 verticies a, b and c and calculate the angle/inclination between verticies.
std::vector<VERTEX> path;
//fill path
std::vector<int> removeList;
//Assure that the value is in the same unit as the return of angleOf function.
double maxTinyVariation = SOMETHING;
for (int i = 0; i < quantity-2; ++i) {
double a = path[i], b = path[i + 1] , c = path[i + 2]
double abAngle = angleOf(a, b);
double bcAngle = angleOf(b, c);
if (abs(ab - bc) <= maxTinyVariation) {
//a, b and c are colinear
//remove b later
removeList.push_back(i+1);
}
}
//Remove vertecies from path using removeList.
How could I simplify if so that for example a straight line with 3 verticies, would remove the middle one since it does nothing to add to the path.
For each set of three consecutive vertices, test whether they are all in a straight line. If they are, remove the middle vertex.
Also how could I avoid destroying sharp corners?
If you're only removing vertices that fall on a straight line between two others, then you won't have a problem with this.
Use Douglas-Peucker method to simplify a Path.
epsilon parameter defines level of "simplicity":
private List<Point> douglasPeucker (List<Point> points, float epsilon){
int count = points.size();
if(count < 3) {
return points;
}
//Find the point with the maximum distance
float dmax = 0;
int index = 0;
for(int i = 1; i < count - 1; i++) {
Point point = points.get(i);
Point lineA = points.get(0);
Point lineB = points.get(count-1);
float d = perpendicularDistance(point, lineA, lineB);
if(d > dmax) {
index = i;
dmax = d;
}
}
//If max distance is greater than epsilon, recursively simplify
List<Point> resultList;
if(dmax > epsilon) {
List<Point> recResults1 = douglasPeucker(points.subList(0,index+1), epsilon);
List<Point> recResults2 = douglasPeucker(points.subList(index, count), epsilon);
List<Point> tmpList = new ArrayList<Point>();
tmpList.addAll(recResults1);
tmpList.remove(tmpList.size()-1);
tmpList.addAll(recResults2);
resultList = tmpList;
} else {
resultList = new ArrayList<Point>();
resultList.add(points.get(0));
resultList.add(points.get(count-1));
}
return resultList;
}
private float perpendicularDistance(Point point, Point lineA, Point lineB){
Point v1 = new Point(lineB.x - lineA.x, lineB.y - lineA.y);
Point v2 = new Point(point.x - lineA.x, point.y - lineA.y);
float lenV1 = (float)Math.sqrt(v1.x * v1.x + v1.y * v1.y);
float lenV2 = (float)Math.sqrt(v2.x * v2.x + v2.y * v2.y);
float angle = (float)Math.acos((v1.x * v2.x + v1.y * v2.y) / (lenV1 * lenV2));
return (float)(Math.sin(angle) * lenV2);
}
Let A, B, C be some points.
The easiest way to check they lie on the same line is to count crossproduct of vectors
B-A, C-A.
If it equals zero, they lie on the same line:
// X_ab, Y_ab - coordinates of vector B-A.
float X_ab = B.x - A.x
float Y_ab = B.y - A.y
// X_ac, Y_ac - coordinates of vector C-A.
float X_ac = C.x - A.x
float Y_ac = C.y - A.y
float crossproduct = Y_ab * X_ac - X_ab * Y_ac
if (crossproduct < EPS) // if crossprudct == 0
{
// on the same line.
} else {
// not on the same line.
}
After you know that A, B, C lie on the same line it is easy to know whether B lies between A and C throw innerproduct of vectors B-A and C-A. If B lies between A and C, then (B-A) has the same direction as (C-A), and innerproduct > 0, otherwise < 0:
float innerproduct = X_ab * X_ac + Y_ab * Y_ac;
if (innerproduct > 0) {
// B is between A and C.
} else {
// B is not between A and C.
}

Shortest distance between points algorithm

Given a set of points on a plane, find the shortest line segment formed by any two of these points.
How can I do that? The trivial way is obviously to calculate each distance, but I need another algorithm to compare.
http://en.wikipedia.org/wiki/Closest_pair_of_points
The problem can be solved in O(n log n) time using the recursive divide and conquer approach, e.g., as follows:
Sort points along the x-coordinate
Split the set of points into two equal-sized subsets by a vertical line x = xmid
Solve the problem recursively in the left and right subsets. This will give the left-side and right-side minimal distances dLmin and dRmin respectively.
Find the minimal distance dLRmin among the pair of points in which one point lies on the left of the dividing vertical and the second point lies to the right.
The final answer is the minimum among dLmin, dRmin, and dLRmin.
I can't immediately think of a quicker alternative than the brute force technique (although there must be plenty) but whatever algorithm you choose don't calculate the distance between each point. If you need to compare distances just compare the squares of the distances to avoid the expensive and entirely redundant square root.
One possibility would be to sort the points by their X coordinates (or the Y -- doesn't really matter which, just be consistent). You can then use that to eliminate comparisons to many of the other points. When you're looking at the distance between point[i] and point[j], if the X distance alone is greater than your current shortest distance, then point[j+1]...point[N] can be eliminated as well (assuming i<j -- if j<i, then it's point[0]...point[i] that are eliminated).
If your points start out as polar coordinates, you can use a variation of the same thing -- sort by distance from the origin, and if the difference in distance from the origin is greater than your current shortest distance, you can eliminate that point, and all the others that are farther from (or closer to) the origin than the one you're currently considering.
You can extract the closest pair in linear time from the Delaunay triangulation and conversly from Voronoi diagram.
There is a standard algorithm for this problem, here you can find it:
http://www.cs.mcgill.ca/~cs251/ClosestPair/ClosestPairPS.html
And here is my implementation of this algo, sorry it's without comments:
static long distSq(Point a, Point b) {
return ((long) (a.x - b.x) * (long) (a.x - b.x) + (long) (a.y - b.y) * (long) (a.y - b.y));
}
static long ccw(Point p1, Point p2, Point p3) {
return (long) (p2.x - p1.x) * (long) (p3.y - p1.y) - (long) (p2.y - p1.y) * (long) (p3.x - p1.x);
}
static List<Point> convexHull(List<Point> P) {
if (P.size() < 3) {
//WTF
return null;
}
int k = 0;
for (int i = 0; i < P.size(); i++) {
if (P.get(i).y < P.get(k).y || (P.get(i).y == P.get(k).y && P.get(i).x < P.get(k).x)) {
k = i;
}
}
Collections.swap(P, k, P.size() - 1);
final Point o = P.get(P.size() - 1);
P.remove(P.size() - 1);
Collections.sort(P, new Comparator() {
public int compare(Object o1, Object o2) {
Point a = (Point) o1;
Point b = (Point) o2;
long t1 = (long) (a.y - o.y) * (long) (b.x - o.x) - (long) (a.x - o.x) * (long) (b.y - o.y);
if (t1 == 0) {
long tt = distSq(o, a);
tt -= distSq(o, b);
if (tt > 0) {
return 1;
} else if (tt < 0) {
return -1;
}
return 0;
}
if (t1 < 0) {
return -1;
}
return 1;
}
});
List<Point> hull = new ArrayList<Point>();
hull.add(o);
hull.add(P.get(0));
for (int i = 1; i < P.size(); i++) {
while (hull.size() >= 2 &&
ccw(hull.get(hull.size() - 2), hull.get(hull.size() - 1), P.get(i)) <= 0) {
hull.remove(hull.size() - 1);
}
hull.add(P.get(i));
}
return hull;
}
static long nearestPoints(List<Point> P, int l, int r) {
if (r - l == P.size()) {
Collections.sort(P, new Comparator() {
public int compare(Object o1, Object o2) {
int t = ((Point) o1).x - ((Point) o2).x;
if (t == 0) {
return ((Point) o1).y - ((Point) o2).y;
}
return t;
}
});
}
if (r - l <= 100) {
long ret = distSq(P.get(l), P.get(l + 1));
for (int i = l; i < r; i++) {
for (int j = i + 1; j < r; j++) {
ret = Math.min(ret, distSq(P.get(i), P.get(j)));
}
}
return ret;
}
int c = (l + r) / 2;
long lD = nearestPoints(P, l, c);
long lR = nearestPoints(P, c + 1, r);
long ret = Math.min(lD, lR);
Set<Point> set = new TreeSet<Point>(new Comparator<Point>() {
public int compare(Point o1, Point o2) {
int t = o1.y - o2.y;
if (t == 0) {
return o1.x - o2.x;
}
return t;
}
});
for (int i = l; i < r; i++) {
set.add(P.get(i));
}
int x = P.get(c).x;
double theta = Math.sqrt(ret);
Point[] Q = set.toArray(new Point[0]);
Point[] T = new Point[Q.length];
int pos = 0;
for (int i = 0; i < Q.length; i++) {
if (Q[i].x - x + 1 > theta) {
continue;
}
T[pos++] = Q[i];
}
for (int i = 0; i < pos; i++) {
for (int j = 1; j < 7 && i + j < pos; j++) {
ret = Math.min(ret, distSq(T[i], T[j + i]));
}
}
return ret;
}
From your question it is not clear if you are looking for the distance of the segment, or the segment itself. Assuming you are looking for the distance (the segment in then a simple modification, once you know which are the two points whose distance is minimal), given 5 points, numbered from 1 to 5, you need to
compare 1 with 2,3,4,5, then
compare 2, with 3,4,5, then
compare 3 with 4,5, then
compare 4 with 5.
If I am not wrong, given the commutativity of the distance you do not need to perform other comparisons.
In python, may sound like something
import numpy as np
def find_min_distance_of_a_cloud(cloud):
"""
Given a cloud of points in the n-dim space, provides the minimal distance.
:param cloud: list of nX1-d vectors, as ndarray.
:return:
"""
dist_min = None
for i, p_i in enumerate(cloud[:-1]):
new_dist_min = np.min([np.linalg.norm(p_i - p_j) for p_j in cloud[(i + 1):]])
if dist_min is None or dist_min > new_dist_min:
dist_min = new_dist_min
return dist_min
That can be tested with something like the following code:
from nose.tools import assert_equal
def test_find_min_distance_of_a_cloud_1pt():
cloud = [np.array((1, 1, 1)), np.array((0, 0, 0))]
min_out = find_min_distance_of_a_cloud(cloud)
assert_equal(min_out, np.sqrt(3))
def test_find_min_distance_of_a_cloud_5pt():
cloud = [np.array((0, 0, 0)),
np.array((1, 1, 0)),
np.array((2, 1, 4)),
np.array((3, 4, 4)),
np.array((5, 3, 4))]
min_out = find_min_distance_of_a_cloud(cloud)
assert_equal(min_out, np.sqrt(2))
If more than two points can have the same minimal distance, and you are looking for the segments, you need again to modify the proposed code, and the output will be the list of points whose distance is minimal (or couple of points). Hope it helps!
Here is a code example demonstrating how to implement the divide and conquer algorithm. For the algorithm to work, the points x-values must be unique. The non-obvious part of the algorithm is that you must sort both along the x and the y-axis. Otherwise you can't find minimum distances over the split seam in linear time.
from collections import namedtuple
from itertools import combinations
from math import sqrt
IxPoint = namedtuple('IxPoint', ['x', 'y', 'i'])
ClosestPair = namedtuple('ClosestPair', ['distance', 'i', 'j'])
def check_distance(cp, p1, p2):
xd = p1.x - p2.x
yd = p1.y - p2.y
dist = sqrt(xd * xd + yd * yd)
if dist < cp.distance:
return ClosestPair(dist, p1.i, p2.i)
return cp
def closest_helper(cp, xs, ys):
n = len(xs)
if n <= 3:
for p1, p2 in combinations(xs, 2):
cp = check_distance(cp, p1, p2)
return cp
# Divide
mid = n // 2
mid_x = xs[mid].x
xs_left = xs[:mid]
xs_right = xs[mid:]
ys_left = [p for p in ys if p.x < mid_x]
ys_right = [p for p in ys if p.x >= mid_x]
# Conquer
cp_left = closest_helper(cp, xs_left, ys_left)
cp_right = closest_helper(cp, xs_right, ys_right)
if cp_left.distance < cp_right.distance:
cp = cp_left
else:
cp = cp_right
ys_strip = [p for p in ys if abs(p.x - mid_x) < cp.distance]
n_strip = len(ys_strip)
for i in range(n_strip):
for j in range(i + 1, n_strip):
p1, p2 = ys_strip[j], ys_strip[i]
if not p1.y - p2.y < cp.distance:
break
cp = check_distance(cp, p1, p2)
return cp
def closest_pair(points):
points = [IxPoint(p[0], p[1], i)
for (i, p) in enumerate(points)]
xs = sorted(points, key = lambda p: p.x)
xs = [IxPoint(p.x + i * 1e-8, p.y, p.i)
for (i, p) in enumerate(xs)]
ys = sorted(xs, key = lambda p: p.y)
cp = ClosestPair(float('inf'), -1, -1)
return closest_helper(cp, xs, ys)

Equidistant points in a line segment

Let assume you have two points (a , b) in a two dimensional plane. Given the two points, what is the best way to find the maximum points on the line segment that are equidistant from each point closest to it with a minimal distant apart.
I use C#, but examples in any language would be helpful.
List<'points> FindAllPointsInLine(Point start, Point end, int minDistantApart)
{
// find all points
}
Interpreting the question as:
Between point start
And point end
What is the maximum number of points inbetween spaced evenly that are at least minDistanceApart
Then, that is fairly simply: the length between start and end divided by minDistanceApart, rounded down minus 1. (without the minus 1 you end up with the number of distances between the end points rather than the number of extra points inbetween)
Implementation:
List<Point> FindAllPoints(Point start, Point end, int minDistance)
{
double dx = end.x - start.x;
double dy = end.y - start.y;
int numPoints =
Math.Floor(Math.Sqrt(dx * dx + dy * dy) / (double) minDistance) - 1;
List<Point> result = new List<Point>;
double stepx = dx / numPoints;
double stepy = dy / numPoints;
double px = start.x + stepx;
double py = start.y + stepy;
for (int ix = 0; ix < numPoints; ix++)
{
result.Add(new Point(px, py));
px += stepx;
py += stepy;
}
return result;
}
If you want all the points, including the start and end point, then you'll have to adjust the for loop, and start 'px' and 'py' at 'start.x' and 'start.y' instead. Note that if accuracy of the end-points is vital you may want to perform a calculation of 'px' and 'py' directly based on the ratio 'ix / numPoints' instead.
I'm not sure if I understand your question, but are you trying to divide a line segment like this?
Before:
A +--------------------+ B
After:
A +--|--|--|--|--|--|--+ B
Where "two dashes" is your minimum distance? If so, then there'll be infinitely many sets of points that satisfy that, unless your minimum distance can exactly divide the length of the segment. However, one such set can be obtained as follows:
Find the vectorial parametric equation of the line
Find the total number of points (floor(length / minDistance) + 1)
Loop i from 0 to n, finding each point along the line (if your parametric equation takes a t from 0 to 1, t = ((float)i)/n)
[EDIT]
After seeing jerryjvl's reply, I think that the code you want is something like this: (doing this in Java-ish)
List<Point> FindAllPointsInLine(Point start, Point end, float distance)
{
float length = Math.hypot(start.x - end.x, start.y - end.y);
int n = (int)Math.floor(length / distance);
List<Point> result = new ArrayList<Point>(n);
for (int i=0; i<=n; i++) { // Note that I use <=, not <
float t = ((float)i)/n;
result.add(interpolate(start, end, t));
}
return result;
}
Point interpolate(Point a, Point b, float t)
{
float u = 1-t;
float x = a.x*u + b.x*t;
float y = a.y*u + b.y*t;
return new Point(x,y);
}
[Warning: code has not been tested]
Find the number of points that will fit on the line. Calculate the steps for X and Y coordinates and generate the points. Like so:
lineLength = sqrt(pow(end.X - start.X,2) + pow(end.Y - start.Y, 2))
numberOfPoints = floor(lineLength/minDistantApart)
stepX = (end.X - start.X)/numberOfPoints
stepY = (end.Y - start.Y)/numberOfPoints
for (i = 1; i < numberOfPoints; i++) {
yield Point(start.X + stepX*i, start.Y + stepY*i)
}

Resources