I'm smoothing a path via cubic interpolation, based on this math:
mu2 = mu*mu;
a0 = -0.5*x0 + 1.5*x1 - 1.5*x2 + 0.5*x3;
a1 = x0 - 2.5*x1 + 2*x2 - 0.5*x3;
a2 = -0.5*x0 + 0.5*x2;
a3 = x1;
interpolated_x = (a0*mu*mu2+a1*mu2+a2*mu+a3)
When I try to trace a segment on the path by iterating mu from 0.0 to 1.0, the distances are not equal. In the screenshot, the red points are the points of the path, the green and blue points are control points to calculate the curve.
Is there a way I can calculate a percentage of distance covered of a segment that gives me equal distances?
(I want to calculate a movement on this path, that has the same speed and depends on the percentage of distance covered. In this way, the movement would be faster in the middle of each segments.)
This function interpolates between x_1 and x_2 for values mu=0..1. But if you calculate the speed xp = diff(x, mu) at the end points you will find
xp_1 = (x_2-x_0)/2 xp_2 = (x_3-x_1)/2
So if the speed isn't equal and the ends points, it is going to vary by location. Even if it was equal at the ends points with v = (x_2-x_0)/2 = (x_3-x_1)/2 or
x_0 = x_2 -2 v x_3 = 2 v + x_1
or the speed function
xp = v - 6*mu*(v+x_1-x_2) + 6*mu2*(v+x_1-x_2)
To make the speed constant, v+x_1-x_2 = 0, or v=x_2-x_1 which yields the linear interpolation function
xp = x_2 - x_1
x = x_1 + mu*(x_2-x_1)
So to keep the spacing equal, you must use linear interpolation.
To provide equal spacing, you need to get curve segments of equal length. Note that curve length (both for the whole curve and for it's fragment) might be calculated using integration (in parametric form from here)
ds = Sqrt((dx/dt)^2+(dy/dt)^2)*dt
L = Integral{t=a..b} (ds), where t = 0..1 for overall length.
Sadly for cubic curves such integral (here it is elliptic) cannot be solved analytically and should be calculated numerically. It is well-known problem for Bezier curves, for instance.
So to build equal-length segments, you need to numerically find length L of the whole curve, find l[i]=i*L/n for position of i-th point and numerically find (for example, with binary search) parameter for this position.
Related
I am writing a graphics application that needs to calculate and display a list of points along a curve arc which is described by three points.
Lets say we have points (1,1), (2,4) and (5,2). I need an algorithm that can give me the values of y for each x from 1 to 5 that fall on the interpolated arc.
I'm sure this is a simple task for you math whizes out there, but for me it's a bit beyond my mathematical payscale.
Thanks in advance!
So the problem is how to compute the center C = (c1, c2) and radius r of a circumference given by three points P = (p1, p2), Q = (q1, q2) and S = (s1, s2).
The idea is very simple. It consists in realizing that, by definition, the center has the same distance to all three points P, Q and S.
Now, the set of all points that are equidistant from Pand Q is the perpendicular to the segment PQ incident at the mid point (P+Q)/2. Similarly, the set of all points equidistant from Q and S is the perpendicular to QS passing thru (Q+S)/2. So, the center C must be the intersection of these two lines.
Let's compute the parametric equations of these two straight lines.
For this we will need two additional functions that I will call dist(A,B) which computes the distance between points A and B and perp(A,B) that normalizes the vector B-A dividing it by its length (or norm) and answers the perpendicular vector to this normalized vector (keep in mind that a perpendicular to (a,b) is (-b,a) because their inner product is 0)
dist((a1,a2),(b1,b2))
Return sqrt(square(b1-a1) + square(b2-a2))
perp((a1,a2),(b1,b2))
dist := dist((a1,a2),(b1,b2)).
a := (b1-a1)/dist.
b := (b2-a2)/dist.
Return (-b,a).
We can now write the parametric expressions of our two lines
(P+Q)/2 + perp(P,Q)*t
(Q+S)/2 + perp(Q,S)*u
Note that both parameters are different, hence the introduction of two variables t and u.
Equating these parametric expressions:
(P+Q)/2 + perp(P,Q)*t = (Q+S)/2 + perp(Q,S)*u
which consists of two linear equations, one for each coordinate, and two unknowns t and u (see below). The solution of this 2x2 system gives the values of the parameters t and u that injected into the parametric expressions give the center C of the circumference.
Once C is known, the radius r can be calculated as r := dist(P,C).
Linear equations
(P+Q)/2 + perp(P,Q)*t = (Q+S)/2 + perp(Q,S)*u
First linear equation (coordinate x)
(p1+q1)/2 + (p2-q2)/dist(P,Q)*t = (q1+s1)/2 + (q2-s2)/dist(Q,S)*u
Second linear equation (coordinate y)
(p2+q2)/2 + (q1-p1)/dist(P,Q)*t = (q2+s2)/2 + (s1-q1)/dist(Q,S)*u
Linear System (2x2)
(p2-q2)/dist(P,Q)*t + (s2-q2)/dist(Q,S)*u = (s1-p1)/2
(q1-p1)/dist(P,Q)*t + (q1-s1)/dist(Q,S)*u = (s2-p2)/2
Consider points Y given in increasing order from [0,T). We are to consider these points as lying on a circle of circumference T. Now consider points X also from [0,T) and also lying on a circle of circumference T.
We say the distance between X and Y is the sum of the absolute distance between the each point in X and its closest point in Y recalling that both are considered to be lying in a circle. Write this distance as Delta(X, Y).
I am trying to find a quick way of determining a rotation of X which makes this distance as small as possible.
My code for making some data to test with is
#!/usr/bin/python
import random
import numpy as np
from bisect import bisect_left
def simul(rate, T):
time = np.random.exponential(rate)
times = [0]
newtime = times[-1]+time
while (newtime < T):
times.append(newtime)
newtime = newtime+np.random.exponential(rate)
return times[1:]
For each point I use this function to find its closest neighbor.
def takeClosest(myList, myNumber, T):
"""
Assumes myList is sorted. Returns closest value to myNumber in a circle of circumference T.
If two numbers are equally close, return the smallest number.
"""
pos = bisect_left(myList, myNumber)
before = myList[pos - 1]
after = myList[pos%len(myList)]
if after - myNumber < myNumber - before:
return after
else:
return before
So the distance between two circles is:
def circle_dist(timesY, timesX):
dist = 0
for t in timesX:
closest_number = takeClosest(timesY, t, T)
dist += np.abs(closest_number - t)
return dist
So to make some data we just do
#First make some data
T = 5000
timesX = simul(1, T)
timesY = simul(10, T)
Finally to rotate circle timesX by offset we can
timesX = [(t + offset)%T for t in timesX]
In practice my timesX and timesY will have about 20,000 points each.
Given timesX and timesY, how can I quickly find (approximately) which rotation of timesX gives
the smallest distance to timesY?
Distance along the circle between a single point and a set of points is a piecewise linear function of rotation. The critical points of this function are the points of the set itself (zero distance) and points midway between neighbouring points of the set (local maximums of distance). Linear coefficients of such function are ±1.
Sum of such functions is again piecewise linear, but now with a quadratic number of critical points. Actually all these functions are the same, except shifted along the argument axis. Linear coefficients of the sum are integers.
To find its minimum one would have to calculate its value in all critical points.
I don'see a way to significantly reduce the amount of work needed, but 1,600,000,000 points is not such a big deal anyway, especially if you can spread the work between several processors.
To calculate sum of two such functions, represent the summands as sequences of critical points and associated coefficients to the left and to the right of each critical point. Then just merge the two point sequences while adding the coefficients.
You can solve your (original) problem with a sweep line algorithm. The trick is to use the right "discretization". Imagine cutting your circle up into two strips:
X: x....x....x..........x................x.........x...x
Y: .....x..........x.....x..x.x...........x.............
Now calculate the score = 5+0++1+1+5+9+6.
The key observation is that if we rotate X very slightly (right say), some of the points will improve and some will get worse. We can call this the "differential". In the above example the differential would be 1 - 1 - 1 + 1 + 1 - 1 + 1 because the first point is matched to something on its right, the second point is matched to something under it or to its left etc.
Of course, as we move X more, the differential will change. However only as many times as the matchings change, which is never more than |X||Y| but probably much less.
The proposed algorithm is thus to calculate the initial score and the time (X position) of the next change in differential. Go to that next position and calculate the score again. Continue until you reach your starting position.
This is probably a good example for the iterative closest point (ICP) algorithm:
It repeatedly matches each point with its closest neighbor and moves all points such that the mean squared distance is minimized. (Note that this corresponds to minimizing the sum of squared distances.)
import pylab as pl
T = 10.0
X = pl.array([3, 5.5, 6])
Y = pl.array([1, 1.5, 2, 4])
pl.clf()
pl.subplot(1, 2, 1, polar=True)
pl.plot(X / T * 2 * pl.pi, pl.ones(X.shape), 'r.', ms=10, mew=3)
pl.plot(Y / T * 2 * pl.pi, pl.ones(Y.shape), 'b+', ms=10, mew=3)
circDist = lambda X, Y: (Y - X + T / 2) % T - T / 2
while True:
D = circDist(pl.reshape(X, (-1, 1)), pl.reshape(Y, (1, -1)))
closestY = pl.argmin(D**2, axis = 1)
distance = circDist(X, Y[closestY])
shift = pl.mean(distance)
if pl.absolute(shift) < 1e-3:
break
X = (X + shift) % T
pl.subplot(1, 2, 2, polar=True)
pl.plot(X / T * 2 * pl.pi, pl.ones(X.shape), 'r.', ms=10, mew=3)
pl.plot(Y / T * 2 * pl.pi, pl.ones(Y.shape), 'b+', ms=10, mew=3)
Important properties of the proposed solution are:
The ICP is an iterative algorithm. Thus it depends on an initial approximate solution. Furthermore, it won't always converge to the global optimum. This mainly depends on your data and the initial solution. If in doubt, try evaluating the ICP with different starting configurations and choose the most frequent result.
The current implementation performs a directed match: It looks for the closest point in Y relative to each point in X. It might yield different matches when swapping X and Y.
Computing all pair-wise distances between points in X and points in Y might be intractable for large point clouds (like 20,000 points, as you indicated). Therefore, the line D = circDist(...) might get replaced by a more efficient approach, e.g. not evaluating all possible pairs.
All points contribute to the final rotation. If there are any outliers, they might distort the shift significantly. This can be overcome with a robust average like the median or simply by excluding points with large distance.
I am trying to find the angle of the outer line of the object in the green region of the image as shown in the image above…
For that, I have scanned the green region and get the points (dark blue points as shown in the image)...
As you can see, the points are not making straight line so I can’t find angle easily.
So I think I have to find a middle way and
that is to find the line so that the distance between each point and line remain as minimum as possible.
So how can I find the line so that each point exposes minimum distance to it……?
Is there any algorithm for this or is there any good way other than this?
The obvious route would be to do a least-squares linear regression through the points.
The standard least squares regression formulae for x on y or y on x assume there is no error in one coordinate and minimize the deviations in the coordinate from the line.
However, it is perfectly possible to set up a least squares calculation such that the value minimized is the sum of squares of the perpendicular distances of the points from the lines. I'm not sure whether I can locate the notebooks where I did the mathematics - it was over twenty years ago - but I did find the code I wrote at the time to implement the algorithm.
With:
n = ∑ 1
sx = ∑ x
sx2 = ∑ x2
sy = ∑ y
sy2 = ∑ y2
sxy = ∑ x·y
You can calculate the variances of x and y and the covariance:
vx = sx2 - ((sx * sx) / n)
vy = sy2 - ((sy * sy) / n)
vxy = sxy - ((sx * sy) / n)
Now, if the covariance is 0, then there is no semblance of a line. Otherwise, the slope and intercept can be found from:
slope = quad((vx - vy) / vxy, vxy)
intcpt = (sy - slope * sx) / n
Where quad() is a function that calculates the root of quadratic equation x2 + b·x - 1 with the same sign as c. In C, that would be:
double quad(double b, double c)
{
double b1;
double q;
b1 = sqrt(b * b + 4.0);
if (c < 0.0)
q = -(b1 + b) / 2;
else
q = (b1 - b) / 2;
return (q);
}
From there, you can find the angle of your line easily enough.
Obviously the line will pass through averaged point (x_average,y_average).
For direction you may use the following algorithm (derived directly from minimizing average square distance between line and points):
dx[i]=x[i]-x_average;
dy[i]=y[i]-y_average;
a=sum(dx[i]^2-dy[i]^2);
b=sum(2*dx[i]*dy[i]);
direction=atan2(b,a);
Usual linear regression will not work here, because it assumes that variables are not symmetric - one depends on other, so if you will swap x and y, you will have another solution.
The hough transform might be also a good option:
http://en.wikipedia.org/wiki/Hough_transform
You might try searching for "total least squares", or "least orthogonal distance" but when I tried that I saw nothing immediately applicable.
Anyway suppose you have points x[],y[], and the line is represented by a*x+b*y+c = 0, where hypot(a,b) = 1. The least orthogonal distance line is the one that minimises Sum{ (a*x[i]+b*y[i]+c)^2}. Some algebra shows that:
c is -(a*X+b*Y) where X is the mean of the x's and Y the mean of the y's.
(a,b) is the eigenvector of C corresponding to it's smaller eigenvalue, where C is the covariance matrix of the x's and y's
I have a quadratic bezier curve described as (startX, startY) to (anchorX, anchorY) and using a control point (controlX, controlY).
I have two questions:
(1) I want to determine y points on that curve based on an x point.
(2) Then, given a line-segment on my bezier (defined by two intermediary points on my bezier curve (startX', startY', anchorX', anchorY')), I want to know the control point for that line-segment so that it overlaps the original bezier exactly.
Why? I want this information for an optimization. I am drawing lots of horizontal beziers. When the beziers are larger than the screen, performance suffers because the rendering engine ends up rendering beyond the extents of what is visible. The answers to this question will let me just render what is visible.
Part 1
The formula for a quadratic Bezier is:
B(t) = a(1-t)2 + 2bt(1-t) + ct2
= a(1-2t+t2) + 2bt - 2bt2 + ct2
= (a-2b+c)t2+2(b-a)t + a
where bold indicates a vector. With Bx(t) given, we have:
x = (ax-2bx+cx)t2+2(bx-ax)t + ax
where vx is the x component of v.
According to the quadratic formula,
-2(bx-ax) ± 2√((bx-ax)2 - ax(ax-2bx+cx))
t = -----------------------------------------
2(ax-2bx+cx)
ax-bx ± √(bx2 - axcx)
= ----------------------
ax-2bx+cx
Assuming a solution exists, plug that t back into the original equation to get the other components of B(t) at a given x.
Part 2
Rather than producing a second Bezier curve that coincides with part of the first (I don't feel like crunching symbols right now), you can simply limit the domain of your parametric parameter to a proper sub-interval of [0,1]. That is, use part 1 to find the values of t for two different values of x; call these t-values i and j. Draw B(t) for t ∈ [i,j]. Equivalently, draw B(t(j-i)+i) for t ∈ [0,1].
The t equation is wrong, you need to use eq(1)
(1) x = (ax-2bx+cx)t2+2(bx-ax)t + ax
and solve it using the the quadratic formula for the roots (2).
-b ± √(b^2 - 4ac)
(2) x = -----------------
2a
Where
a = ax-2bx+cx
b = 2(bx-ax)
c = ax - x
I would like to get some code in AS2 to interpolate a quadratic bezier curve. the nodes are meant to be at constant distance away from each other. Basically it is to animate a ball at constant speed along a non-hyperbolic quadratic bezier curve defined by 3 pts.
Thanks!
The Bezier curve math is really quite simple, so I'll help you out with that and you can translate it into ActionScript.
A 2D quadratic Bezier curve is defined by three (x,y) coordinates. I will refer to these as P0 = (x0,y0), P1 = (x1,y1) and P2 = (x2,y2). Additionally a parameter value t, which ranges from 0 to 1, is used to indicate any position along the curve. All x, y and t variables are real-valued (floating point).
The equation for a quadratic Bezier curve is:
P(t) = P0*(1-t)^2 + P1*2*(1-t)*t + P2*t^2
So, using pseudocode, we can smoothly trace out the Bezier curve like so:
for i = 0 to step_count
t = i / step_count
u = 1 - t
P = P0*u*u + P1*2*u*t + P2*t*t
draw_ball_at_position( P )
This assumes that you have already defined the points P0, P1 and P2 as above. If you space the control points evenly then you should get nice even steps along the curve. Just define step_count to be the number of steps along the curve that you would like to see.
Please note that the expression can be done much more efficient mathematically.
P(t) = P0*(1-t)^2 + P1*2*(1-t)*t + P2*t^2
and
P = P0*u*u + P1*2*u*t + P2*t*t
both hold t multiplications which can be simplified.
For example:
C = A*t + B(1-t) = A*t + B - B*t = t*(A-B) + B = You saved one multiplication = Double performance.
The solution proposed by Naaff, that is P(t) = P0*(1-t)^2 + P1*2*(1-t)*t + P2*t^2, will get you the correct "shape", but selecting evenly-spaced t in the [0:1] interval will not produce evenly-spaced P(t). In other words, the speed is not constant (you can differentiate the previous equation with respect to t to see see it).
Usually, a common method to traverse a parametric curve at constant-speed is to reparametrize by arc-length. This means expressing P as P(s) where s is the length traversed along the curve. Obviously, s varies from zero to the total length of the curve. In the case of a quadratic bezier curve, there's a closed-form solution for the arc-length as a function of t, but it's a bit complicated. Computationally, it's often faster to just integrate numerically using your favorite method. Notice however that the idea is to compute the inverse relation, that is, t(s), so as to express P as P(t(s)). Then, choosing evenly-spaced s will produce evenly-space P.