3D Point to 2D Function Not Working Properly - math

I have been researching this topic for some time now and I am finally trying to implement it myself; however, for some reason the 3d point is not converting to a 2d point correctly, which means that my function is returning the wrong value. This could be from 2 different cases:
1) My math is incorrect
2) My Matrix has the incorrect values
Since I am reversing and using addresses, I am unsure of the matrix. Is it possible for someone to check my math to see if it is the math that is incorrect? Any help on this function will be appreciated. Thank you in advance for any advice.
private bool ConvertToScreen(Vector3 position3D, ref Point screenPoint)
{
// r is the right rotation (x-axis)
// u is the up rotation (y-axis)
// f is the forward rotation (z-axis)
// p is the position (transform)
Point returnPoint = new Point(300, 400);
// Set Values of Matrix
Matrix matrix = GetMatrix();
// Do the math calculations here
float xPrime = matrix.rX * position3D.x + matrix.rY * position3D.y + matrix.rZ * position3D.z + matrix.rW;
float yPrime = matrix.uX * position3D.x + matrix.uY * position3D.y + matrix.uZ * position3D.z + matrix.uW;
// Dont need zPrime
float wPrime = matrix.pX * position3D.x + matrix.pY * position3D.y + matrix.pZ * position3D.z + matrix.pW;
// If wPrime > 0 we can see the point
if (wPrime <= 0)
{
return false;
}
xPrime *= 1 / wPrime;
yPrime *= 1 / wPrime;
// Relative To Screen Center
xPrime = rect.left + 0.5f * xPrime * (rect.right - rect.left) + 0.5f;
yPrime = rect.top + 0.5f * yPrime * (rect.bottom - rect.top) + 0.5f;
returnPoint = new Point((int)xPrime, (int)yPrime);
screenPoint = returnPoint;
return true;
}

There are several steps to this calculation, and I advise to split it up into corresponding sections and test each one.
Model to camera coordinates - This is the rotation of position = (x,y,z) points from model coordinates into camera coordinates. Here we assume that the target of the camera is at the origin.
local = rot * (position - target)
| x' | | rx ry rz | | x | | rx*x + ry*y + rz*z |
| y' | = | ux uy yz | * | y | = | ux*x + uy*y + uz*z |
| z' | | fx fy fz | | z | | fx*x + fy*y + fz*z |
Perspective Projection - You need a definition of the distance between the target and the camera, as well as the size of the model that would cover the view. Let's call those, distance and size. The result is in view coordinates that vary between 0..1 in both x and y.
| vx | | (distance/size)*(x'/(distance+z')) |
| | = | | |
| vy | | (distance/size)*(y'/(distance+z')) |
This comes out of similar triangles. If x'=size and z'=0 then vx=1. The larger z' is the smaller vx becomes.
Pixel Coordinates
Here you map the view coordinates into pixels. Your viewport has a width and a height and you want [0,0] pixels on the top left, and [width-1,height-1] to the bottom right.
width
+-------------------+
|(-1,1) : (1,1)|
| : |
| : (0,0) |
+- - - - -+- - - - -+ height
| : |
| : |
|(-1,-1) : (1,-1)|
+-------------------+
px = (width-1)*(vx+1.0)/2.0
py = (height-1)*(1.0-vy)/2.0
Finally, I recommend using OOP programming (if possible) to separate the vector/matrix math to the intent. Consider the following example in c#.
public static Vector3 ThroughCamera(Vector3 point, Vector3 target, Matrix3 camera)
{
return camera.Transpose()*(point-target);
}
public static Vector2 Perspective(Vector3 point, double distance, double size=1)
{
return new Vector2(
(distance/size)*(point.X/(distance+point.Z)),
(distance/size)*(point.Y/(distance+point.Z)) );
}
public static PointF Pixel(Vector2 point, int width, int height)
{
return new PointF(
(float) ((width-1)*(point.X+1)/2),
(float) ((height-1)*(1-point.Y)/2) );
}
static void dlg_Paint(object sender, PaintEventArgs e)
{
Form dlg=sender as Form;
// Set camera rotation
Matrix3 camera=Matrix3.Ry(0.67);
double distance=25;
double size=20;
for(int i=0; i<10; i++)
{
for(int j=0; j<10; j++)
{
// this is the model points
Vector3 pt=new Vector3(5*(i-5), 5*(j-5), 0);
// these are the points through the camera
Vector3 pt_local=ThroughCamera(pt, Vector3.O, camera);
// these are the view coordinates
Vector2 pt_view=Perspective(pt_local, distance, size);
// these are the pixel coordinates
PointF px=Pixel(pt_view, dlg.ClientSize.Width, dlg.ClientSize.Height);
e.Graphics.DrawRectangle(Pens.Blue, px.X, px.Y, 1f, 1f);
}
}
}

Related

Convert xy coordinates (in pixels) to barycentric coordinates (x,y,z)

This may be a strange one but I have a graphic of a triangle like this:
and in a survey platform, whenever one click on a point in this graphic an x,y coordinates in pixels are recorded like this (the origin for x,y is from the top-left of the image)
Note: the clicking is only allowed inside the green triangle.
Is there a way to convert these coordinates to barycentric coordinates (x,y,z) with respect to a simplex in 2D (this triangle)?
If so what is the appropriate equation given that we have the x,y in pixels.
Would it matter if they are in pixels or are they still considered Cartesian coordinates?
Thanks!
So you know the coordinates of the three vertex of the triangle
A: {ax, ay}
B: {bx, by}
C: {cx, cy}
and you want to take an arbitrary point
P: {px, py}
and find the three barycentric coordinates w_A, w_B, w_C such that
px = w_A * ax + w_B * bx + w_C * cx
py = w_A * ay + w_B * by + w_C * cy
1 = w_A + w_B + w_C
Turn this into a linear algebra problem
| px | | ax bx cx | | w_A |
| py | = | ay by cy | | w_B |
| 1 | | 1 1 1 | | w_C |
to be solved for {w_A,w_B,w_C}
Use the sample code below which I tested with the following results
Triangle A: <5, 0>
Triangle B: <2.5, 5>
Triangle C: <0, 0>
Random Point: <3.169941, 0.417091>
Barycentric: (0.5922791,0.08341819,0.3243028)
Target Point: <3.169941, 0.417091>
Triangle.cs
using System.Numerics;
namespace ConsoleApp2
{
public class Triangle
{
public Triangle(Vector2 a, Vector2 b, Vector2 c)
{
A = a;
B = b;
C = c;
}
public Vector2 A { get; set; }
public Vector2 B { get; set; }
public Vector2 C { get; set; }
public Vector2 GetPoint(float w_A, float w_B, float w_C) => GetPoint((w_A, w_B, w_C));
public Vector2 GetPoint((float w_A, float w_B, float w_C) coord)
{
return coord.w_A * A + coord.w_B * B + coord.w_C * C;
}
public (float w_A, float w_B, float w_C) GetBarycentric(Vector2 P)
{
float D = A.X * (B.Y - C.X) + A.Y * (B.X - C.X) + B.X * C.Y - B.Y * C.X;
float w_A = ((B.Y - C.Y) * P.X + (C.X - B.X) * P.Y + (B.X * C.Y - B.Y * C.X)) / D;
float w_B = ((C.Y - A.Y) * P.X + (A.X - C.X) * P.Y + (C.X * A.Y - C.Y * A.X)) / D;
float w_C = ((A.Y - B.Y) * P.X + (B.X - A.X) * P.Y + (A.X * B.Y - A.Y * B.X)) / D;
return (w_A, w_B, w_C);
}
public bool Contains(Vector2 point)
{
var (w_A, w_B, w_C) = GetBarycentric(point);
return w_A>=0 && w_A<=1
&& w_B>=0 && w_B<=1
&& w_C>=0 && w_C<=1;
}
}
}
Program.cs
using System;
using System.Numerics;
namespace ConsoleApp2
{
public static class Program
{
static readonly Random rng = new Random();
static Vector2 RandomVector(float minValue = 0, float maxValue = 1)
{
return new Vector2(
minValue + (maxValue - minValue) * (float)rng.NextDouble(),
minValue + (maxValue - minValue) * (float)rng.NextDouble());
}
static void Main(string[] args)
{
Vector2 A = new Vector2(5f,0f);
Vector2 B = new Vector2(2.5f,5f);
Vector2 C = new Vector2(0f,0f);
var triangle = new Triangle(A, B, C);
Console.WriteLine($"Triangle A: {A}");
Console.WriteLine($"Triangle B: {B}");
Console.WriteLine($"Triangle C: {C}");
Vector2 P = RandomVector(0f, 5f);
Console.WriteLine($"Random Point: {P}");
var (w_A, w_B, w_C) = triangle.GetBarycentric(P);
Console.WriteLine($"Barycentric: ({w_A},{w_B},{w_C})");
Vector2 T = triangle.GetPoint(w_A, w_B, w_C);
Console.WriteLine($"Target Point: {T}");
}
}
}
Pixels are just the specific unit, this triangle representation would still be in cartesian coordinates, so you just apply the same formula, i.e. (as specified in the comments )this

Find where line-segments intersect with a box

I am trying to figure out where a bunch of line-segments clip into a window around them. I saw the Liang–Barsky algorithm, but that seems to assume the segments already clip the edges of the window, which these do not.
Say I have a window from (0,0) to (26,16), and the following segments:
(7,6) - (16,3)
(10,6) - (19,6)
(13,10) - (21,3)
(16,12) - (19,14)
Illustration:
I imagine I need to extend the segments to a certain X or Y point, till they hit the edge of the window, but I don't know how.
How would I find the points where these segments (converted to lines?) clip into the edge of the window? I will be implementing this in C#, but this is pretty language-agnostic.
If you have two line segments P and Q with points
P0 - P1
Q0 - Q1
The line equations are
P = P0 + t(P1 - P0)
Q = Q0 + r(Q1 - Q0)
then to find out where they intersect after extension you need to solve the following equation for t and r
P0 + t(P1 - P0) = Q0 + r(Q1 - Q0)
The following code can do this. ( Extracted from my own code base )
public static (double t, double r )? SolveIntersect(this Segment2D P, Segment2D Q)
{
// a-d are the entries of a 2x2 matrix
var a = P.P1.X - P.P0.X;
var b = -Q.P1.X + Q.P0.X;
var c = P.P1.Y - P.P0.Y;
var d = -Q.P1.Y + Q.P0.Y;
var det = a*d - b*c;
if (Math.Abs( det ) < Utility.ZERO_TOLERANCE)
return null;
var x = Q.P0.X - P.P0.X;
var y = Q.P0.Y - P.P0.Y;
var t = 1/det*(d*x - b*y);
var r = 1/det*(-c*x + a*y);
return (t, r);
}
If null is returned from the function then it means the lines are parallel and cannot intersect. If a result is returned then you can do.
var result = SolveIntersect( P, Q );
if (result != null)
{
var ( t, r) = result.Value;
var p = P.P0 + t * (P.P1 - P.P0);
var q = Q.P0 + t * (Q.P1 - Q.P0);
// p and q are the same point of course
}
The extended line segments will generally intersect more than one box edge but only one of those intersections will be inside the box. You can check this easily.
bool IsInBox(Point corner0, Point corner1, Point test) =>
(test.X > corner0.X && test.X < corner1.X && test.Y > corner0.Y && test.Y < corner1.Y ;
That should give you all you need to extend you lines to the edge of your box.
I managed to figure this out.
I can extend my lines to the edge of the box by first finding the equations of my lines, then solving for the X and Y of each of the sides to get their corresponding point. This requires passing the max and min Y and the max and min X into the following functions, returning 4 values. If the point is outside the bounds of the box, it can be ignored.
My code is in C#, and is making extension methods for EMGU's LineSegment2D. This is a .NET wrapper for OpenCv.
My Code:
public static float GetYIntersection(this LineSegment2D line, float x)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if(dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return m * x + b;
}
public static float GetXIntersection(this LineSegment2D line, float y)
{
Point p1 = line.P1;
Point p2 = line.P2;
float dx = p2.X - p1.X;
if (dx == 0)
{
return float.NaN;
}
float m = (p2.Y - p1.Y) / dx; //Slope
float b = p1.Y - (m * p1.X); //Y-Intercept
return (y - b) / m;
}
I can then take these points, check if they are in the bounds of the box, discard the ones that are not, remove duplicate points (line goes directly into corner). This will leave me with one x and one y value, which I can then pair to the corresponding min or max Y or X values I passed into the functions to make 2 points. I can then make my new segment with the two points.
Wiki description of Liang-Barsky algorithm is not bad, but code is flaw.
Note: this algorithm intended to throw out lines without intersection as soon as possible. If most of lines intersect the rectangle, then approach from your answer might be rather effective, otherwise L-B algorithm wins.
This page describes approach in details and contains concise effective code:
// Liang-Barsky function by Daniel White # http://www.skytopia.com/project/articles/compsci/clipping.html
// This function inputs 8 numbers, and outputs 4 new numbers (plus a boolean value to say whether the clipped line is drawn at all).
//
bool LiangBarsky (double edgeLeft, double edgeRight, double edgeBottom, double edgeTop, // Define the x/y clipping values for the border.
double x0src, double y0src, double x1src, double y1src, // Define the start and end points of the line.
double &x0clip, double &y0clip, double &x1clip, double &y1clip) // The output values, so declare these outside.
{
double t0 = 0.0; double t1 = 1.0;
double xdelta = x1src-x0src;
double ydelta = y1src-y0src;
double p,q,r;
for(int edge=0; edge<4; edge++) { // Traverse through left, right, bottom, top edges.
if (edge==0) { p = -xdelta; q = -(edgeLeft-x0src); }
if (edge==1) { p = xdelta; q = (edgeRight-x0src); }
if (edge==2) { p = -ydelta; q = -(edgeBottom-y0src);}
if (edge==3) { p = ydelta; q = (edgeTop-y0src); }
if(p==0 && q<0) return false; // Don't draw line at all. (parallel line outside)
r = q/p;
if(p<0) {
if(r>t1) return false; // Don't draw line at all.
else if(r>t0) t0=r; // Line is clipped!
} else if(p>0) {
if(r<t0) return false; // Don't draw line at all.
else if(r<t1) t1=r; // Line is clipped!
}
}
x0clip = x0src + t0*xdelta;
y0clip = y0src + t0*ydelta;
x1clip = x0src + t1*xdelta;
y1clip = y0src + t1*ydelta;
return true; // (clipped) line is drawn
}

How to get a logarithmic distribution from an interval

I am currently trying to cut an interval into not equal-width slices. In fact I want the width of each slice to follow a logarithmic rule. For instance the first interval is supposed to be bigger than the second one, etc.
I have a hard time remembering my mathematics lectures. So assuming I know a and b which are respectively the lower and upper boundaries of my interval I, and n is the number of slices:
how can I find the lower and upper boundaries of each slice (following a logarithmic scale)?
In other word, here's what I have done to get equal-width interval:
for (i = 1; i< p; i++) {
start = lower + i -1 + ((i-1) * size_piece);
if (i == p-1 ) {
end = upper;
} else {
end = start + size_piece;
}
//function(start, end)
}
Where: p-1= number of slices, and size_piece = |b-a|.
What I want to get now is start and end values, but following a logarithmic scale instead of an arithmetic scale (which are going to be called in some function in the for loop).
Thanks in advance for your help.
If I have understood your question, this C++ program will show you a practical example of the algorithm that can be used:
#include <iostream>
#include <cmath>
void my_function( double a, double b ) {
// print out the lower and upper bounds of the slice
std::cout << a << " -- " << b << '\n';
}
int main() {
double start = 0.0, end = 1.0;
int n_slices = 7;
// I want to create 7 slices in a segment of length = end - start
// whose extremes are logarithmically distributed:
// | 1 | 2 | 3 | 4 | 5 |6 |7|
// +-----------------+----------+------+----+---+--+-+
// start end
double scale = (end - start) / log(1.0 + n_slices);
double lower_bound = start;
for ( int i = 0; i < n_slices; ++i ) {
// transform to the interval (1,n_slices+1):
// 1 2 3 4 5 6 7 8
// +-----------------+----------+------+----+---+--+-+
// start end
double upper_bound = start + log(2.0 + i) * scale;
// use the extremes in your function
my_function(lower_bound,upper_bound);
// update
lower_bound = upper_bound;
}
return 0;
}
The output (the extremes of the slices) is:
0 -- 0.333333
0.333333 -- 0.528321
0.528321 -- 0.666667
0.666667 -- 0.773976
0.773976 -- 0.861654
0.861654 -- 0.935785
0.935785 -- 1

Determine position of number in a grid of numbers centered around 0 and increasing in spiral

I've got the following grid of numbers centered around 0 and increasing in spiral. I need an algorithm which would receive number in spiral and return x; y - numbers of moves how to get to that number from 0. For example for number 9 it would return -2; -1. For 4 it would be 1; 1.
25|26|... etc.
24| 9|10|11|12
23| 8| 1| 2|13
22| 7| 0| 3|14
21| 6| 5| 4|15
20|19|18|17|16
This spiral can be slightly changed if it would help the algorithm to be better.
Use whatever language you like. I would really appreciate mathematical explanation.
Thank you.
First we need to determine which cycle (distance from center) and sector (north, east, south or west) we are in. Then we can determine the exact position of the number.
The first numbers in each cycle is as follows: 1, 9, 25
This is a quadratic sequence: first(n) = (2n-1)^2 = 4n^2 - 4n + 1
The inverse of this is the cycle-number: cycle(i) = floor((sqrt(i) + 1) / 2)
The length of a cycle is: length(n) = first(n+1) - first(n) = 8n
The sector will then be:
sector(i) = floor(4 * (i - first(cycle(i))) / length(cycle(i)))
Finally, to get the position, we need to extrapolate from the position of the first number in the cycle and sector.
To put it all together:
def first(cycle):
x = 2 * cycle - 1
return x * x
def cycle(index):
return (isqrt(index) + 1)//2
def length(cycle):
return 8 * cycle
def sector(index):
c = cycle(index)
offset = index - first(c)
n = length(c)
return 4 * offset / n
def position(index):
c = cycle(index)
s = sector(index)
offset = index - first(c) - s * length(c) // 4
if s == 0: #north
return -c, -c + offset + 1
if s == 1: #east
return -c + offset + 1, c
if s == 2: #south
return c, c - offset - 1
# else, west
return c - offset - 1, -c
def isqrt(x):
"""Calculates the integer square root of a number"""
if x < 0:
raise ValueError('square root not defined for negative numbers')
n = int(x)
if n == 0:
return 0
a, b = divmod(n.bit_length(), 2)
x = 2**(a+b)
while True:
y = (x + n//x)//2
if y >= x:
return x
x = y
Example:
>>> position(9)
(-2, -1)
>>> position(4)
(1, 1)
>>> position(123456)
(-176, 80)
Do you mean something like this? I did not implement any algorithm and the code can be written better but it works - that's always a start :) Just change the threshold value for whatever you wish and you'll get the result.
static int threshold=14, x=0, y=0;
public static void main(String[] args) {
int yChange=1, xChange=1, count=0;
while( !end(count) ){
for (int i = 0; i < yChange; i++) {
if( end(count) )return;
count++;
y--;
}
yChange++;
for (int i = 0; i < xChange; i++) {
if( end(count) )return;
count++;
x++;
}
xChange++;
for (int i = 0; i < yChange; i++) {
if( end(count) )return;
count++;
y++;
}
yChange++;
for (int i = 0; i < xChange; i++) {
if( end(count) )return;
count++;
x--;
}
xChange++;
}
}
public static boolean end(int count){
if(count<threshold){
return false;
}else{
System.out.println("count: "+count+", x: "+x+", y: "+y);
return true;
}
}

Interpolating values between interval, interpolation as per Bezier curve

To implement a 2D animation I am looking for interpolating values between two key frames with the velocity of change defined by a Bezier curve. The problem is Bezier curve is represented in parametric form whereas requirement is to be able to evaluate the value for a particular time.
To elaborate, lets say the value of 10 and 40 is to be interpolated across 4 seconds with the value changing not constantly but as defined by a bezier curve represented as 0,0 0.2,0.3 0.5,0.5 1,1.
Now if I am drawing at 24 frames per second, I need to evaluate the value for every frame. How can I do this ? I looked at De Casteljau algorithm and thought that dividing the curve into 24*4 pieces for 4 seconds would solve my problem but that sounds erroneous as time is along the "x" axis and not along the curve.
To further simplify
If I draw the curve in a plane, the x axis represents the time and the y axis the value I am looking for. What I actually require is to to be able to find out "y" corresponding to "x". Then I can divide x in 24 divisions and know the value for each frame
I was facing the same problem: Every animation package out there seems to use Bézier curves to control values over time, but there is no information out there on how to implement a Bézier curve as a y(x) function. So here is what I came up with.
A standard cubic Bézier curve in 2D space can be defined by the four points P0=(x0, y0) .. P3=(x3, y3).
P0 and P3 are the end points of the curve, while P1 and P2 are the handles affecting its shape. Using a parameter t ϵ [0, 1], the x and y coordinates for any given point along the curve can then be determined using the equations
A) x = (1-t)3x0 + 3t(1-t)2x1 + 3t2(1-t)x2 + t3x3 and
B) y = (1-t)3y0 + 3t(1-t)2y1 + 3t2(1-t)y2 + t3y3.
What we want is a function y(x) that, given an x coordinate, will return the corresponding y coordinate of the curve. For this to work, the curve must move monotonically from left to right, so that it doesn't occupy the same x coordinate more than once on different y positions. The easiest way to ensure this is to restrict the input points so that x0 < x3 and x1, x2 ϵ [x0, x3]. In other words, P0 must be to the left of P3 with the two handles between them.
In order to calculate y for a given x, we must first determine t from x. Getting y from t is then a simple matter of applying t to equation B.
I see two ways of determining t for a given y.
First, you might try a binary search for t. Start with a lower bound of 0 and an upper bound of 1 and calculate x for these values for t via equation A. Keep bisecting the interval until you get a reasonably close approximation. While this should work fine, it will neither be particularly fast nor very precise (at least not both at once).
The second approach is to actually solve equation A for t. That's a bit tough to implement because the equation is cubic. On the other hand, calculation becomes really fast and yields precise results.
Equation A can be rewritten as
(-x0+3x1-3x2+x3)t3 + (3x0-6x1+3x2)t2 + (-3x0+3x1)t + (x0-x) = 0.
Inserting your actual values for x0..x3, we get a cubic equation of the form at3 + bt2 + c*t + d = 0 for which we know there is only one solution within [0, 1]. We can now solve this equation using an algorithm like the one posted in this Stack Overflow answer.
The following is a little C# class demonstrating this approach. It should be simple enough to convert it to a language of your choice.
using System;
public class Point {
public Point(double x, double y) {
X = x;
Y = y;
}
public double X { get; private set; }
public double Y { get; private set; }
}
public class BezierCurve {
public BezierCurve(Point p0, Point p1, Point p2, Point p3) {
P0 = p0;
P1 = p1;
P2 = p2;
P3 = p3;
}
public Point P0 { get; private set; }
public Point P1 { get; private set; }
public Point P2 { get; private set; }
public Point P3 { get; private set; }
public double? GetY(double x) {
// Determine t
double t;
if (x == P0.X) {
// Handle corner cases explicitly to prevent rounding errors
t = 0;
} else if (x == P3.X) {
t = 1;
} else {
// Calculate t
double a = -P0.X + 3 * P1.X - 3 * P2.X + P3.X;
double b = 3 * P0.X - 6 * P1.X + 3 * P2.X;
double c = -3 * P0.X + 3 * P1.X;
double d = P0.X - x;
double? tTemp = SolveCubic(a, b, c, d);
if (tTemp == null) return null;
t = tTemp.Value;
}
// Calculate y from t
return Cubed(1 - t) * P0.Y
+ 3 * t * Squared(1 - t) * P1.Y
+ 3 * Squared(t) * (1 - t) * P2.Y
+ Cubed(t) * P3.Y;
}
// Solves the equation ax³+bx²+cx+d = 0 for x ϵ ℝ
// and returns the first result in [0, 1] or null.
private static double? SolveCubic(double a, double b, double c, double d) {
if (a == 0) return SolveQuadratic(b, c, d);
if (d == 0) return 0;
b /= a;
c /= a;
d /= a;
double q = (3.0 * c - Squared(b)) / 9.0;
double r = (-27.0 * d + b * (9.0 * c - 2.0 * Squared(b))) / 54.0;
double disc = Cubed(q) + Squared(r);
double term1 = b / 3.0;
if (disc > 0) {
double s = r + Math.Sqrt(disc);
s = (s < 0) ? -CubicRoot(-s) : CubicRoot(s);
double t = r - Math.Sqrt(disc);
t = (t < 0) ? -CubicRoot(-t) : CubicRoot(t);
double result = -term1 + s + t;
if (result >= 0 && result <= 1) return result;
} else if (disc == 0) {
double r13 = (r < 0) ? -CubicRoot(-r) : CubicRoot(r);
double result = -term1 + 2.0 * r13;
if (result >= 0 && result <= 1) return result;
result = -(r13 + term1);
if (result >= 0 && result <= 1) return result;
} else {
q = -q;
double dum1 = q * q * q;
dum1 = Math.Acos(r / Math.Sqrt(dum1));
double r13 = 2.0 * Math.Sqrt(q);
double result = -term1 + r13 * Math.Cos(dum1 / 3.0);
if (result >= 0 && result <= 1) return result;
result = -term1 + r13 * Math.Cos((dum1 + 2.0 * Math.PI) / 3.0);
if (result >= 0 && result <= 1) return result;
result = -term1 + r13 * Math.Cos((dum1 + 4.0 * Math.PI) / 3.0);
if (result >= 0 && result <= 1) return result;
}
return null;
}
// Solves the equation ax² + bx + c = 0 for x ϵ ℝ
// and returns the first result in [0, 1] or null.
private static double? SolveQuadratic(double a, double b, double c) {
double result = (-b + Math.Sqrt(Squared(b) - 4 * a * c)) / (2 * a);
if (result >= 0 && result <= 1) return result;
result = (-b - Math.Sqrt(Squared(b) - 4 * a * c)) / (2 * a);
if (result >= 0 && result <= 1) return result;
return null;
}
private static double Squared(double f) { return f * f; }
private static double Cubed(double f) { return f * f * f; }
private static double CubicRoot(double f) { return Math.Pow(f, 1.0 / 3.0); }
}
You have a few options:
Let's say your curve function F(t) takes a parameter t that ranges from 0 to 1 where F(0) is the beginning of the curve and F(1) is the end of the curve.
You could animate motion along the curve by incrementing t at a constant change per unit of time.
So t is defined by function T(time) = Constant*time
For example, if your frame is 1/24th of a second, and you want to move along the curve at a rate of 0.1 units of t per second, then each frame you increment t by 0.1 (t/s) * 1/24 (sec/frame).
A drawback here is that your actual speed or distance traveled per unit time will not be constant. It will depends on the positions of your control points.
If you want to scale speed along the curve uniformly you can modify the constant change in t per unit time. However, if you want speeds to vary dramatically you will find it difficult to control the shape of the curve. If you want the velocity at one endpoint to be much larger, you must move the control point further away, which in turn pulls the shape of the curve towards that point. If this is a problem, you may consider using a non constant function for t. There are a variety of approaches with different trade-offs, and we need to know more details about your problem to suggest a solution. For example, in the past I have allowed users to define the speed at each keyframe and used a lookup table to translate from time to parameter t such that there is a linear change in speed between keyframe speeds (it's complicated).
Another common hangup: If you are animating by connecting several Bezier curves, and you want the velocity to be continuous when moving between curves, then you will need to constrain your control points so they are symmetrical with the adjacent curve. Catmull-Rom splines are a common approach.
I've answered a similar question here. Basically if you know the control points before hand then you can transform the f(t) function into a y(x) function. To not have to do it all by hand you can use services like Wolfram Alpha to help you with the math.

Resources