Рисование и масштабирование прямоугольника с использованием соотношения - PullRequest
0 голосов
/ 15 февраля 2019

У меня есть программа, в которой я могу с помощью мыши нарисовать прямоугольник в любом из четырех направлений.

Эти прямоугольники используются в окне PictureBox для обрезки частей изображения.

Эти прямоугольники должны быть нарисованы, сохраняя соотношение заданного размера, например, 320 x 200.

Я хочу, чтобы этот инструмент вел себя почти так же, как инструмент обрезки в Photoshop, или как в примере обрезки, найденном здесь:https://imageresize.org/

У меня большинство элементов работают правильно, я просто пытаюсь выполнить несколько геометрических вычислений.

См. Пример "Внизу справа" в моем коде.Это работает отлично, и в основном я просто хочу применить эту точную формулу к другим направлениям.

Я часами играю с разными вычислениями, и я просто не могу разобраться с этим.

Вот рабочий код:

 Protected Overrides Sub OnMouseMove(ByVal e As MouseEventArgs)
    'Draw rectangle keeping aspect ratio
    If e.Button = Windows.Forms.MouseButtons.Left Then
        If e.X > startPos.X And e.Y > startPos.Y Then
            'Bottom right
            mRect = New Rectangle(mRect.Left, mRect.Top, e.X - mRect.Left, e.Y - mRect.Top)
            mRect.Size = New Size(mRect.Width, mRect.Width / Ratio.Text)
            If e.Y < mRect.Bottom Then
                mRect = Rectangle.FromLTRB(startPos.X, startPos.Y, e.X, e.Y)
                mRect.Size = New Size(mRect.Height * Ratio.Text, mRect.Height)
            End If
            Me.Invalidate()
        ElseIf e.X < startPos.X And e.Y > startPos.Y Then
            'Bottom left
            mRect = New Rectangle(e.X, startPos.Y, startPos.X - e.X, e.Y - startPos.Y)
            mRect.Size = New Size(mRect.Width, mRect.Width / Ratio.Text)
            Me.Invalidate()
        ElseIf e.X > startPos.X And e.Y < startPos.Y Then
            'Top right
            mRect = New Rectangle(startPos.X, e.Y, e.X - startPos.X, startPos.Y - e.Y)
            mRect.Size = New Size(mRect.Height * 1.6, mRect.Height)
            Me.Invalidate()
        ElseIf e.X < startPos.X And e.Y < startPos.Y Then
            'Top left
            mRect = New Rectangle(e.X, e.Y, startPos.X - e.X, startPos.Y - e.Y)
            mRect.Size = New Size(mRect.Width, mRect.Width / Ratio.Text)
            Me.Invalidate()
        End If
    End If
End Sub

Любая помощь будет принята с благодарностью.Спасибо!

Ниже показано, как все работает в настоящее время, вы можете увидеть, что все становится не так просто при рисовании в северо-западном регионе.Мне нужно получить то же поведение, что и на юго-востоке (или справа внизу в соответствии с кодом) для всех квадрантов.

enter image description here

Ответы [ 2 ]

0 голосов
/ 15 февраля 2019

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

Конечно, вам нужно сохранить начальную позицию прямоугольника, используя событие MouseDown элемента управления, а затем отслеживать движения мыши, используя событие MouseMove.

Текущая позиция курсора рассчитывается как обычно (смена текущей позиции курсора и начальной позиции, когда смещения отрицательны).

Единственная разница - это высота прямоугольника, когда общий размер зависит от отношения Ratio.
В этом случае Rectangle.Location.Y определяется Rectangle.Width / Ratio мера.Это становится видимым, если Cursor.Location.Y на выше начальной позиции (Cursor.Location.Y <= StartingPosition.Y).Так же, как в коде, который вы опубликовали.

Например, я использую пользовательский класс Rectangle, который содержит всю информацию, необходимую для рисования фигуры, с определенным соотношением или без него, примененным к его размерам.

Обратите внимание, что Ratio жестко задан в 1.6: это просто для тестирования, конечно, он может быть установлен на что-то еще.

Визуальный образец результатов:

Drawing Rectangle with ration

Private DrawingRects As List(Of DrawingRectangle) = New List(Of DrawingRectangle)()


Private Sub PicureBox1_MouseDown(sender As Object, e As MouseEventArgs) Handles PicureBox1.MouseDown

    If e.Button = MouseButtons.Left Then
        DrawingRects.Add(New DrawingRectangle() With {
            .DrawingcColor = Color.LightGreen,
            .Location = e.Location,
            .Owner = CType(sender, Control),
            .Ratio = 1.6,
            .Size = Size.Empty,
            .StartPosition = e.Location
        })
    End If
End Sub

Private Sub PicureBox1_MouseMove(sender As Object, e As MouseEventArgs) Handles PicureBox1.MouseMove

    If e.Button = MouseButtons.Left Then
        Dim rect As DrawingRectangle = DrawingRects.Last()
        If e.X < rect.StartPosition.X Then rect.Location = New Point(e.X, rect.Location.Y)
        If e.Y < rect.StartPosition.Y Then rect.Location = New Point(rect.Location.X, e.Y)

        Dim currentWidth As Integer = Math.Abs(rect.StartPosition.X - e.X)

        If rect.Ratio = 1.0F Then
            rect.Size = New Size(currentWidth, Math.Abs(rect.StartPosition.Y - e.Y))
        Else
            If rect.StartPosition.Y <= rect.Location.Y Then
                rect.Size = New Size(currentWidth, CType(Math.Abs(rect.StartPosition.X - e.X) / rect.Ratio, Integer))
            Else
                Dim currentHeight As Integer = CType(currentWidth / rect.Ratio, Integer)
                rect.Location = New Point(rect.Location.X, rect.StartPosition.Y - currentHeight)
                rect.Size = New Size(currentWidth, currentHeight)
            End If
        End If
        DrawingRects(DrawingRects.Count - 1) = rect

        DirectCast(sender, Control).Invalidate()
    End If
End Sub

Private Sub PicureBox1_Paint(sender As Object, e As PaintEventArgs) Handles PicureBox1.Paint

    Dim canvas As Control = DirectCast(sender, Control)

    If DrawingRects.Count > 0 Then
        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias
        For Each rect As DrawingRectangle In DrawingRects
            If canvas IsNot rect.Owner Then Continue For
            Using pen As New Pen(rect.DrawingcColor, rect.PenSize)
                e.Graphics.DrawRectangle(pen, New Rectangle(rect.Location, rect.Size))
            End Using
        Next
    End If
End Sub

Класс DrawingRectangle:

Примечание: класс имеет свойство Owner, ссылающееся на текущий элемент управления, на котором нарисована форма: это позволяет использовать List(Of DrawingRectangle) с различными элементами управления нав то же время.

Public Class DrawingRectangle
    Private rectAspect As SizeF = SizeF.Empty
    Private rectRatio As Single = 0F

    Public Property Owner As Control
    Public Property Location As Point
    Public Property Size As Size
    Public Property StartPosition As Point
    Public Property DrawingcColor As Color
    Public Property PenSize As Single

    Public Property Aspect() As SizeF
        Get
            Return rectAspect
        End Get
        Set(ByVal value As SizeF)
            Me.rectAspect = value
            SetAspectRatio(value)
        End Set
    End Property

    Public Property Ratio As Single
        Get
            Return rectRatio
        End Get
        Set(ByVal value As Single)
            rectRatio = value
            SetAspectRatio(value)
        End Set
    End Property

    Private Sub SetAspectRatio(aspect As SizeF)
        Me.rectRatio = aspect.Width / aspect.Height
    End Sub
    Private Sub SetAspectRatio(ratio As Single)
        Me.rectAspect = New SizeF(100, 100 / ratio)
    End Sub
End Class
0 голосов
/ 15 февраля 2019

У вас есть образец прямоугольника S с заданным соотношением и размерами sw, sh (320x200 в вашем примере)

Позиции мыши образуют новый прямоугольник N с размерами nw, nh (абсолютные значения!)

Ваша задача, насколько я понимаю, вписать прямоугольник с тем же соотношением, что и S, в прямоугольник N, получив прямоугольник R с базовой точкой (rx0, ry0) и размерами (rw, rh)

   nx0 = min(e.x, startpos.x) 
   ny0 = min(e.y, startpos.y) 
   nw = abs(e.x - startpos.x)
   nh = abs(e.y - startpos.y) 

   if nw * sh >= nh * sw then   //    if N is "too wide"
        rh = nh
        rw = rh * sw / sh
        ry0 = ny0                   
        rx0 = nx0 + (nw - rw) / 2
   else                        //      N is "too slim"
        rw = nw
        rh = rw * sh / sw
        rx0 = nx0
        ry0 = ny0 + (nh - rh) / 2

, а затем

  mRect = New Rectangle(rx0,  ry0, rx0 + rw, ry0 + rh)

enter image description here

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...