2018年2月4日日曜日

ベジェ

 C#でベジェ曲線っぽいものを書きたくなったので試しに。
 ベジェ曲線の書き方自体うろ覚えで、しかも寝起きで書いてるので変な所あるかも。



 なんとなくそれっぽい感じに書けてるから、大丈夫じゃないかなぁって気がする。
 今回はそれっぽい曲線が書ければ十分なので、これで大丈夫ということにしておく。

 計算のpublic関数で、intとdoubleで多重定義しているので、そこだけ注意。
 それぞれの線の最大長さを指定する方法と、分割数を指定する方法の2種類がある。
 前者は計算量が多い代わりに、なめらかな曲線になる。後者は計算量が少ない代わりに、ガタガタした曲線になりやすい。
 上画像では、黒や白は前者で、緑は後者で計算している。





readonly PictureBox picture_box = null;

public Form1()
{
    InitializeComponent();

    picture_box = new PictureBox();
    picture_box.Dock = DockStyle.Fill;
    picture_box.Paint += Picture_box_Paint;
    picture_box.Click += delegate (object sender, EventArgs e) { ((PictureBox)sender).Refresh(); };
    Controls.Add(picture_box);

    MaximumSize = MinimumSize = Size = Size + new Size(1200, 1200) - picture_box.Size;
    StartPosition = FormStartPosition.CenterScreen;
}

private void Picture_box_Paint(Object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    g.Clear(Color.Gray);
    g.TranslateTransform(100, 100);
    g.DrawRectangle(Pens.Black, 0, 0, 1000, 1000);

    g.DrawLines(Pens.Black, PointD.convert_to_PointF(new Bezier()
    {
        points = new[] 
        {
            new PointD(0, 0),
            new PointD(0, 500),
            new PointD(1000, 500),
            new PointD(1000, 1000),
        }
    }.calc(Math.Sqrt(2))));

    g.DrawLines(Pens.White, PointD.convert_to_PointF(new Bezier()
    {
        points = new[] 
        {
            new PointD(0,0),
            new PointD(1000, 1000),
        }
    }.calc(Math.Sqrt(2))));
    g.DrawLines(Pens.White, PointD.convert_to_PointF(new Bezier()
    {
        points = new[] 
        {
            new PointD(0,0),
            new PointD(1000, 0),
            new PointD(1000, 1000),
        }
    }.calc(Math.Sqrt(2))));

    g.DrawLines(Pens.White, PointD.convert_to_PointF(new Bezier()
    {
        points = new[]
        {
            new PointD(0,0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 1000),
        }
    }.calc(Math.Sqrt(2))));

    g.DrawLines(Pens.White, PointD.convert_to_PointF(new Bezier()
    {
        points = new[] 
        {
            new PointD(0,0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 1000),
        }
    }.calc(Math.Sqrt(2))));

    g.DrawLines(Pens.White, PointD.convert_to_PointF(new Bezier()
    {
        points = new[]
        {
            new PointD(0,0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 1000),
        }
    }.calc(Math.Sqrt(2))));

    g.DrawLines(Pens.White, PointD.convert_to_PointF(new Bezier()
    {
        points = new[]
        {
            new PointD(0,0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 0),
            new PointD(1000, 1000),
        }
    }.calc(Math.Sqrt(2))));

    g.DrawLines(Pens.Lime, PointD.convert_to_PointF(new Bezier()
    {
        points = new[]
        {
            new PointD(0,0),
            new PointD(0, 1000),
            new PointD(1000, 1000),
            new PointD(1000, 0),
        }
    }.calc(10)));
}

class Bezier
{
    public PointD[] points { get; set; }

    public PointD[] calc(int number_of_points)
    {
        PointD[] result = new PointD[number_of_points];
        for (int i = 0; i < result.Length; i++)
        {
            result[i] = calc(points, (double)i / (result.Length - 1));
        }
        return (result);
    }

    public PointD[] calc(double P2P_max_distance)
    {
        return (calc(points, P2P_max_distance, 0, 1));
    }

    protected PointD[] calc(PointD[] points, double P2P_max_distance, double start_point, double end_point)
    {
        List list = new List();

        PointD p1 = calc(points, start_point);
        PointD p2 = calc(points, end_point);
        double distance = (p1 - p2).length;

        list.Add(p1);

        if (distance > P2P_max_distance)
        {
            double center = start_point + (end_point - start_point) * 0.5;
            list.AddRange(calc(points, P2P_max_distance, start_point, center));
            list.AddRange(calc(points, P2P_max_distance, center, end_point));
        }

        list.Add(p2);

        return (list.ToArray());
    }

    protected PointD calc(PointD[] points, double point)
    {
        if (points.Length < 2)
        {
            throw (new Exception());
        }
        else if (points.Length == 2)
        {
            return (calc(points, 0, point));
        }
        else
        {
            PointD[] new_points = new PointD[points.Length - 1];

            for (int i = 0; i < new_points.Length; i++)
            {
                new_points[i] = calc(points, i, point);
            }

            return (calc(new_points, point));
        }
    }

    protected PointD calc(PointD[] points, int start_index, double point)
    {
        int i = start_index;
        int j = i + 1;
        return (points[i] + (points[j] - points[i]) * point);
    }
}

struct PointD
{
    public double x { get; set; }
    public double y { get; set; }

    public PointD(double x, double y)
    {
        this.x = x;
        this.y = y;
    }

    public PointF convert_to_PointF()
    {
        return (new PointF((float)x, (float)y));
    }

    public static PointF[] convert_to_PointF(PointD[] points)
    {
        PointF[] result = new PointF[points.Length];
        for (int i = 0; i < result.Length; i++)
        {
            result[i] = points[i].convert_to_PointF();
        }
        return (result);
    }

    public double length
    {
        get
        {
            return (Math.Sqrt(x * x + y * y));
        }
    }

    public static PointD operator +(PointD p1, PointD p2)
    {
        return (new PointD(p1.x + p2.x, p1.y + p2.y));
    }

    public static PointD operator -(PointD p1, PointD p2)
    {
        return (new PointD(p1.x - p2.x, p1.y - p2.y));
    }

    public static PointD operator *(PointD p1, PointD p2)
    {
        return (new PointD(p1.x * p2.x, p1.y * p2.y));
    }

    public static PointD operator *(PointD p, double d)
    {
        return (new PointD(p.x * d, p.y * d));
    }

    public static PointD operator *(double d, PointD p)
    {
        return (p * d);
    }

    public static PointD operator /(PointD p1, PointD p2)
    {
        return (new PointD(p1.x / p2.x, p1.y / p2.y));
    }

    public static PointD operator /(PointD p, double d)
    {
        return (new PointD(p.x / d, p.y / d));
    }
}







0 件のコメント:

コメントを投稿