简体   繁体   中英

On a canvas, how do I let the user to only draw a circle in a specified line?

Im doing school project. My task is to write a small Winform application that represents the Bezier Curve, but with some constraints.

I did almost everything, just one more step is ahead of me.

The whole program starts with an empty canvas, then the user can click on it, and a circle is drawn. After every 4th click, the bezier curve appears to that polygon. Now comes my problem.

What I am stuck with is that I have to controll somehow where the 5th click is going to be. It must be on a line that comes from 2 points: the 3rd and 4th points.

Can anybody help me with this? I have really no idea how to even start.

So far, this is my code.

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

You can simply project the click position on the desired line.

If c is the click position and A and B are the two last control points, then the projected position p is:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM