Bezier Curve Arc-Length Parameterization - c#

I'm learning about Bezier curves and would like to parameterize the equations for distance using an estimation method. So far, my code seems to work for single points (EG Bezier(start=0, mid=1, end=5, nPoints=6) yields [0 1 2 3 4 5]). However, when I attempt to apply this to multi-dimensional curves, my results are not as expected.
C# code (executed in Unity for visualization). The function (should) get a point on the curve (defined by the points pts) at a length l% of the length.
Vector3 BezierL(Vector3[] pts, float l)
{
int i;
float[] tVals = new float[n];
Vector3[] points = new Vector3[n];
float[] cumDist = new float[n];
for (i = 1; i < n; i++)
{
tVals[i] = i / (float)(n - 1);
points[i] = Bezier(pts, tVals[i]);
cumDist[i] = cumDist[i - 1] +
(points[i] - points[i - 1]).magnitude;
}
// Interpolate to estimate t
float targetLen = l * cumDist[n - 1];
int ind = Array.BinarySearch(cumDist, targetLen);
if (ind < 0)
ind = ~ind;
float t = Mathf.Lerp(tVals[ind - 1], tVals[ind],
(targetLen - cumDist[ind - 1]) / (cumDist[ind] - cumDist[ind - 1]));
return Bezier(pts, t);
}
where Bezier(Vector3[] pts, t) gets a point on the curve defined by pts at time t. For whatever reason, this partially works in that all points are equally spaced, but some points are stacked at the initial point rather than being distributed along the curve.
This was my reference for developing this algorithm, so I'm unsure if my implementation is incorrect, or if it only applies to lower-dimensional curves.
Thanks in advance!

Oof how embarrassing, I just forgot to compute the 0th point!

Related

Implementing Cubic Spline Interpolation (PCHIP or MAKIMA) for 3D points in C#

I wrote an application in C# which controls a camera through a 3D environment.
At the moment the camera follows a path which is defined as an array of points, where each point is defined as: x, y, z & rotation for each axis (everything is a float)
The camera position between those points are computed by linear interpolation every frame.
Because this leads to very clunky movement, I want to implement cubic spline interpolation instead.
There are many different algorithms, but so far I set my eyes on the following, since those feel "more correct", which means they don't overshoot:
Piecewise Cubic Hermite Interpolating Polynomial (PCHIP)
Modified Akima piecewise cubic Hermite interpolation (MAKIMA)
My biggest problem is, that I am absolutely not a mathematician and I can't read and understand the gibberish found online for their definitions, so I need help translating it into code.
Constraints for the implementation
Algorithm should use either PCHIP or MAKIMA (I am open for other "correct" approaches though)
Interpolated values must be a function of time (t from 0 to {points.Length})
Must run for 3D points, not only 2D
Should not contain any external libraries if possible
Libraries
I have checked the following, but they either don't support 3D points, or interpolation by time (and aren't intuitive to use either... not even talking about the license here):
Math.NET Numerics
ALGLIB
Here is a small code stump which I came up with to demonstrate what I am looking for:
static float[,] points = new float[,] {
{10, 10, 10},
{35, 5, 25},
{15, 30, 15}
};
static int numberOfPoints = points.Length / 3; // dividing for each stored float value per point: only x, y, z for simplification
static void Main()
{
List<float[]> pointsWithInterpolation = new(); // holding both known and interpolated points
float stepSize = 0.1f; // step size for t
for (int i = 0; i < numberOfPoints - 1; i++)
{ // loop for each spline segment
pointsWithInterpolation.Add(new float[] { points[i, 0], points[i, 1], points[i, 2] }); // add an allready known point to the list
for (float t = stepSize; t < 1f; t += stepSize)
{ // should run (1 / {stepSize} - 2) times
float[] interpolatedPoint = Interpolate(i + t);
pointsWithInterpolation.Add(interpolatedPoint); // add an interpolated point to the list
}
if (i == numberOfPoints - 1)
{ // add the very last point if we have finished the last segment
pointsWithInterpolation.Add(new float[] { points[i + 1, 0], points[i + 1, 1], points[i + 1, 2] }); // add an allready known point to the list
}
}
}
private static float[] Interpolate(float t)
{
// point indexes
int p1 = (int)t;
int p2 = p1 + 1;
int p0 = p1 == 0 ? 0 : p1 - 1; // if there are no previous point, set it to the first one
int p3 = p2 == numberOfPoints - 1 ? p2 : p2 + 1; // if there are no following points, set it to the last one
float x0 = points[p0, 0];
float x1 = points[p1, 0];
float x2 = points[p2, 0];
float x3 = points[p3, 0];
float y0 = points[p0, 1];
float y1 = points[p1, 1];
float y2 = points[p2, 1];
float y3 = points[p3, 1];
float z0 = points[p0, 2];
float z1 = points[p1, 2];
float z2 = points[p2, 2];
float z3 = points[p3, 2];
float x, y, z;
/* black magic hocus pocus happening here
x = ???;
y = ???;
z = ???;
*/
float[] point = new float[] { x, y, z };
return point;
}
So the question is: what is the formula for solving for x/y/z (the formula should be the same for each variable, but they use different variables).
Yes I know, this results in a constant number of interpolated points between the "real" ones, even if the distance of segments widely varies - I will tackle the issue of constant speed separately.
If I am not mistaken, every interpolation between p1 and p2 needs 4 values, so I set p0 equal to p1 for the very first segment and p3 equal to p2 for the last one to make up for non-existant points.
I haven't performance optimized the code, so I (hopefully) make it more clear/easy to understand what I try to do.
Of course I would also be thankful for code in other languages, as long as it fulfils the implementation constraints and doesn't use functions which aren't available in C# (and also no assembler please lmao)

Calculate a curve that goes through all the points

I having trouble building a curve that goes through all the points but not outside of those points as the bezier curve does in SVG.
I have tried
Bezier Curve,
Quadratic Curve,
Smooth Curve,
and Casteljau
Here is a link to my example https://dotnetfiddle.net/KEqts0
Unfortunately, I can use a 3rd party to do the mapping.
I do not want to place the output in because that will just be noise, I have included a picture for reference.
Observation: The initial question was tagged javascript. After posting my answer the javascript tag was deleted (not by the OP).
Given a points array pointsRy you will need to calculate the position of the control points for the Bézier curves. The first & the last curve are quadratic Bezier. All the other curves are cubic Bézier.
This is an image where I'm marking the points and the tangent to the curve through each point. The control points are the starting and ending points of the tangent.
The size of the tangent is calculated relative to the distance between the points: let t = 1 / 5; Change this to change the curvature.
let svg = document.querySelector("svg")
let t = 1 / 5;// change this to change the curvature
let pointsRy = [[100,100],[250,150],[300,300],[450,250], [510,140],[590,250],[670,140]];
thePath.setAttribute("d", drawCurve(pointsRy));
function drawCurve(p) {
var pc = controlPoints(pointsRy); // the control points array
let d="";
d += `M${p[0][0]}, ${p[0][1]}`
// the first & the last curve are quadratic Bezier
// because I'm using push(), pc[i][1] comes before pc[i][0]
d += `Q${pc[1][1].x}, ${pc[1][1].y}, ${p[1][0]}, ${p[1][1]}`;
if (p.length > 2) {
// central curves are cubic Bezier
for (var i = 1; i < p.length - 2; i++) {
d+= `C${pc[i][0].x}, ${pc[i][0].y} ${pc[i + 1][1].x},${pc[i + 1][1].y} ${p[i + 1][0]},${p[i + 1][1]}`;
}//end for
// the first & the last curve are quadratic Bezier
let n = p.length - 1;
d+=`Q${pc[n - 1][0].x}, ${pc[n - 1][0].y} ${p[n][0]},${p[n][1]}`;
}
return d;
}
function controlPoints(p) {
// given the points array p calculate the control points
let pc = [];
for (var i = 1; i < p.length - 1; i++) {
let dx = p[i - 1][0] - p[i + 1][0]; // difference x
let dy = p[i - 1][1] - p[i + 1][1]; // difference y
// the first control point
let x1 = p[i][0] - dx * t;
let y1 = p[i][1] - dy * t;
let o1 = {
x: x1,
y: y1
};
// the second control point
var x2 = p[i][0] + dx * t;
var y2 = p[i][1] + dy * t;
var o2 = {
x: x2,
y: y2
};
// building the control points array
pc[i] = [];
pc[i].push(o1);
pc[i].push(o2);
}
return pc;
}
body{background:black; margin:1em;}
svg{border: 1px solid #999;}
path{fill:none; stroke:white;}
<svg viewBox="0 0 800 400">
<path id="thePath" stroke="white"/>
</svg>

Diagonal sweep and orthogonal distance of a point to diagonal

I have a problem where I'm required to find the maximum number of points that are less than or equal to a given distance D to a line drawn in a two-dimensional Euclidean plane. To solve this I wrote the algorithms that would compute a possible maximum if the line was orthogonal to either the x-axis or the y-axis. My problem is when only a diagonal line would yield the maximum number of points.
Given the constraints that both x and y have a minimum value of -1000000 and maximum of 1000000. I wrote the following algorithm to try and find out the maximum. I don't seem to be getting the right answer. Could someone please guide me on where I am going wrong. I've tried drawing a regression line as well but that used vertical distance which did not work for my purposes. Maybe I'm going all wrong and this problem can be solved as an optimization problem. Anyways' any help with a descent explanation is much appreciated.
// diagonal sweep
for (int degree = 1; degree < 180; degree++) if (degree % 90 != 0)
{
int k = 1, degrees = degree;
double x1 = -1000000, x2 = 1000000;
if (degree > 90 && degree < 180)
{
degrees = 180 - degrees;
k = -1;
}
//slope
double m1 = Math.Tan(Math.PI * degrees * k / 180.0);
//Point A
Point A = new Point(x1, m1 * x1);
//Point B
Point B = new Point(x2, m1 * x2);
for (int i = 0; i < x.Length; i++)
{
//Point P = household that needs power
Point P = new Point(x[i], y[i]);
double normalLength = Math.Sqrt((B.X - A.X) * (B.X - A.X) + (B.Y - A.Y) * (B.Y - A.Y));
double segmentLength = 1d * Math.Abs((P.X - A.X) * (B.Y - A.Y) - (P.Y - A.Y) * (B.X - A.X)) / normalLength;
if (segmentLength <= D)
tempCnt++;
}
maxConnections = Math.Max(maxConnections, tempCnt);
tempCnt = 0;
}
return maxConnections;
If you want to define this problem as an optimization problem, you should do it as follows, but it doesn't seem to me this optimization problem is solveable efficiently as is.
maximize: x_1 + x_2 + ... + x_n + 0*a + 0*b + 0*c
s.t.
x_i * d(p_i, line(a,b,c))/ MAX_DISTANCE <= 1
x_i is in {0,1}
Explanation:
x_i are inclusion variables - can get a value of 0 / 1 , and it indicates if the point p_i is in the required distance from the line.
a,b,c are the parameters for the line: ax + by + c = 0
The idea is to maximize the sum of included points, such that each included point is in the desired range. This is represented by the constraint, if x_i=0 - there is no restriction on the point p_i, as the constraint is always satisfied. Otherwise, x_i=1, and you need the distance from the line (let it be d) satisfy 1* d/MAX_DISTANCE <= 1 - which is exactly what you want.
Though I don't think there is an optimal efficient solution to this optimization problem, you might want to try some heuristical solutions for this optiization - such as Genetic Algorithms or Hill Climbing
As a side note, my gut says this problem is NP-Complete, though I have no proof for it yet - and will update this part of the answer if I (or someone else) can come up with a reduction/polynomial solution.

Run Dijkstra's Algorithm on a List<Point> in C#

I have a list of Point types in C#. I want to run Dijkstra's algorithm on this list of points where the first entry in the list is the starting location.
Is there a way of doing this using an existing library?
If such library doesn't exist, is there a way of calculating the distance between two points with x and y coordinates. For example, calculate the distance between point A (x coordinate =2, y coordinate = 4) and point B ((x coordinate =9, y coordinate = 7).
I have used the ZedGraph library to build the graph.
I think you misunderstood, what the Dijkstra algorithm stands for.
For a given source vertex (node) in the graph, the algorithm finds the path with lowest cost (i.e. the shortest path) between that vertex and every other vertex.
What you need (i think) the lowest distance between two points based on their coordinates.
And the distance between two points can be counted using basic math:
Point p = new Point(4, 5);
Point r = new Point(10, 2);
double distance = Math.Sqrt(Math.Pow(p.X - r.X, 2) + Math.Pow(p.Y - r.Y, 2));
using this knowledge the problem could be solved with two functions like this:
Returns the distance between p and r:
static double distance(Point p, Point r)
{
return Math.Sqrt(Math.Pow(p.X - r.X, 2) + Math.Pow(p.Y - r.Y, 2));
}
Returns the index of the closest point to the fromIndex th element of the points list:
static int closestPoint(List<Point> points, int fromIndex)
{
Point start = points[fromIndex];
int resultIndex = 0;
for (int i = 1; i < points.Count; i++)
{
if (fromIndex == i)
continue;
Point current = points[i];
if (distance(start, current) < distance(start, points[resultIndex]))
resultIndex = i;
}
return resultIndex;
}
I'm really sorry, if i misunderstood you!

How to hit test shapes in Silverlight?

I need the ability to determine which Shape a given point falls within. There will be overlapped shapes and I need to find the Shape with the smallest area. For example, given the Shapes and points illustrated in the image below the following would be true:
Point 3 - collides with star
Point 2 - collides with diamond
Point 1 - collides with circle
Given this, I would like to know if there is a built in way to do what is needed.
If you are drawing these shapes manually, you could do a second drawing pass into a separate buffer, and instead of drawing the shape, you write an ID into the buffer if the pixel is within the shape. Then your hit test just has to index into that buffer and retrieve the ID. You would get to re-use your drawing code completely, and it scales much better when you have more shapes, vertices, and hits to test.
I've arrived at a solution that meets the requirements, still interested in hearing if there is a better way of doing this. My approach is as follows: do a hit-test by bounding box, then a geometric hit test based on the type of geometry.
For Polygons, I've adapted the C code mentioned http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes /pnpoly.html to work in C#.
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
{
int i, j, c = 0;
for (i = 0, j = nvert-1; i < nvert; j = i++) {
if ( ((verty[i]>testy) != (verty[j]>testy)) &&
(testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
c = !c;
}
return c;
}
For Ellipses, I've adaptated this code: http://msdn.microsoft.com/en-us/library/aa231172%28v=vs.60%29.aspx
BOOL CCircCtrl::InCircle(CPoint& point)
{
CRect rc;
GetClientRect(rc);
GetDrawRect(&rc);
// Determine radii
double a = (rc.right - rc.left) / 2;
double b = (rc.bottom - rc.top) / 2;
// Determine x, y
double x = point.x - (rc.left + rc.right) / 2;
double y = point.y - (rc.top + rc.bottom) / 2;
// Apply ellipse formula
return ((x * x) / (a * a) + (y * y) / (b * b) <= 1);
}

Categories

Resources