ベジェ曲線 (3次 cubic-bezier) を描画する - C#

C#で3次のベジェ曲線(cubic-bezier)を描画するコードを紹介します。

概要

C#で3次のベジェ曲線を描画する場合は、GraphicsオブジェクトのDrawBezier() メソッドを利用すると簡単に描画できます。
補足
座標値の値が必要な場合など、直接ベジェ曲線の計算をして描画する方法についてはこちらの記事を参照してください。

プログラム例

UI

下図のフォームを作成します。テキストボックスを4つ、ボタンを1つ、パネルコントロールを一つ配置します。

コード

以下のコードを記述します。
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 FormDrawCubicBezier : Form
  {
    Point point1;
    Point point2;
    Point point3;
    Point point4;
    Pen bPen;
    Brush backBrush;

    public FormDrawCubicBezier()
    {
      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);
      int cp2x = Convert.ToInt32(textBox3.Text);
      int cp2y = Convert.ToInt32(textBox4.Text);

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

      panel1.Refresh();
    }

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

      e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
      e.Graphics.DrawBezier(bPen, point1, point2, point3, point4);
    }
  }
}

解説

[button1]をクリックすると以下のコードを実行します。 テキストボックスに入力されている2つの制御点の座標値を変数に代入し、曲線の始点と終点を合わせた4つのPoint型の値を作成します。
  • point1 : 曲線の始点 パネルの左下(0,320)とします。
  • point2 : 曲線の1つ目のコントロールポイント(制御点)テキストボックスに入力された値を利用します。
  • point3 : 曲線の2つ目のコントロールポイント(制御点)テキストボックスに入力された値を利用します。
  • point4 : 曲線の終点 パネルの右上(320,0)とします。
  int cp1x = Convert.ToInt32(textBox1.Text);
  int cp1y = Convert.ToInt32(textBox2.Text);
  int cp2x = Convert.ToInt32(textBox3.Text);
  int cp2y = Convert.ToInt32(textBox4.Text);

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

値の設定後、パネルを更新します。
  panel1.Refresh();

パネルのPaintメソッドで曲線を描画します。描画のコードは次のコードです。

背景部分を白色で塗りつぶします。
  e.Graphics.FillRectangle(backBrush, new Rectangle(0, 0, 320, 320));

SmoothingMode でアンチエイリアスを有効にします。DrawBezier() メソッドでベジェ曲線を描画します。 第一引数にびょぐあで利用するペンオブジェクトを与えます。第2引数にベジェ曲線の始点の座標、第3引数に1つ目のコントロールポイントの座標、 第4引数に2つ目のコントロールポイントの座標、第5引数にベジェ曲線の終点の座標を与えます。
  e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
  e.Graphics.DrawBezier(bPen, point1, point2, point3, point4);

実行結果

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


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


上部のテキストボックスのコントロールポイントの座標値を変更して[Draw]ボタンをクリックすると、曲線の形状が変わることが確認できます。


3次ベジェ曲線を描画できました。

プログラム例2

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

UI

下図のフォームを作成します。テキストボックスを4つ、ボタンを1つ、パネルコントロールを一つ配置します。

コード

以下のコードを記述します。
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 FormDrawCubicBezierEx : Form
  {
    Point point1;
    Point point2;
    Point point3;
    Point point4;
    Pen bPen;
    Pen fPen;
    Pen cPen;
    Brush backBrush;
    Brush controlpointBrush;

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

    public FormDrawCubicBezierEx()
    {
      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, point4, point3);

      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));

      e.Graphics.DrawBezier(bPen, point1, point2, point3, point4);
    }

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

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

      panel1.Refresh();
    }
  }
}

解説

基本の処理は先のコードと同様です。コントロールポイントを描画する処理や、正規化した値を座標に変換するための掛け算などを追加しています。

実行結果

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


[Draw]ボタンをクリックします。下図の画面が表示されます。ベジェ曲線とコントロールポイントがパネル上に表示されます。


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

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2022-02-14
作成日: 2022-02-13
iPentec all rights reserverd.