Квадратичная кривая Безье: вычисление точек - PullRequest
53 голосов
/ 12 апреля 2011

Я бы хотел вычислить точку на квадратичной кривой.Чтобы использовать его с элементом canvas HTML5.

Когда я использую в JavaScript функцию quadraticCurveTo(), у меня есть исходная точка, целевая точка иконтрольная точка.

Как вычислить точку на созданной квадратичной кривой, скажем, t=0.5, когда "только" знают эти три точки?

Ответы [ 5 ]

111 голосов
/ 12 апреля 2011

Используйте квадратную формулу Безье, найденную, например, на странице Википедии для Кривые Безье :

quadratic Bezier formula

В псевдокоде это

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] - начальная точка, p[1] - контрольная точка, а p[2] - конечная точка.t - это параметр, который изменяется от 0 до 1.

30 голосов
/ 29 сентября 2015

В случае, если кому-то нужна кубическая форма:

        //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;
7 голосов
/ 30 декабря 2016

Я создал эту демонстрацию:

// 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 

Полный пример здесь

может кому-то помочь.

2 голосов
/ 03 марта 2019

Я отредактировал ответ talkhabis (кубическая кривая), чтобы кривая отображалась с правильными координатами. (Не могу комментировать) Координаты Y нужно было изменить (-p []. Y + 150). (Новая переменная для этого может быть более хорошим и эффективным решением, но вы поймете, что идея)

// 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/

Я также создал С-код для проверки результатов кубической кривой. Просто введите координаты X и Y в основной функции.

#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

1 голос
/ 08 августа 2016

Просто примечание: если вы используете обычные формулы, представленные здесь, не ожидайте, что t = 0.5 вернет точку на половине длины кривой. В большинстве случаев это не так.

Подробнее об этом здесь ниже "§23 - Трассировка кривой с фиксированными интервалами расстояния" и здесь .

...