На холсте, как я могу позволить пользователю нарисовать только круг в указанной линии? - PullRequest
0 голосов
/ 15 января 2020

Я занимаюсь школьным проектом. Моя задача - написать небольшое приложение Winform, представляющее кривую Безье, но с некоторыми ограничениями.

Я сделал почти все, только еще один шаг впереди меня.

Вся программа запускается с пустым холстом, затем пользователь может щелкнуть по нему, и круг будет нарисован. После каждого 4-го щелчка кривая Безье появляется для этого многоугольника. Теперь приходит моя проблема.

Я застрял в том, что мне нужно каким-то образом контролировать, где будет 5-й щелчок. Это должно быть на линии, которая идет от 2-х пунктов: 3-го и 4-го.

Кто-нибудь может мне помочь с этим? Я действительно понятия не имею, с чего начать.

Пока это мой код.

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 grafika_beadando_kettesert
{
    public partial class MainForm : Form
    {
        Graphics g;
        int counter = 0;
        Pen PenBlack = Pens.Black;                  //ezzel a tollal rajzolom a vonalat
        Pen PenCurve = new Pen(Color.Blue, 3f);     //ezzel a tollal rajzolom a görbét
        Brush PenPoint;                             //Ezzel töltöm ki a pontot
        int size = 4;                               // a lerakott pont mérete
        int found = -1;

        List<PointF> Points = new List<PointF>();   //ebbe a listába tárolom a pontokat
        PointF p0, p1;

        public MainForm()
        {
            InitializeComponent();
            PenPoint = new SolidBrush(canvas.BackColor);
            this.DoubleBuffered = true;
        }

        private void canvas_Paint(object sender, PaintEventArgs e)
        {
            g = e.Graphics;

            g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;

            for (int i = 0; i < Points.Count - 1; i++)       // mindig meg kell rajzolni az eddig meghúzott vonalakat a polygonból újra
                g.DrawLine(PenBlack, Points[i], Points[i + 1]);

            if (counter == 4)
            {
                DrawBeziergorbe();
                counter = 0;
            }

            for (int i = 0; i < Points.Count; i++)          // ezzel rajzolom meg az eddig felrakott pontokat újra
            {
                g.FillEllipse(PenPoint, Points[i].X - size, Points[i].Y - size, 2 * size, 2 * size);
                g.DrawEllipse(PenBlack, Points[i].X - size, Points[i].Y - size, 2 * size, 2 * size);
            }
        }

        private void canvas_MouseUp(object sender, MouseEventArgs e)
        {
            found = -1;
        }

        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (found != -1)
            {
                Points[found] = e.Location;
                canvas.Invalidate();
            }

        }

        private void canvas_MouseDown(object sender, MouseEventArgs e)
        {
            for (int i = 0; i < Points.Count; i++)
            {
                if (Math.Abs(Points[i].X - e.X) <= size && Math.Abs(Points[i].Y - e.Y) <= size)
                {
                    found = i;
                    break;
                }
            }

            if (found == -1)
            {
                Points.Add(e.Location);  //ha nincs túl közel a lerakott pont egy jelenlegihez, akkor hozzáadja a 
                                         //"Points" listához, hogy innen kiolvasva újra belehessen rajzolni
                found = Points.Count - 1;
                counter++;
                canvas.Invalidate();

            }
        }  

        private void DrawBeziergorbe() //Mivel n-ed fokú bezier görbe kell, ezért használom a binomiálisos megoldást
        {
            int n = Points.Count - 1;
            double t = 0;
            double h = 1.0 / 500.0;
            double b = 0.0;
            p0 = new PointF(0, 0);
            for (int i = 0; i <= n; i++)
            {
                b = B(n, i, t);
                p0.X += (float)(b * Points[i].X);
                p0.Y += (float)(b * Points[i].Y);
            }
            while (t < 1)
            {
                t += h;
                p1 = new PointF(0, 0);
                for (int i = 0; i <= n; i++)
                {
                    b = B(n, i, t);
                    p1.X += (float)(b * Points[i].X);
                    p1.Y += (float)(b * Points[i].Y);
                }
                g.DrawLine(PenCurve, p0, p1);
                p0 = p1;
            }
        }

        private double B(int n, int i, double t)
        {
            return Binom(n, i) * (Math.Pow(1 - t, n - i) * Math.Pow(t, i));
        }

        private uint Binom(int n, int k)
        {
            if (n == 0) return 0;
            else if (k == 0 || k == n) return 1;
            else return Binom(n - 1, k - 1) + Binom(n - 1, k);
        }
    }
}

1 Ответ

0 голосов
/ 15 января 2020

Вы можете просто спроецировать позицию щелчка на нужную строку.

Если c - это позиция щелчка, а A и B - две последние контрольные точки, то спроецированная позиция p is:

d = B - A
p = A + dot(c - A, d) / dot(d, d) * d
...