【问题标题】:Drawing and scaling rectangle using a ratio使用比率绘制和缩放矩形
【发布时间】:2021-07-08 10:15:09
【问题描述】:

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

这些矩形在图片框上用于裁剪图像的一部分。

绘制这些矩形时必须保持给定尺寸的比例,例如 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

任何帮助将不胜感激。谢谢!

以下是目前的工作方式,您可以看到在西北地区绘图时情况变得很时髦。对于所有象限,我需要获得与东南部(或代码右下角)相同的行为。

【问题讨论】:

    标签: vb.net winforms graphics geometry


    【解决方案1】:

    当 Ratio 应用于 Rectangle 尺寸时,我提出了一种稍微不同的方法来计算光标的当前位置。

    您当然需要使用 Control 的 MouseDown 事件保存 Rectangle 的起始位置,然后使用 MouseMove 事件跟踪鼠标移动。

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

    唯一的区别是矩形的高度,当整体大小受比率约束时。
    在这种情况下,Rectangle.Location.YRectangle.Width / Ratio 度量确定。如果 Cursor.Location.Y 位于上方 起始位置 (Cursor.Location.Y &lt;= StartingPosition.Y),这将变得可见。就像您发布的代码一样。

    例如,我使用了一个自定义的 Rectangle 类,该类包含绘制形状所需的所有信息,无论是否应用了特定的比率应用于其尺寸。

    ▶ 请注意,Ratio 被硬编码为1.6:它只是用于测试,当然它可以设置为其他任何值。

    结果的视觉样本:

    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
    

    【讨论】:

    • 这是一个完美的解决方案,做了一些小小的调整,我已经按照我需要的方式实现了它。我从中学到了很多 - 非常感谢您抽出宝贵的时间!
    【解决方案2】:

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

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

    据我了解,您的任务是将具有与 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)
    

    【讨论】:

    • 感谢您的回复!我最初不确定如何将它应用到我的示例中 - 但我会研究它,看看我是否可以解决它!
    • 看例子
    • 你的例子效果很好,虽然它不是我需要的行为。这是我的错,因为我发现很难正确解释我需要什么。我添加了更多信息并提供了一个 GIF,显示了我前进的方向。最重要的是 originX 和 originY 需要保持固定!
    • 是的,我将原点固定在中心(鼠标按下时),然后进行圆周移动和绘图(鼠标移动),移动光标总是在灰色矩形的最远角。所以目前我认为这与您的 gif 显示的行为相同。
    猜你喜欢
    • 2012-07-02
    • 2016-02-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多