Control de curva Bezier con manija de control: historia, principio, implementación

© 2013-2023 Conmajia
Actualizado el 9 de marzo de 2023
Iniciado el 22 de febrero de 2015

Resumen Este artículo presenta la historia y los principios básicos de las curvas de Bezier, y toma .NET Frameworks GDI+ como ejemplo para implementar de forma sencilla un control de curva con tiradores de control y estilos extensibles.

1. Introducción

Las curvas con manijas de control se utilizan a menudo en el software de dibujo para ajustar con precisión los valores que cambian continuamente. Las curvas de Bezier se derivan de una familia de ecuaciones de parámetros de curva ampliamente utilizadas en CAD de ingeniería y gráficos por computadora. Pierre Étienne Bézier (Pierre Étienne Bézier, 1910-1999) propuso en 1962, y desarrolló el sistema de modelado UNISURF basado en él (1968).Para obtener detalles sobre las funciones de Bessel, consulte el libro de Bessel My "The Mathematical Foundations of the UNISURF CAD System" (Butterworths, 1986, p. 56).

1.1 Función de Bessel

Las funciones de Bessel son una familia de la forma

x 2 y ′ ′ + xy ′ + ( x 2 − norte 2 ) y = 0 x^2y''+xy'+\left(x^2-n^2\right)y=0X2 años"+x y+( X2norte2 )y=0

función, donde nnn es el orden. La solución de esta familia de funciones es

y ( x ) = AJ n ( x ) + POR n ( x ) , y(x)=AJ_n(x)+BY_n(x),y ( x )=AJ _n( X )+POR _n( X ) ,

Entre ellos J n ( x ) J_n(x)jn( x ) se llamafunción de Bessel de primer tipo,Y n ( x ) Y_n(x)Yn( x ) se denominafunción de Bessel de segunda clase.

La función de Bessel se puede considerar como una familia de funciones de seno en descomposición, como se muestra en la Figura 1, donde J 0 ( 0 ) = 1 J_0(0)=1j0( 0 )=1 Y n ( 0 ) = ∞ Y_n(0)=\infty Yn( 0 )= .

Figura 1. Gráficas de funciones de Bessel de primera y segunda clase

Usando el método de Frobenius para resolver, podemos obtener,

J norte ( X ) = ∑ k = 0 ∞ ( - 1 ) kk ! ( n + k ) ! ( X 2 ) norte + 2 k Y norte ( X ) = 2 π J norte ( X ) [ en ⁡ X 2 + γ ] - 1 π ∑ k = 0 norte - 1 ( norte - k - 1 ) ! k! ( X 2 ) 2 k - norte - 1 π ∑ k = 0 ∞ ( - 1 ) k [ Φ ( k ) + Φ ( norte + k ) ] 1 k ! ( n + k ) ! ( x 2 ) 2 k + n , J_n(x)=\sum_{k=0}^\infty\frac{(-1)^k}{k!(n+k)!}\left(\frac{ x}{2}\right)^{n+2k}\\ \begin{alineado} Y_n(x)=&\frac{2}{\pi}J_n(x)\left[\ln\frac{x} {2}+\gamma\right]-\frac{1}{\pi}\sum_{k=0}^{n-1}\frac{(nk-1)!}{k!}\left(\ frac{x}{2}\right)^{2k-n}\\ &-\frac{1}{\pi}\sum_{k=0}^\infty(-1)^k\left[\Phi (k)+\Phi(n+k)\right]\frac{1}{k!(n+k)!}\left(\frac{x}{2}\right)^{2k+n}, \end{alineado}jn( X )=k = 0k ! ( n+k )!( -1 ) _k(2x)n + 2 kYn( X )=Pi2jn( X )[ en2x+do ]Pi1k = 0n 1k !( nk1 ) !(2x)2 k nPi1k = 0( -1 ) _k[ Φ ( k )+F ( n+k ) ]k ! ( n+k )!1(2x)2 k + n,

donde γ = 0,0,577216 \gamma\stackrel{.}{=}0,577216C=.0.577216Φ ( pag ) = 1 + 1 2 + 1 3 + ⋯ + 1 pag \Phi(p)=1+\dfrac{1}{2}+\dfrac{1}{3}+\cdots+\dfrac{ 1Φ ( pag )=1+21+31++pag1p > 0 p > 0pag>0Φ(0)=0 \Phi(0)=0Φ ( 0 )=0 _

Entonces se puede considerar como x → ∞ x\to\inftyXLa ecuación asíntota de la función de Bessel en ∞ se aproxima como

J norte ( X ) ∼ 2 π X porque ⁡ ( X - norte π 2 - π 4 ) Y norte ( X ) ∼ 2 π X pecado ⁡ ( X - norte π 2 - π 4 ) \begin{alineado} J_n(x)&\sim\sqrt\frac{2}{\pi x}\cos\left(x-\frac{n\pi}{2}-\frac{\pi}{4 }\right)\\ Y_n(x)&\sim\sqrt\frac{2}{\pi x}\sin\left(x-\frac{n\pi}{2}-\frac{\pi}{ 4}\derecha). \end{alineado}jn( X )Yn( x )x_ _2 porque( X24pag)x_ _2 pecado( X24pag).

Además, la función de Hankel (Hankel), que se usa comúnmente en ingeniería, también se denomina función de Bessel de tercer tipo .

H norte ( 1 ) ( X ) ≡ J norte ( X ) + j Y norte ( X ) H norte ( 2 ) ( X ) ≡ J norte ( X ) − j Y norte ( X ) , \begin{alineado} H_n^ {(1)}(x)&\equiv J_n(x)+jY_n(x)\\ H_n^{(2)}(x)&\equiv J_n(x)-jY_n(x), \end{alineado}Hnorte( 1 )( X )Hnorte( 2 )( x )jn( X )+j Yn( X )jn( X )j Yn( x ) ,

donde superíndice ( 1 ) ^{(1)}( 1 )( 2 ) ^{(2)}( 2 ) para el primer y segundo tipo de funciones de Bessel respectivamente Cuandox → ∞ x\to\inftyX ,

H norte ( 1 ) ( X ) ∼ 2 π xe + j ( X - norte π 2 - π 4 ) H norte ( 2 ) ( X ) ∼ 2 π xe - j ( X - norte π 2 - π 4 ) . \begin{alineado} H_n^{(1)}(x)&\sim\sqrt\frac{2}{\pi x}e^{+j\left(x-\frac{n\pi}{2} -\frac{\pi}{4}\right)}\\ H_n^{(2)}(x)&\sim\sqrt\frac{2}{\pi x}e^{-j\left(x) -\frac{n\pi}{2}-\frac{\pi}{4}\right)}. \end{alineado}Hnorte( 1 )( X )Hnorte( 2 )( x )x_ _2 mi+ j ( x -24pag)x_ _2 mij ( x 24pag) .

Las funciones de Bessel del tercer tipo se utilizan a menudo para tratar problemas gráficos o de ingeniería con múltiples sistemas de coordenadas.

1.2 Curva de Bézier

La base matemática de las curvas de Bezier en realidad proviene de los polinomios de Bernstein publicados en 1912, una familia que lleva el nombre del antiguo matemático soviético Sergei Natanovich Bernstein (серге́й нататнович б. stein polinomios fue propuesto por el ingeniero francés de Citroen Paul de Casteljau (Paul de Casteljau, 1930-2022) en 1959. Debido a que este método fue adoptado por Citroen Registrado como patente, su contenido principal no se reveló hasta la década de 1980, por lo que ahora parece B ( t ) = ∑ yo = 0 norte β ibi , norte ( t ) segundo(t)=\sum_{i=0}^n\beta_i b_{i,n}(t)segundo ( t )=yo = 0nbyobyo , nLa curva de forma de Bernstein de ( t ) lleva el nombre de la primera Bezier pública, llamadacurva de Bezier.Dondeβ i \beta_ibyopor segundocontrolo puntos,

bi , norte ( t ) = ( ni ) ( 1 − t ) norte − iti , b_{i,n}(t)=\binom{n}{i}(1-t)^{ni}t^i,byo , n( t )=(in) (1t )norte - yo tyo ,

es el polinomio base de Bernstein.

1.2.1 Algoritmo de De Castrio

El algoritmo de De Castrio en realidad descompone una sola función de Bernstein en dos curvas de Bezier, t 0 t_0t0La curva at puede obtenerse mediante la siguiente relación de recurrencia,

β yo ( 0 ) ≡ β yo , yo = 0 , 1 , ⋯ , norte β yo ( j ) ≡ β yo ( j - 1 ) ( 1 - t 0 ) + β yo + 1 ( j - 1 ) t 0 , \begin{alineado} \beta_i^{(0)}&\equiv\beta_i,\;i=0,1,\cdots,n \\ \beta_i^{(j)}&\equiv\beta_i^{(j -1)}(1-t_0)+\beta_{i+1}^{(j-1)}t_0, \end{alineado}bi( 0 )bi( j )byo,i=0 ,1 ,,nortebi( j 1 )( 1t0)+byo + 1( j 1 )t0,

Entre ellos, para β i ( j ) \beta_i^{(j)}bi( j ),有i = 0 , 1 , ⋯ , norte − ji=0,1,\cdots,nji=0 ,1 ,,nortej ,j = 1 , 2 , ⋯ , nj=1,2,\cdots,nj=1 ,2 ,,N. En este momentot 0 t_0t0La curva de Bezier en B ( t 0 ) = β 0 ( n ) B(t_0)=\beta_0^{(n)}segundo _ _0)=b0( n ), y se puede descomponer en

β 0 ( 0 ) , β 0 ( 1 ) , β 0 ( 2 ) , ⋯ , β 0 ( norte ) β 0 ( norte ) , β 1 ( norte - 1 ) , β 2 ( norte - 2 ) , ⋯ , β norte ( 0 ) . \begin{alineado} &\beta_0^{(0)},\beta_0^{(1)},\beta_0^{(2)},\cdots,\beta_0^{(n)}\\ &\beta_0^ {(n)},\beta_1^{(n-1)},\beta_2^{(n-2)},\cdots,\beta_n^{(0)}. \end{alineado}b0( 0 ),b0( 1 ),b0( 2 ),,b0( n )b0( n ),b1( norte - 1 ),b2( norte - 2 ),,bnorte( 0 ).

La ventaja del algoritmo de de castrio es que tiene las características de estabilidad numérica, y la desventaja es que es menos eficiente que el cálculo directo.La siguiente es una implementación simple del algoritmo de de castrio en lenguaje Python, para referencia de los lectores 1 .

def de_casteljau(t, coefs):
    beta = [c for c in coefs] # 此列表中的数据将于后续计算中覆盖
    n = len(beta)
    for j in range(1, n):
        for k in range(n - j):
            beta[k] = beta[k] * (1 - t) + beta[k + 1] * t
    return beta[0]

1.2.2 Curva de Bézier 2

Como se mencionó anteriormente, nnLa fórmula general de la curva de Bézier de orden n es

segundo ( t ) = ∑ yo = 0 norte β ibi , norte ( t ) , segundo(t)=\sum_{i=0}^n\beta_i b_{i,n}(t),segundo ( t )=yo = 0nbyobyo , n( t ) ,

donde t ∈ [ 0 , 1 ] t\in[0,1]t[ 0 ,1 ] Obviamente, cuandon = 1 n=1norte=1 , se puede obtener una curva de Bezier

segundo ( t ) = β 0 + ( β 1 − β 0 ) t = ( 1 − t ) β 0 + t β 1 , \begin{alineado} B(t)&=\beta_0+(\beta_1-\beta_0)t \\ &=(1-t)\beta_0+t\beta_1, \end{alineado}segundo ( t )=b0+( segundo1b0) t=( 1t ) segundo0+t β1,

Su gráfico es el punto final de β 0 \beta_0b0Movimiento a β 1 ​​\beta_1b1La línea recta, como se muestra en la Figura 2, también se llama curva de Bezier lineal. Tenga en cuenta que β i \beta_i en la figurabyoUsar P i P_iPAGyosignifica, lo mismo a continuación.

Figura 2. Una curva de Bezier

norte = 2 norte = 2norte=2 , segundo( t ) = ( 1 − t ) 2 β 0 + 2 t ( 1 − t ) β 1 + t 2 β 2 B(t)=(1-t)^2\beta_0+2t(1- t)\beta_1+t^2\beta_2segundo ( t )=( 1t )2b _0+2t ( 1 _t ) segundo1+t2b _2es un punto en movimiento Q 0 Q_0q0Q 1 Q_1q1- también conocido como punto intermedio - ya que β 1 \beta_1b1Movimiento a β 2 \beta_2b2formulario, como se muestra en la figura 3.

Figura 3. Curva de Bézier cuadrática

Las fuentes TrueType comúnmente utilizadas en los sistemas informáticos se implementan mediante curvas Bezier cuadráticas.

cuando n = 3 n=3norte=3 o superior, para construirnnn veces la curva de Bezier, es necesario introducir( nn − 1 ) \dbinom{n}{n-1}(norte1n) puntos intermedios La figura 4 demuestra el proceso de construcción de curvas de Bézier cúbicas y cuarticas.

Primero
Segundo

Figura 4. (A) Curva de Bézier cúbica, (B) Curva de Bézier cuartica

Los gráficos y animaciones anteriores se realizan mediante el algoritmo de De Castrio.

2 Controles de diseño basados ​​en curvas Bezier

Todos los sistemas operativos de gráficos comunes actualmente en el mercado tienen funciones integradas para dibujar curvas de Bezier en sus sistemas de gráficos Tomando Windows como ejemplo, su sistema de gráficos GDI/GDI+ implementa una curva de Bezier de cuatro puntos como se muestra en la Figura 5. Eso es , en la curva de Bezier cúbica, β 1 \beta_1b1y β 2 \beta_2b2utilizados respectivamente como β 0 \beta_0b0y β 3 \beta_3b3La mano de control de En la expresión de la curva, β 0 \beta_0b0β 1 \beta_1b1β 2 \beta_2b2β 3 \beta_3b3Correspondientes a los puntos "Primero", "Segundo", "Tercero" y "Cuarto" del gráfico respectivamente.

Figura 5. Curvas de Bezier en gráficos por computadora

2.1 Dibujar una curva Bezier 3

Al llamar a las funciones envueltas en GDI+ para dibujar curvas Bezier cúbicas DrawBezier,

public void DrawBezier(Pen pen,
                       Point pt1,
                       Point pt2,
                       Point pt3,
                       Point pt4);
public void DrawBezier(Pen pen,
                       PointF pt1,
                       PointF pt2,
                       PointF pt3,
                       PointF pt4);
public void DrawBezier(Pen pen,
                       float x1,
                       float y1,
                       float x2,
                       float y2,
                       float x3,
                       float y3,
                       float x4,
                       float y4);

Los usuarios pueden dibujar fácilmente la curva Bezier deseada en el formulario.La figura 6 se dibuja con el siguiente código.

private void Exercise_Paint(object sender, PaintEventArgs e)
{
    
    
        Pen penCurrent = new Pen(Color.Blue);
        Point pt1 = new Point(20, 12),
                    pt2 = new Point(88, 246),
                    pt3 = new Point(364, 192),
                    pt4 = new Point(250, 48);

        e.Graphics.DrawBezier(penCurrent, pt1, pt2, pt3, pt4);
}

Figura 6. Curva de Bezier dibujada por GDI+

2.2 Definir la curva Bezier en el control gráfico

Para realizar la función de interacción gráfica entre el usuario y el control, primero maneje los puntos de control de la curva Bézier cúbica. Si se expresa en el concepto de "Modelo-Vista-Control" (MVC), el control gráfico de la curva Bézier aquí puede diseñarse de acuerdo con los siguientes capítulos.

2.2.1 Modelo

Se puede ver en los capítulos anteriores que la curva de Bézier cúbica tiene 2 puntos finales y 2 puntos intermedios, como se muestra en la Figura 7, que se denominan puntos de anclaje (anclaje) y puntos de control (manejador) respectivamente.

Figura 7. Los puntos finales en el control de la curva Bezier, los puntos de control en la figura son los puntos de manejo descritos en el texto

Los puntos finales de la curva de Bézier se definen de la siguiente manera:

// 贝塞尔曲线点
public class BezierPoint {
    
    
	public Point Anchor {
    
     get; set; }
	public Point Handler {
    
     get; set; }
}

El punto final de cada curva de Bezier contiene un punto de anclaje y el punto de control correspondiente, es decir, ( β 0 , β 1 ) (\beta_0,\beta_1) en la expresión de la curva( segundo0,b1) o( β 2 , β 3 ) (\beta_2,\beta_3)( segundo2,b3) Entonces, un segmento de curva Bézier cúbico se puede definir como:

// 贝塞尔曲线段
public class BezierSegment {
    
    
    public BezierPoint Begin;
    public BezierPoint End;
}

Al cambiar la posición del punto de anclaje y la posición del punto de control, se puede realizar una curva Bezier de cualquier forma, y ​​su efecto de uso debe ser como se muestra en la animación de la Figura 8.

Figura 8. Aspecto del control de la curva Bezier controlable y demostración interactiva

2.2.2 Vistas

La apariencia de la curva es independiente de los puntos finales de la curva y se puede dibujar utilizando un renderizador escalable independiente, como el siguiente renderizador simple:

public class BezierRenderer {
    
    
    public void DrawSegment(Graphics g, BezierSegment s, Color color) {
    
    
    using (Pen p = new Pen(color)) {
    
    
        g.DrawBezier(
            p,
            s.Begin.Anchor,
            s.Begin.Handler,
            s.End.Anchor,
            s.End.Handler);
    }
    public void DrawHandle(Graphics g, Point pt, bool solid, Color color) {
    
    
        if (solid)
            using (SolidBrush b = new SolidBrush(color))
                g.FillRectangle(b, pt.X - 2, pt.Y - 2, 4, 4);
        else
            using (Pen p = new Pen(color))
                g.DrawRectangle(b, pt.X - 2, pt.Y - 2, 4, 4);
    }
    public void DrawBar(Graphics g, Point pt1, Point pt2, Color color) {
    
    
        using (Pen p = new Pen(color))
            g.DrawLine(p, pt1, pt2);
    }
}

Este renderizador solo brinda la capacidad de personalizar el color de cada punto y el color de la línea de conexión desde el punto de control hasta el punto de anclaje. Aquí hay un ejemplo de cómo usarlo:

// 画曲线
renderer.DrawSegment(g, s, Color.DimGray);

// 画锚点,灰色
renderer.DrawHandle(g, s.Begin.Anchor, false, Color.DimGray);
renderer.DrawHandle(g, s.End.Anchor, false, Color.DimGray);

// 画手柄点,黑色
renderer.DrawBar(g, s.Begin.Anchor, s.Begin.Handler, Color.Black);
renderer.DrawHandle(g, s.Begin.Handler, false, Color.Black);
renderer.DrawBar(g, s.End.Anchor, s.End.Handler, Color.Black);
renderer.DrawHandle(g, s.End.Handler, false, Color.Black);

// 被选中的手柄点为实心
if (target != null)
    renderer.DrawHandle(g, target.Handler, true, Color.Black);

De esta forma, se puede dibujar una curva Bezier, como se muestra en la Figura 9. Tenga en cuenta que el punto de anclaje y el punto de control coinciden en este momento, lo que se parece a una curva Bezier.

Figura 9. La curva Bezier dibujada en el control de gráficos, el punto de anclaje coincide con el punto de control

2.2.3 Mando

Lo anterior ha realizado el almacenamiento de datos y la expresión de apariencia del segmento de la curva de Bezier. Ahora agregue funciones de control, es decir, la capacidad de modificar datos. Aquí, el cursor del mouse se usa para interactuar. La lógica de interacción simple puede considerarse como sigue:

  1. Presione el botón del mouse, si el punto cae en un punto final (o dentro de su vecindad), el punto debe marcarse como seleccionado
  2. Mueva el cursor del mouse, si se selecciona un punto final, modifique las coordenadas del punto final de acuerdo con la posición del cursor
  3. Levante el botón del mouse para cancelar todas las selecciones de puntos finales

De esta manera, el control debe procesarse en los eventos MouseDown, MouseMovey MouseUpdel control. A continuación se muestra una implementación simple. Tenga en cuenta que la parte sobre el movimiento aquí solo considera el punto de control y, de hecho, el punto de anclaje debe considerarse en la implementación completa.

Primero, use el área de monitoreo del punto de acceso (es decir, el vecindario mencionado en 1) para expandir ligeramente la selección para reducir la dificultad de la selección del cursor. Además, defina un puntero para apuntar al punto final seleccionado.

Rectangle rectBegin, rectEnd;
BezierPoint target = null;

Ahora maneje los eventos de interacción del mouse.

// MouseDown,若落点位于某端点邻域内,则将其标记为已选中
if (rectBegin.Contains(e.Location))
    target = s.Begin;
else if (rectEnd.Contains(e.Location))
    target = s.End;

// MouseMove,若有选中状态的端点,则用光标位置更新其坐标
if (target != null) 
	target.ControlPoint = e.Location;

// MouseUp,取消选择
target = null;

De esta forma, se puede realizar el control de la curva de Bezier que se muestra en la Figura 8.

2.4 Efectos adicionales

Si modifica el renderizador, puede obtener más efectos de imagen, como la curva de color que se muestra en la Figura 10.

Figura 10. Demostración del efecto de ejecución del control después de cambiar el renderizador

Resumir

La curva de Bezier es una poderosa herramienta gráfica. En los sistemas operativos de las computadoras modernas, es fácil implementar controles de curva de Bezier interactivos y extensibles llamando a las funciones de dibujo del sistema. Este documento presenta las funciones de Bezier, las curvas y los métodos de computación y diseño relacionados brindan una implementación simple basada en Windows GDI+, y demostrar cómo cambiar la apariencia de los controles a través del renderizador. Los usuarios pueden conectar múltiples puntos de anclaje para lograr efectos de curvas complejas de cualquier escala. Además, también pueden usar Un método de dibujo más eficiente optimiza el control y agrega un mecanismo de manejo de eventos personalizado al control para que pueda ser utilizado en la práctica comercial.

(encima)

© 2013-2023 Conmajía


  1. Ver Algoritmo de De Castrio . ↩︎

  2. Ver curvas de Bézier . ↩︎

  3. Ver Formas GDI+: Curvas Bezier ↩︎

Supongo que te gusta

Origin blog.csdn.net/conmajia/article/details/43907499
Recomendado
Clasificación