ベジェ曲線 (2次 quadratic-bezier) の座標を計算する - C#

ベジェ曲線 (2次 quadratic-bezier) の座標を計算するコードを紹介します。

概要

こちらの記事では3次のベジェ曲線の座標を計算するするコードを紹介しました。 この記事では、2次のベジェ曲線の座標を計算するコードを紹介します。

計算式

2次のベジェ曲線の計算式は次の通りです。


x,yのそれぞれの座標値は次の計算式になります。


  • 始点 (p1x,p1y)
  • コントロールポイント1 (p2x,p2y)
  • 終点 (p3x,p3y)
このとき
x = (1-t)*(1-t)*p1x + 2*(1-t)*t*p2x + t*t*p3x

y = (1-t)*(1-t)*p1x + 2*(1-t)*t*p2y + t*t*p3y
となり、tの値を0から1まで変化させたときのx,yの値がベジェ曲線の座標値になります。

プログラム

UI

下図のフォームを作成します。

コード

下記のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SimpleDrawCubicBezier
{
  public partial class FormManualDrawQuadraticBezier : Form
  {
    Point point1;
    Point point2;
    Point point3;
    Pen bPen;
    Brush backBrush;

    public FormManualDrawQuadraticBezier()
    {
      InitializeComponent();
      bPen = new Pen(Color.Blue, 2);
      backBrush = new SolidBrush(Color.White);
    }

    private void button1_Click(object sender, EventArgs e)
    {
      int cp1x = Convert.ToInt32(textBox1.Text);
      int cp1y = Convert.ToInt32(textBox2.Text);

      point1 = new Point(0, 320);
      point2 = new Point(cp1x, cp1y);
      point3 = new Point(320, 0);

      panel1.Refresh();
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
      e.Graphics.FillRectangle(backBrush, new Rectangle(0, 0, 320, 320));

      double px = point1.X;
      double py = point1.Y;

      for (double t = 0; t <= 1.0; t = t + 0.01)
      {
        double x = Math.Pow(1 - t, 2) * point1.X + 2 * (1 - t) * t * point2.X + Math.Pow(t, 2) * point3.X;
        double y = Math.Pow(1 - t, 2) * point1.Y + 2 * (1 - t) * t * point2.Y + Math.Pow(t, 2) * point3.Y;

        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        e.Graphics.DrawLine(bPen, (float)px, (float)py, (float)x, (float)y);

        px = x;
        py = y;
      }
    }
  }
}

解説

コードの処理のほとんどは3次のベジェ曲線のプログラムと同様です。
計算式の部分が異なります。
  double x = Math.Pow(1 - t, 2) * point1.X + 2 * (1 - t) * t * point2.X + Math.Pow(t, 2) * point3.X;
  double y = Math.Pow(1 - t, 2) * point1.Y + 2 * (1 - t) * t * point2.Y + Math.Pow(t, 2) * point3.Y;

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[Draw]ボタンをクリックします。パネルにベジェ曲線が描画されます。


[コントロールポイント]の値を変更し、[Draw]ボタンを再度クリックします。曲線の形状が変更されることが確認できます。

プログラム例2

コントロールポイントの位置や、ベジェ曲線の値が枠をはみ出した場合でも描画できるよう周囲にマージンを追加します。 また、コントロールポイントの値を枠の幅、高さで正規化します。

UI

下図のフォームを作成します。

コード

以下のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SimpleDrawCubicBezier
{
  public partial class FormManualDrawQuadraticBezierEx : Form
  {

    Point point1;
    Point point2;
    Point point3;

    Pen bPen;
    Pen fPen;
    Pen cPen;
    Brush backBrush;
    Brush controlpointBrush;

    int offsetX, offsetY;
    int areaWidth, areaHeight;
    int controlpointRaduis;


    public FormManualDrawQuadraticBezierEx()
    {
      InitializeComponent();

      offsetX = 64;
      offsetY = 64;
      areaWidth = 320;
      areaHeight = 320;
      controlpointRaduis = 8;

      bPen = new Pen(Color.Blue, 2);
      fPen = new Pen(Color.Black, 1);
      cPen = new Pen(Color.FromArgb(105, 161, 255), 1);
      backBrush = new SolidBrush(Color.White);
      controlpointBrush = new SolidBrush(Color.FromArgb(105, 161, 255));
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
      e.Graphics.FillRectangle(backBrush, new Rectangle(0, 0, panel1.Width, panel1.Height));

      e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

      e.Graphics.DrawRectangle(fPen, new Rectangle(offsetX, offsetY, areaWidth, areaHeight));

      e.Graphics.DrawLine(cPen, point1, point2);
      e.Graphics.DrawLine(cPen, point3, point2);

      e.Graphics.FillEllipse(controlpointBrush, new Rectangle(point2.X - controlpointRaduis, point2.Y - controlpointRaduis, controlpointRaduis * 2, controlpointRaduis * 2));
      e.Graphics.FillEllipse(controlpointBrush, new Rectangle(point3.X - controlpointRaduis, point3.Y - controlpointRaduis, controlpointRaduis * 2, controlpointRaduis * 2));

      double px = point1.X;
      double py = point1.Y;

      for (double t = 0; t <= 1.0; t = t + 0.01)
      {
        double x = Math.Pow(1 - t, 2) * point1.X + 2 * (1 - t) * t * point2.X + Math.Pow(t, 2) * point3.X;
        double y = Math.Pow(1 - t, 2) * point1.Y + 2 * (1 - t) * t * point2.Y + Math.Pow(t, 2) * point3.Y;

        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        e.Graphics.DrawLine(bPen, (float)px, (float)py, (float)x, (float)y);

        px = x;
        py = y;
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      double cp1x = Convert.ToDouble(textBox1.Text);
      double cp1y = Convert.ToDouble(textBox2.Text);

      point1 = new Point(offsetX, offsetY + areaHeight);
      point2 = new Point(offsetX + (int)(areaWidth * cp1x), offsetY + (int)(areaHeight * cp1y));
      point3 = new Point(offsetX + areaWidth, offsetY);

      panel1.Refresh();
    }
  }
}

解説

コードの処理のほとんどは3次のベジェ曲線のプログラムと同様です。
計算式の部分が異なります。

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[Draw]ボタンをクリックします。パネルにベジェ曲線が描画されます。


コントロールポイントの値を変更して再度[Draw]ボタンをクリックします。ベジェ曲線の形状が変わります。


コントロールポイントの値を変更してベジェ曲線が枠からはみ出る場合でも描画できます。


2次のベジェ曲線の座標値を求め、描画できました。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2022-02-14
iPentec all rights reserverd.