C#でGIMPのパス機能ぽいものを試作する。

コンピュータ

C#でGraphicsPathを眺めていてGIMPのパス機能が作れないか試行錯誤してみました。

namespace Maru1;

using System.Drawing.Drawing2D;
using System.Collections.Generic;
using System.Linq;

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        // コントロールの生成
        var panel1 = new Panel {
            Dock = DockStyle.Fill,
            AutoScroll = true,
        };
        Controls.Add(panel1);
        var picbox1 = new PictureBox {
            SizeMode = PictureBoxSizeMode.AutoSize,
            Image = new Bitmap(1024, 1024),
        };
        panel1.Controls.Add(picbox1);

        // パス
        LinkedList<GraphicsPath> marus = new();
        GraphicsPath lines = new();

        // ペン
        var pen1 = new Pen(Brushes.Black, 1);

        // ポイントの削除及び移動
        LinkedListNode<GraphicsPath>? targetPoint = null;
        PointF? removePoint = null;

        // マウスダウン
        picbox1.MouseDown += (s, e) => {
            
            LinkedListNode<GraphicsPath>? p = marus.First;
            while(p != null) {
                var tmp = p.Value;
                if (tmp.IsVisible(e.Location)) {
                    targetPoint = p;
                }
                p = p.Next;
            }
            if (targetPoint != null) {
                // 〇を削除
                removePoint = e.Location;
                return;
            } else {
                // 〇を追加
                var path = new GraphicsPath();
                path.AddEllipse(e.X-5, e.Y-5, 10, 10);
                var n = new LinkedListNode<GraphicsPath>(path);
                marus.AddLast(n);
            }
            picbox1.Invalidate();
        };
        // マウスアップ
        picbox1.MouseUp += (s, e) => {
            if (targetPoint != null) {
                if (removePoint == e.Location) {
                    // 〇を削除
                    marus.Remove(targetPoint);
                    targetPoint.Value.Dispose();
                }
                targetPoint = null;
                picbox1.Invalidate();
            }
        };
        // マウス移動
        picbox1.MouseMove += (s, e) => {
            if (targetPoint != null) {
                // 〇を移動
                targetPoint.Value.Dispose();
                targetPoint.Value = new GraphicsPath();
                targetPoint.Value.AddEllipse(e.X-5, e.Y-5, 10, 10);
                picbox1.Invalidate();
            }
        };
        // ペイント
        picbox1.Paint += (s, e) => {
            // 背景色でクリア
            e.Graphics.Clear(picbox1.BackColor);
            

            List<PointF> points = new();
            LinkedListNode<GraphicsPath>? p = marus.First;
            while(p != null) {
                var path = p.Value;

                e.Graphics.DrawPath(pen1, path);
                PointF pf = new(path.PathPoints[0].X-5, path.PathPoints[0].Y);
                points.Add(pf);

                p = p.Next;
            }
            if (points.Any()) {
                lines.Dispose();
                lines = new GraphicsPath();
                lines.AddLines(points.ToArray());
                e.Graphics.DrawPath(pen1, lines);
            };
        };
    }
}

機能的には、マウスを適当な場所でクリックするとアンカーポイントとして〇が表示されます。さらに別の場所をクリックすると〇の表示に加えて前回のアンカーポイントを結ぶ直線(セグメント)が表示されます。
既存のアンカーポイント上でをマウスボタンを押し、同じ場所でマウスボタンを離すとアンカーポイントが削除されます。
また、既存のアンカーポイント上でをマウスボタンを押し、押した状態でマウスを移動しマウスボタンを離すとアンカーポイントが移動します。

今の段階では使い道がないのですが、パスを閉じる?機能をつけることが出来れば、パスで多角形を作ることが出来るので範囲選択に使えそうな感じがします。

また、線の途中にポイントを追加する機能も付けたいところですが、今の作りでは難しそうです。
さらに言えばベジェ曲線?を作るためハンドル機能も付けたいところですが、非常に大変そうです。

コメント