Как нарисовать линию, используя мышь и угол блокировки каждые 5 градусов - PullRequest
1 голос
/ 07 апреля 2020

У меня есть проблема, когда мне нужно нарисовать линию с помощью мыши и зафиксировать угол каждые 5 градусов.

Я могу заблокировать линию по горизонтали и вертикали, но не могу зафиксировать угол

if (e.Button == MouseButtons.Left)
{
    if (e.Button != MouseButtons.Left)
    {
      return;
    }

     startLine = new Point();
     endLine = new Point();                    

     if (ModifierKeys == Keys.Control)
     {                        
       pointLineDest = new Point(e.X, pointLineOrigin.Y);
     }
     else if (ModifierKeys == (Keys.Control | Keys.Shift))
     {
       pointLineDest = new Point(pointLineOrigin.X, e.Y);                      
     }
     else
     {
        pointLineDest = e.Location;
        Globals.AddOutputLog($"{CalculeAngle(pointLineOrigin, pointLineDest)}");
     }

     startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
     endLine = new Point(pointLineDest.X, pointLineDest.Y);

     canvas.Refresh();
}

в вышеприведенном коде я фиксируюсь до 0 градусов, когда удерживаете Ctrl, и удерживает 90 градусов, когда удерживаете Shift + Ctrl. Но мне нужно разрешить пользователю перемещать линию на 5 градусов, если он этого хочет, и я не знаю, как это сделать.

Кто-нибудь может мне помочь, пожалуйста? извините за мой Engli sh.

Edit -----------------

Я думаю, что я решил проблему, но есть еще деталь .

Используя следующий метод, я могу повернуть на 5 градусов при перемещении мыши.

 static Point RotatePoint(Point pointToRotate, Point centerPoint, double angleInDegrees)
        {
            double angleInRadians = angleInDegrees * (Math.PI / 180);
            double cosTheta = Math.Cos(angleInRadians);
            double sinTheta = Math.Sin(angleInRadians);
            return new Point
            {
                X = (int)(cosTheta * (pointToRotate.X - centerPoint.X) -
                    sinTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.X),
                Y = (int)(sinTheta * (pointToRotate.X - centerPoint.X) +
                    cosTheta * (pointToRotate.Y - centerPoint.Y) + centerPoint.Y)
            };
        }

Я вызываю метод внутри события mouseMove следующим образом:

Point tempP = new Point(e.X, pointLineOrigin.Y);

int dy = e.Y - pointLineOrigin.Y;                        
int dir = (dy > 0) ? -1 : 1;

if (dir == -1)
   pointLineDest = RotatePoint(tempP, pointLineOrigin, -5);
else
   pointLineDest = RotatePoint(tempP, pointLineOrigin, 5);

startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
endLine = new Point(pointLineDest.X, pointLineDest.Y);

Итак, у меня есть линия, которая начинается с 0 градусов, и если я перемещаю мышь вверх, она меняется на 5 градусов, если я двигаюсь вниз, она меняется на -5 градусов. Отлично! Но если я продолжу двигать мышь вверх или вниз, это не увеличит наклон на 5 градусов. То есть я могу перемещаться только на 5 градусов вверх или вниз один раз.

Как удерживать линию вращающейся при перемещении мыши?

Редактировать ---------- ----------

Хорошо, я получил это с помощью @ amin29 a. Спасибо, приятель!

_rad5 = DegreeToRadian(5);
float rad = (float)Math.Atan2(e.Y - pointLineOrigin.Y, e.X - pointLineOrigin.X);

int x = pointLineOrigin.X;
int y = pointLineOrigin.Y;
float angle = rad; 

double step = _rad5;
double finalAngle;

double c = rad % _rad5;
finalAngle = angle - c;
if (c > step / 2)
     finalAngle = (angle - c) + step;

double length = Math.Sqrt((Math.Pow(pointLineOrigin.X - e.X, 2) + Math.Pow(pointLineOrigin.Y - e.Y, 2)));

// Create points that define line.
Point point1 = new Point(x, y);
Point point2 = new Point((int)(x + Math.Cos(finalAngle) * length), (int)(y + Math.Sin(finalAngle) * length));

startLine = point1;
endLine = point2;

1 Ответ

1 голос
/ 07 апреля 2020

используйте эти методы

    private double DegreeToRadian(double degree)
    {
        return degree * Math.PI / 180;
    }
    private Point LockInAngle(Point pt, double degree)
    {
        int y = (int)(pt.X * Math.Tan(DegreeToRadian(degree)));
        return new Point(pt.X, y);
    }

и ваш код должен быть таким

        if (e.Button == MouseButtons.Left)
        {
            startLine = new Point();
            endLine = new Point();

            if (ModifierKeys == Keys.Control)
            {
                pointLineDest = new Point(e.X, pointLineOrigin.Y);
            }
            else if (ModifierKeys == (Keys.Control | Keys.Shift))
            {
                pointLineDest = new Point(pointLineOrigin.X, e.Y);
            }
            else
            {
                pointLineDest = LockInAngle(e.Location, 5);
            }

            startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
            endLine = new Point(pointLineDest.X, pointLineDest.Y);


            canvas.Refresh();
        }

Редактировать ------------------ --------------------------------------- проверьте этот код, это windows формы

public partial class Form1 : Form
{
    Point startLine;
    Point endLine;
    Point pointLineDest;
    Point pointLineOrigin;
    double _rad5;
    public Form1()
    {
        InitializeComponent();
        startLine = Point.Empty;
        endLine = Point.Empty;
        pointLineOrigin = new Point(Width / 2, Height / 2);
        Paint += Form1_Paint;
        SizeChanged += Form1_SizeChanged;
        _rad5 = DegreeToRadian(5);
    }

    private void Form1_SizeChanged(object sender, EventArgs e)
    {
        pointLineOrigin = new Point(Width / 2, Height / 2);
        Invalidate(false);
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic; ;
        e.Graphics.DrawLine(Pens.Red, startLine, endLine);
    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (ModifierKeys == Keys.Control)
        {
            pointLineDest = new Point(e.X, pointLineOrigin.Y);
            startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
            endLine = new Point(pointLineDest.X, pointLineDest.Y);
            Invalidate(false);
        }
        else if (ModifierKeys == (Keys.Control | Keys.Shift))
        {
            pointLineDest = new Point(pointLineOrigin.X, e.Y);
            startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
            endLine = new Point(pointLineDest.X, pointLineDest.Y);
            Invalidate(false);
        }
        else
        {
            float rad =(float) Math.Atan2(e.Y - pointLineOrigin.Y, e.X - pointLineOrigin.X);


            if (Math.Abs(rad % _rad5) < 0.005)
            {
                pointLineDest = e.Location;
                startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
                endLine = new Point(pointLineDest.X, pointLineDest.Y);
                Invalidate(false);
            }
        }
    }

    private double DegreeToRadian(double degree)
    {
        return degree * Math.PI / 180;
    }

}

Редактировать ---------- Я отредактировал ваш код для повышения производительности, и вы можете добавить события MouseUp и MouseDown, если хотите работать левой кнопкой мыши. (Если ваш холст имеет свойство DoubleBuffered, установите его в true)

 public partial class Form1 : Form
{
    bool _isMouseDown;
    Point startLine;
    Point endLine;
    Point pointLineDest;
    Point pointLineOrigin;
    float _rad5;
    float _rad5DivideBy2;

    public Form1()
    {
        InitializeComponent();
        this.DoubleBuffered = true;
        _rad5 = DegreeToRadian(5);
        _rad5DivideBy2 = _rad5 / 2;

        startLine = Point.Empty;
        endLine = Point.Empty;
        pointLineOrigin = new Point(Width / 2, Height / 2);
        Paint += Form1_Paint;
        SizeChanged += Form1_SizeChanged;
    }

    private void Form1_SizeChanged(object sender, EventArgs e)
    {
        pointLineOrigin = new Point(Width / 2, Height / 2);
        Invalidate(false);
    }

    private void Form1_Paint(object sender, PaintEventArgs e)
    {
        e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.Bilinear; 
        e.Graphics.DrawLine(Pens.Red, startLine, endLine);
    }

    private void Form1_MouseDown(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            _isMouseDown = true;
        }
    }

    private void Form1_MouseUp(object sender, MouseEventArgs e)
    {
        if (e.Button == MouseButtons.Left)
        {
            _isMouseDown = false;
        }

    }

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {
        if (_isMouseDown)
        {
            if (ModifierKeys == Keys.Control)
            {
                pointLineDest = new Point(e.X, pointLineOrigin.Y);
                startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
                endLine = new Point(pointLineDest.X, pointLineDest.Y);
                Invalidate(false);
            }
            else if (ModifierKeys == (Keys.Control | Keys.Shift))
            {
                pointLineDest = new Point(pointLineOrigin.X, e.Y);
                startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
                endLine = new Point(pointLineDest.X, pointLineDest.Y);
                Invalidate(false);
            }
            else
            {
                int dx = e.X - pointLineOrigin.X;
                int dy = e.Y - pointLineOrigin.Y;

                float rad = (float)Math.Atan2(dy, dx);

                if (rad < 0)
                {
                    rad += (float)(2 * Math.PI);
                }

                float rem = rad % _rad5;
                float finalAngle = rad - rem;
                if (rem > _rad5DivideBy2)
                {
                    finalAngle += _rad5;
                }

                float length = (float)Math.Sqrt(dx * dx + dy * dy);

                pointLineDest = new Point((int)(Math.Cos(finalAngle) * length), (int)(Math.Sin(finalAngle) * length));
                pointLineDest.Offset(pointLineOrigin);
                startLine = new Point(pointLineOrigin.X, pointLineOrigin.Y);
                endLine = new Point(pointLineDest.X, pointLineDest.Y);
                Invalidate(false);
            }
        }
    }

    private float DegreeToRadian(float degree)
    {
        return (float)(degree * Math.PI / 180);
    }

}
...