Quadratic Bézier Curve: Calculate Points
Asked Answered
M

5

64

I'd like to calculate a point on a quadratic curve. To use it with the canvas element of HTML5.

When I use the quadraticCurveTo() function in JavaScript, I have a source point, a target point and a control point.

How can I calculate a point on the created quadratic curve at let's say t=0.5 with "only" knowing this three points?

Matrix answered 12/4, 2011 at 11:31 Comment(0)
F
133

Use the quadratic Bézier formula, found, for instance, on the Wikipedia page for Bézier Curves:

quadratic Bezier formula

In pseudo-code, that's

t = 0.5; // given example value
x = (1 - t) * (1 - t) * p[0].x + 2 * (1 - t) * t * p[1].x + t * t * p[2].x;
y = (1 - t) * (1 - t) * p[0].y + 2 * (1 - t) * t * p[1].y + t * t * p[2].y;

p[0] is the start point, p[1] is the control point, and p[2] is the end point. t is the parameter, which goes from 0 to 1.

Fatherinlaw answered 12/4, 2011 at 11:36 Comment(6)
Multiplying (adding) points in this case, means that you multiply (add) each component. That is, 3 P = [3 * P.x, 3 * p.y] and P1 + P2 = [P1.x + P2.x, P1.y + P2.y]. Finally, to square something, you multiply it with itself: x² = x * x. The last part, "t ∈ [1,0]", means that t is supposed to be between 0 and 1.Convalescent
So, this means: Point.x = (1-t)^2 * P0.x + 2 * (1-t) * t * P1.x + t^2 * P2.x; Point.y = (1-t)^2 * P0.y + 2 * (1-t) * t * P1.y + t^2 * P2.y; Tested and it works! =) Thank you!Matrix
@xan: IMO you should have answered with some code (or pseudo-code) rather than mathematical notation since this is a programming question.Marzipan
what is t? What is p0, p1 and p2?Clarey
@openfrog, t gives a fraction of where the point will be with respect to the starting and end point. It's the percentage of where it is assuming that the start and end point total 1, so t is usually a fraction. p0 is your starting point. p1 is your control/anchor point. p2 is your end point.Jana
@Jana that was the crucial missing part in the answer, thanks.Brandibrandice
H
37

In case somebody needs the cubic form:

        //B(t) = (1-t)**3 p0 + 3(1 - t)**2 t P1 + 3(1-t)t**2 P2 + t**3 P3

        x = (1-t)*(1-t)*(1-t)*p0x + 3*(1-t)*(1-t)*t*p1x + 3*(1-t)*t*t*p2x + t*t*t*p3x;
        y = (1-t)*(1-t)*(1-t)*p0y + 3*(1-t)*(1-t)*t*p1y + 3*(1-t)*t*t*p2y + t*t*t*p3y;
Heliport answered 29/9, 2015 at 10:37 Comment(0)
C
10

I created this demo :

// x = a * (1-t)³ + b * 3 * (1-t)²t + c * 3 * (1-t)t² + d * t³
//------------------------------------------------------------
// x = a - 3at + 3at² - at³ 
//       + 3bt - 6bt² + 3bt³
//             + 3ct² - 3ct³
//                    + dt³
//--------------------------------
// x = - at³  + 3bt³ - 3ct³ + dt³
//     + 3at² - 6bt² + 3ct²
//     - 3at + 3bt
//     + a
//--------------------------------
// 0 = t³ (-a+3b-3c+d) +  => A
//     t² (3a-6b+3c)   +  => B
//     t  (-3a+3b)     +  => c
//     a - x              => D
//--------------------------------

var A = d - 3*c + 3*b - a,
    B = 3*c - 6*b + 3*a,
    C = 3*b - 3*a,
    D = a-x;

// So we need to solve At³ + Bt² + Ct + D = 0 

Full example here

may help someone.

Catechu answered 30/12, 2016 at 18:52 Comment(3)
Your JSFiddle example doesn't actually show y for x. But I tried it anyway. And it worked 🎉 Converted to swift: gist.github.com/eonist/f5bb11533ee52ce24bad3ee47044239a THX!Ligni
@GitSyncApp its because the cubic function. it returns 3 answers that I used just first answer. see 1728.org/cubic.htmCatechu
Yeh, I know. But that was what I needed. Finding y for x on a cubic bezier graph. My point was that your fiddle is sort of scaled in the x-axis. Could be a browser thing ¯_(ツ)_/¯ It's awesome non the less. Kudos!Ligni
S
2

I edited talkhabis answer (cubic curve) so the curve is displayed with the right coordinates. (Couldn't comment) The Y-coordinates needed to be changed (-p[].y+150). (A new variable for that might be a nicer and more efficient solution, but you get the idea)

// Apply points to SVG and create the curve and controllers :

var path  =  document.getElementById('path'),
    ctrl1 =  document.getElementById('ctrl1'),
    ctrl2 =  document.getElementById('ctrl2'),
    D = 'M ' + p0.x + ' ' + (-p0.y+150) +
    'C ' + c0.x + ' ' + (-c0.y+150) +', ' + c1.x + ' ' + (-c1.y+150) + ', ' + p1.x + ' ' + (-p1.y+150);

path.setAttribute('d',D);
ctrl1.setAttribute('d','M'+p0.x+','+(-p0.y+150)+'L'+c0.x+','+(-c0.y+150));
ctrl2.setAttribute('d','M'+p1.x+','+(-p1.y+150)+'L'+c1.x+','+(-c1.y+150));

// Lets test the "Bezier Function" 

var t = 0, point = document.getElementById('point');

setInterval(function(){

  var p = Bezier(p0,c0,c1,p1,t);
  point.setAttribute('cx',p.x);
  point.setAttribute('cy',-p.y+150);

  t += 0.01;
  if(t>=1) t=0;

},50);


// OK ... Now tring to get "y" on cruve based on mouse "x" : 

var svg = document.getElementById('svg'),
    point2 = document.getElementById('point2');

svg.onmousemove = function(e){

    var x = (e.pageX - 50)/2,  
        y = (e.pageY - 50)/2;
   // "-50" because of "50px margin" on the left side 
   // and "/2" because the svg width is 300 units and 600 px => 300 = 600/2    

  // Get the x,y by mouse x
  var p = YBX(p0,c0,c1,p1,x); 

  point2.setAttribute('cx',p.x);
  point2.setAttribute('cy',-p.y+150);  
} 

http://jsfiddle.net/u214gco8/1/

I also created some C-Code to test the results for the cubic curve. Just enter the X and Y coordinates in the main function.

#include <stdio.h>
#include <stdlib.h> 
#include <math.h> 

void bezierCurve(int x[] , int y[]) 
{ 
    double xu = 0.0 , yu = 0.0 , u = 0.0 ; 
    int i = 0 ; 
    for(u = 0.0 ; u <= 1.0 ; u += 0.05) 
    { 
        xu = pow(1-u,3)*x[0]+3*u*pow(1-u,2)*x[1]+3*pow(u,2)*(1-u)*x[2] 
             +pow(u,3)*x[3]; 
        yu = pow(1-u,3)*y[0]+3*u*pow(1-u,2)*y[1]+3*pow(u,2)*(1-u)*y[2] 
            +pow(u,3)*y[3]; 
        printf("X: %i   Y: %i \n" , (int)xu , (int)yu) ; 
    } 
} 

int main(void) {
    int x[] = {0,75,50,300};
    int y[] = {0,2,140,100};
    bezierCurve(x,y);
    return 0;
}

https://ideone.com/glLXcB

Subset answered 3/3, 2019 at 20:40 Comment(1)
Where is the reasoning behind adjusting the Y with 150? Is this a "fixed" adjustment, or does it vary somehow in different sizes of the curve/container?Adamant
L
1

Just a note: If you are using the usual formulas presented here then don't expect t = 0.5 to return the point at half of the curve's length.. In most cases it won't.

More on this here under "§23 — Tracing a curve at fixed distance intervals" and here.

Lil answered 8/8, 2016 at 15:56 Comment(2)
The curve's length is really hard to measure anyway. At t=0.5 you will on average if you assume random control points be at the center. But, do note that it has the same issue as most curves of varying speed. Finding the halfway point generally would requiring measuring parts of the curve and finding the center bit with a binary search. It's not exactly super typically needed. But, it is worthwhile to understand that if you find all points points at t=.1 increments they will not be equal in length. -- Though this has little to do with question and plenty to do with the nature of the curves.Heliport
@Tatarize: Mostly true, as also explained in the links provided. A very typical scenario would be a camera or a mesh movement along a path with constant speed... one will probably end up using polylines calculated from curves and using binary search...Lil

© 2022 - 2024 — McMap. All rights reserved.