使用比例绘制和缩放矩形

时间:2019-02-15 05:14:57

标签: vb.net graphics geometry

我有一个程序,可以用鼠标在四个方向中的任何一个上绘制矩形。

这些矩形用于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 :(得分:1)

您有具有给定比率和尺寸sw, sh的示例矩形S(在您的示例中为320x200)

鼠标位置形成尺寸为nw, nh(绝对值!)的新矩形N。

据我所知,您的任务是将与S具有相同比率的矩形刻入矩形N,获得基点为(rx0, ry0)和尺寸为(rw, rh)的矩形R

   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

答案 1 :(得分:1)

当将“比率”应用于“矩形”尺寸时,我提出了一种略有不同的方法来计算光标的当前位置。

您当然需要使用Control的MouseDown事件保存Rectangle的开始位置,然后使用MouseMove事件来跟踪Mouse的运动。

照常计算光标的当前位置(当偏移量为负时,交换当前光标的位置和起始位置)。

唯一的区别是矩形的高度,而整体尺寸受“比率”约束。
在这种情况下, Rectangle.Location.Y Rectangle.Width / Ratio 度量确定。如果 Cursor.Location.Y 位于起始位置(Cursor.Location.Y <= StartingPosition.Y上方,则此位置可见。就像您发布的代码一样。

在此示例中,我使用了一个自定义的Rectangle类,该类包含绘制形状所需的所有信息,无论是否在其尺寸上应用了特定的Ratio。

  

请注意, 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 属性,引用了当前的Control   绘制形状的位置:允许将 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
相关问题