Finding Y given X on a Cubic Bezier Curve? - c++

I need a method that allows me to find the Y-coordinate on a Cubic Bezier Curve, given an x-coordinate.
I've come across lots of places telling me to treat it as a cubic function then attempt to find the roots, which I understand. HOWEVER the equation for a Cubic Bezier curve is (for x-coords):
X(t) = (1-t)^3 * X0 + 3*(1-t)^2 * t * X1 + 3*(1-t) * t^2 * X2 + t^3 * X3
What confuses me is the addition of the (1-t) values. For instance, if I fill in the X values with some random numbers:
400 = (1-t)^3 * 100 + 3*(1-t)^2 * t * 600 + 3*(1-t) * t^2 * 800 + t^3 * 800
then rearrange it:
800t^3 + 3*(1-t)*800t^2 + 3*(1-t)^2*600t + (1-t)^3*100 -400 = 0
I still don't know the value of the (1-t) coefficients. How I am I supposed to solve the equation when (1-t) is still unknown?

There are three common ways of expressing a cubic bezier curve.
First x as a function of t
x(t) = sum( f_i(t) x_i )
= (1-t)^3 * x0 + 3*(1-t)^2 * t * x1 + 3*(1-t) * t^2 * x2 + t^3 * x3
Secondly y as a function of x
y(x) = sum( f_i(x) a_i )
= (1-x)^3 * y0 + 3*(1-x)^2 * x * y1 + 3*(1-x) * x^2 * y2 + x^3 * y3
These first two are mathematically the same, just using different names for the variables.
Judging by your description "find the Y-coordinate on a Cubic Bezier Curve, given an x-coordinate on it." I'm guessing that you've got a question using the second equation are are trying to rearrange the first equation to help you solve it, where as you should be using the second equation. If thats the case, then no rearranging or solving is required - just plug your x value in and you have the solution.
Its possible that you have an equation of the third kind case, which is the ugly and hard case.
This is both the x and y parameters are cubic Beziers of a third variable t.
x(t) = sum( f_i(t) x_i )
y(t) = sum( f_i(t) y_i )
If this is your case. Let me know and I can detail what you need to do to solve it.

I think this is a fair CS question, so I'm going to attempt to show how I solved this. Note that a given x may have more than 1 y value associated with it. In the case where I needed this, that was guaranteed not to be the case, so you'll have to figure out how to determine which one you want.
I iterated over t generating an array of x and y values. I did it at a fairly high resolution for my purposes. (I was looking to generate an 8-bit look-up table, so I used ~1000 points.) I just plugged t into the bezier equation for the next x and the next y coordinates to store in the array. Once I had the entire thing generated, I scanned through the array to find the 2 nearest x values. (Or if there was an exact match, used that.) I then did a linear interpolation on that very small line segment to get the y-value I needed.

Developing the expression further should get you rid of the (1 - t) factors
If you run:
expand(800*t^3 + 3*(1-t)*800*t^2 + 3*(1-t)^2*600*t + (1-t)^3*100 -400 = 0);
In either wxMaxima or Maple (you have to add the parameter t in this one though), you get:
100*t^3 - 900*t^2 + 1500*t - 300 = 0
Solve the new cubic equation for t (you can use the cubic equation formula for that), after you got t, you can find x doing:
x = (x4 - x0) * t (asuming x4 > x0)

Equation for Bezier curve (getting x value):
Bx = (-t^3 + 3*t^2 - 3*t + 1) * P0x +
(3*t^3 - 6*t^2 + 3*t) * P1x +
(-3*t^3 + 3*t^2) * P2x +
(t^3) * P3x
Rearrange in the form of a cubic of t
0 = (-P0x + 3*P1x - 3*P2x + P3x) * t^3+
(3*P0x - 6*P1x + 3*P2x) * t^2 +
(-3*P0x + 3*P1x) * t +
(P0x) * P3x - Bx
Solve this using the cubic formula to find values for t. There may be multiple real values of t (if your curve crosses the same x point twice). In my case I was dealing with a situation where there was only ever a single y value for any value of x. So I was able to just take the only real root as the value of t.
a = -P0x + 3.0 * P1x - 3.0 * P2x + P3x;
b = 3.0 * P0x - 6.0 * P1x + 3.0 * P2x;
c = -3.0 * P0x + 3.0 * P1x;
d = P0x;
t = CubicFormula(a, b, c, d);
Next put the value of t back into the Bezier curve for y
By = (1-t)^3 * P0x +
3t(1-t)^2 * P1x +
3t^2(1-t) * P2x +
t^3 * P3x

So I've been looking around for some sort of method to allow me to find the Y-coordinate on a Cubic Bezier Curve, given an x-coordinate on it.
Consider a cubic bezier curve between points (0, 0) and (0, 100), with control points at (0, 33) and (0, 66). There are an infinite number of Y's there for a given X. So there's no equation that's going to solve Y given X for an arbitrary cubic bezier.
For a robust solution, you'll likely want to start with De Casteljau's algorithm
Split the curve recursively until individual segments approximate a straight line. You can then detect whether and where these various line segments intercept your x or whether they are vertical line segments whose x corresponds to the x you're looking for (my example above).

Related

Determine if points are within a rotated rectangle (standard Python 2.7 library only) [duplicate]

This question already has answers here:
Finding whether a point lies inside a rectangle or not
(10 answers)
Closed 2 years ago.
I have a rotated rectangle with these coordinates as vertices:
1 670273 4879507
2 677241 4859302
3 670388 4856938
4 663420 4877144
And I have points with these coordinates:
670831 4867989
675097 4869543
Using only the Python 2.7 standard library, I want to determine if the points fall within the rotated rectangle.
I am not able to add additional Python libraries to my Jython implementation
What would it take to do this?
A line equation of the form ax+by+c==0 can be constructed from 2 points. For a given point to be inside a convex shape, we need testing whether it lies on the same side of every line defined by the shape's edges.
In pure Python code, taking care of writing the equations avoiding divisions, this could be as follows:
def is_on_right_side(x, y, xy0, xy1):
x0, y0 = xy0
x1, y1 = xy1
a = float(y1 - y0)
b = float(x0 - x1)
c = - a*x0 - b*y0
return a*x + b*y + c >= 0
def test_point(x, y, vertices):
num_vert = len(vertices)
is_right = [is_on_right_side(x, y, vertices[i], vertices[(i + 1) % num_vert]) for i in range(num_vert)]
all_left = not any(is_right)
all_right = all(is_right)
return all_left or all_right
vertices = [(670273, 4879507), (677241, 4859302), (670388, 4856938), (663420, 4877144)]
The following plot tests the code visually for several shapes. Note that for shapes with horizontal and vertical lines usual line equations could provoke division by zero.
import matplotlib.pyplot as plt
import numpy as np
vertices1 = [(670273, 4879507), (677241, 4859302), (670388, 4856938), (663420, 4877144)]
vertices2 = [(680000, 4872000), (680000, 4879000), (690000, 4879000), (690000, 4872000)]
vertices3 = [(655000, 4857000), (655000, 4875000), (665000, 4857000)]
k = np.arange(6)
r = 8000
vertices4 = np.vstack([690000 + r * np.cos(k * 2 * np.pi / 6), 4863000 + r * np.sin(k * 2 * np.pi / 6)]).T
all_shapes = [vertices1, vertices2, vertices3, vertices4]
for vertices in all_shapes:
plt.plot([x for x, y in vertices] + [vertices[0][0]], [y for x, y in vertices] + [vertices[0][1]], 'g-', lw=3)
for x, y in zip(np.random.randint(650000, 700000, 1000), np.random.randint(4855000, 4880000, 1000)):
color = 'turquoise'
for vertices in all_shapes:
if test_point(x, y, vertices):
color = 'tomato'
plt.plot(x, y, '.', color=color)
plt.gca().set_aspect('equal')
plt.show()
PS: In case you are running a 32-bit version of numpy, with this size of integers it might be necessary to convert the values to float to avoid overflow.
If this calculation needs to happen very often, the a,b,c values can be precalculated and stored. If the direction of the edges is known, only one of all_left or all_right is needed.
When the shape is fixed, a text version of the function can be generated:
def generate_test_function(vertices, is_clockwise=True, function_name='test_function'):
ext_vert = list(vertices) + [vertices[0]]
unequality_sign = '>=' if is_clockwise else '<='
print(f'def {function_name}(x, y):')
parts = []
for (x0, y0), (x1, y1) in zip(ext_vert[:-1], ext_vert[1:]):
a = float(y1 - y0)
b = float(x0 - x1)
c = a * x0 + b * y0
parts.append(f'({a}*x + {b}*y {unequality_sign} {c})')
print(' return', ' and '.join(parts))
vertices = [(670273, 4879507), (677241, 4859302), (670388, 4856938), (663420, 4877144)]
generate_test_function(vertices)
This would generate a function as:
def test_function(x, y):
return (-20205.0*x + -6968.0*y >= -47543270741.0) and (-2364.0*x + 6853.0*y >= 31699798882.0) and (20206.0*x + 6968.0*y >= 47389003912.0) and (2363.0*x + -6853.0*y >= -31855406372.0)
This function then can be copy-pasted and optimized by the Jython compiler. Note that the shape doesn't need to be rectangular. Any convex shape will do, allowing to use a tighter box.
Take three consequent vertices A, B, C (your 1,2,3)
Find lengths of sides AB and BC
lAB = sqrt((B.x - A.x)^2+(B.y - A.y)^2)
Get unit (normalized) direction vectors
uAB = ((B.x - A.x) / lAB, (B.y - A.y) / lAB)
For tested point P get vector BP
BP = ((P.x - B.x), (P.y - B.y))
And calculate signed distances from sides to point using cross product
SignedDistABP = Cross(BP, uAB) = BP.x * uAB.y - BP.y * uAB.x
SignedDistBCP = - Cross(BP, uBC) = - BP.x * uBC.y + BP.y * uBC.x
For points inside rectangle both distances should have the same sign - either negative or positive depending on vertices order (CW or CCW), and their absolute values should not be larger than lBC and lAB correspondingly
Abs(SignedDistABP) <= lBC
Abs(SignedDistBCP) <= lAB
As the shape is an exact rectangle, the easiest is to rotate all points by the angle
-arctan((4859302-4856938)/(677241-670388))
Doing so, the rectangle becomes axis-aligned and you just have to perform four coordinate comparisons. Rotations are easy to compute with complex numbers.
In fact you can simply represent all points as complex numbers, compute the vector defined by some side, and multiply everything by the conjugate.
A slightly different approach is to consider the change of coordinate frame that brings some corner to the origin and two incident sides to (1,0) and (0,1). This is an affine transformation. Then your test boils down to checking insideness to the unit square.

Projectile Motion in 3D: Calculating Time of Collision

I'm writing a function that takes in an object with a trajectory (including starting position, starting velocity, and acceleration, all represented as Vector3s) in 3D space and if it hits another object, returns the point of collision and time of the collision. I'm using kinematic equations with a timestep to detect possible collisions and I can get the point of collision that way, but once I have that I want to find the exact time that that collision would occur at.I thought of rearranging a kinematic equation to solve for time and plug in what I already had, but I can't figure out how I can use all three axes of motion to do this, since my other values are Vec3's and time is just scalar. I've thought about just doing the calculation on one axis, but I'm not sure if that would lead to an accurate result.
Would it be accurate to calculate just based on one axis, or is there a way to incorporate all three into the calculation? The formula I'm using to solve for time is:
t = (v_init +/- Sqrt((v_init)^2 - (accel * disp * 4 * .5)))/accel;
Where v_init is initial velocity, disp is total displacement, and accel is acceleration. I'm basing this off of the kinematic equation:
d = v*t + .5*a*t^2
Let me write in the general case. The component-wise motion law is
x(t) = x0 + v_x t + 0.5 a_x t^2
y(t) = y0 + v_y t + 0.5 a_y t^2
z(t) = z0 + v_z t + 0.5 a_z t^2
where (x0,y0,z0)^t is the initial position, (v_x, v_y, v_z)^t is the initial velocity vector, and (a_x, a_y, a_z)^t is the vector of acceleration. The 3rd component of the latter may include also the gravity acceleration.
I assume that the collision plane is horizontal, having thus equation z = k. Solve in t the equation
z(t) = k
for finding the time t_c in which the projectile hits the plane. Compute then the collision coordinates x(t_c) and y(t_c) using the above formula by substituting t with t_c.
If the plane has the general equation
a x + b y +c z + d = 0
I suggest to put the frame of reference on the plane, having the xy plane on the collision plane, and then apply the above procedure.
You may also solve the non linear system
x = x0 + v_x t + 0.5 a_x t^2
y = y0 + v_y t + 0.5 a_y t^2
z = z0 + v_z t + 0.5 a_z t^2
a x + b y +c z + d = 0
taking the solution for t>0 (I dropped the dependency on t for x, y and z).
To solve it in C++, you may search a math library, such as Eigen which has a module for non linear systems.

Finding Perpendicular Points Given An Angle With Piece-wise Hermite Splines

I am given a Hermite spline from which I want to create another spline with every point on that spline being exactly x distance away.
Here's an example of what I want to do:
.
I can find every derivative and point on the original spline. I also know all the coefficients of each polynomial.
Here's the code that I've came up with that does this for every control point of the original spline. Where controlPath[i] is a vector of control points that makeup the spline, and Point is a struct representing a 2D point with its facing angle.
double x, y, a;
a = controlPath[i].Angle + 90;
x = x * cosf(a * (PI / 180)) + controlPath[i].X;
y = x * sinf(a * (PI / 180)) + controlPath[i].Y;
Point l(x, y, a - 90);
a = controlPath[i].Angle - 90;
x = x * cosf(a * (PI / 180)) + controlPath[i].X;
y = x * sinf(a * (PI / 180)) + controlPath[i].Y;
Point r(x, y, a + 90);
This method work to an extent, but its results are subpar.
Result of this method using input:
The inaccuracy is not good. How do I confront this issue?
If you build normals of given length in every point of Hermite spline and connect endpoint of these normals, resulting curve (so-called parallel curve) is not Hermit spline in general case. The same is true for Bezier curve and the most of pther curve (only circle arc generates self-similar curve and some exotic curves).
So to generate reliable result, it is worth to subdivide curve into small pieces, build normals in all intermediate points and generate smooth piecewise splines through "parallel points"
Also note doubtful using x in the right part of formulas - should be some distance.
Also you don't need to calculate sin/cos twice
double x, y, a, d, c, s;
a = controlPath[i].Angle + 90;
c = d * cosf(a * (PI / 180));
s = d * sinf(a * (PI / 180))
x = c + controlPath[i].X;
y = s + controlPath[i].Y;
Point l(x, y, controlPath[i].Angle);
x = -c + controlPath[i].X;
y = -s + controlPath[i].Y;
Point l(x, y, controlPath[i].Angle);

OpenGL - Creating a circle, change radius?

I must be the worst person on the planet when it comes to math because i can't figure out how to change this circle radius:
from math import *
posx, posy = 0,0
sides = 32
glBegin(GL_POLYGON)
for i in range(100):
cosine=cos(i*2*pi/sides)+posx
sine=sin(i*2*pi/sides)+posy
glVertex2f(cosine,sine)
I'm not entirely sure how or why this becomes a circle because the *2 confuses me a bit.
Note that this is done in Pyglet under Python2.6 calling OpenGL libraries.
Followed Example 4-1: http://fly.cc.fer.hr/~unreal/theredbook/chapter04.html
Clarification: This works, i'm interested in why and how to modify the radius.
This should do the trick :)
from math import *
posx, posy = 0,0
sides = 32
radius = 1
glBegin(GL_POLYGON)
for i in range(100):
cosine= radius * cos(i*2*pi/sides) + posx
sine = radius * sin(i*2*pi/sides) + posy
glVertex2f(cosine,sine)
But I would pick another names for variables. cosine and sine is not exactly what these variables are.
And as far as I see, you son't need a loop from 1 to 100 (or from 0 to 99, I'm not too good at Python), you just need a loop from 1 to sides.
Explanation:
When you calculate
x = cos (angle)
y = sin(angle)
you get a point on a circle with radius = 1, and centre in the point (0; 0) (because sin^2(angle) + cos^2(angle) = 1).
If you want to change a radius to R, you simply multiply cos and sin by R.
x = R * cos (angle)
y = R * sin(angle)
If you want to transfer the circle to another location (for example, you want the circle to have it's centre at (X_centre, Y_centre), you add X_centre and Y_xentre to x and y accordingly:
x = R * cos (angle) + X_centre
y = R * sin(angle) + Y_centre
When you need to loop through N points (in your case N = sides) on your circle, you should change the angle on each iteration. All those angles should be equal and their sum should be 2 * pi. So each angle should be equal to 2 * pi/ N. And to get i-th angle you multiply this value by i: i * 2 * pi / N.
math : P=pr^2=p*r*r= p*r*2 programming i*2*pi/sides
together : i = p i*2, *2=r^2 this should help you

(C++) Need to figure out all points within a radius using reg. 2D windows coord. system

Sorry in advance, I'm struggling a bit with how to explain this... :)
Essentially, I've got a typical windows coordinate system (the Top, Left is 0,0). If anybody's familiar with the haversine query, like in SQL, it can get all points in a radius based on latitude and longitude coordinates.
I need something much simpler, but my math skills ain't all up to par! Basically, I've got random points scattered throughout about a 600x400 space. I have a need to, for any X,Y point on the map, run a query to determine how many other points are within a given radius of that one.
If that's not descriptive enough, just let me know!
Straightforward approach:
You can calculate the distance between to points using the Pythagorean theorem:
deltaX = x1 - x2
deltaY = y1 - y2
distance = square root of (deltaX * deltaX + deltaY * deltaY)
Given point x1,y1, do this for every other point (x2,y2) to see if the calculated distance is within (less than or equal to) your radius.
If you want to make it speedier, calculate and store the square of the radius and just compare against (deltaX * deltaX + deltaY * deltaY), avoiding the square root.
Before doing the Pythagoras, you could also quickly eliminate any point that falls outside of the square that can fully contain the target circle.
// Is (x1, y1) in the circle defined by center (x,y) and radius r
bool IsPointInCircle(x1, y1, x, y, r)
{
if (x1 < x-r || x1 > x+r)
return false;
if (y1 < y-r || y1 > y+r)
return false;
return (x1-x)*(x1-x) + (y1-y)*(y1-y) <= r*r
}
Use Pythagoras:
distance = sqrt(xDifference^2 + yDifference^2)
Note that '^' in this example means "to the power of" and not C's bitwise XOR operator. In other words the idea is to square both differences.
If you only care about relative distance you shouldn't use square root you can do something like:
rSquared = radius * radius #square the radius
foreach x, y in Points do
dX = (x - centerX) * (x - centerX) #delta X
dY = (y - centerY) * (y - centerY) #delta Y
if ( dX + dY <= rSquared ) then
#Point is within Circle
end
end
Using the equation for a circle:
radius ** 2 = (x - centerX) ** 2 + (y - centerY) ** 2
We want to find if a point (x, y) is inside of the circle. We perform the test using this equation:
radius ** 2 < (x - centerX) ** 2 + (y - centerY) ** 2
// (Or use <= if you want the circumference of the circle to be included as well)
Simply substitute your values into that equation. If it works (the inequality is true), the point is inside of the circle. Otherwise, it isn't.