使用比率绘制和缩放矩形 [英] Drawing and scaling rectangle using a ratio
问题描述
我有一个程序,我可以用鼠标在四个方向中任意一个方向绘制一个矩形.
这些矩形在图片框上用于裁剪图像的一部分.
绘制这些矩形时必须保持给定尺寸的比例,例如 320 x 200.
我希望这个工具的行为与 Photoshop 中的裁剪工具非常相似,或者类似于此处的裁剪示例:
当 Ratio 应用于 Rectangle 尺寸时,我提出了一种稍微不同的方法来计算光标的当前位置.
您当然需要使用 Control 的 MouseDown 事件保存 Rectangle 的起始位置,然后使用 MouseMove 事件跟踪鼠标移动.
光标的当前位置照常计算(当偏移量为负时交换当前光标位置和起始位置).
唯一的区别是矩形的高度,当整体尺寸受到比率约束时.
在这种情况下,Rectangle.Location.Y
由 Rectangle.Width/Ratio
度量确定.如果 Cursor.Location.Y
高于 起始位置 (Cursor.Location.Y <= 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) 处理 PicureBox1.MouseDownIf e.Button = MouseButtons.Left ThenDrawingRects.Add(New DrawingRectangle() With {.DrawingcColor = 颜色.LightGreen,.Location = e.Location,.Owner = CType(发件人,控制),.比率 = 1.6,.Size = Size.Empty,.StartPosition = e.Location})万一结束子Private Sub PicureBox1_MouseMove(sender As Object, e As MouseEventArgs) 处理 PicureBox1.MouseMoveIf e.Button = MouseButtons.Left ThenDim rect As DrawingRectangle = DrawingRects.Last()如果 e.X 0 然后e.Graphics.SmoothingMode = SmoothingMode.AntiAlias对于每个矩形作为 DrawingRectangle 在 DrawingRectsIf canvas IsNot rect.Owner Then Continue For使用笔作为新笔(rect.DrawingcColor,rect.PenSize)e.Graphics.DrawRectangle(pen, New Rectangle(rect.Location, rect.Size))结束使用下一个万一结束子
DrawingRectangle
类:
▶ 注意:该类有一个 Owner
属性,引用绘制形状的当前控件:这允许使用 List(DrawingRectangle)
同时带有不同的控件.
公共类 DrawingRectanglePrivate rectAspect As SizeF = SizeF.EmptyPrivate rectRatio As Single = 0F公共财产所有者作为控制公共物业位置作为点公共财产大小作为大小公共属性 StartPosition 作为点公共属性 DrawingcColor 作为颜色公共属性 PenSize As Single公共属性 Aspect() As SizeF得到返回 rectAspect结束获取Set(ByVal value As SizeF)Me.rectAspect = 值设置纵横比(值)结束集结束属性公有产权比例为单一得到返回 rectRatio结束获取Set(ByVal value As Single)rectRatio = 值设置纵横比(值)结束集结束属性Private Sub SetAspectRatio(aspect As SizeF)Me.rectRatio = aspect.Width/aspect.Height结束子Private Sub SetAspectRatio(ratio As Single)Me.rectAspect = New SizeF(100, 100/ratio)结束子结束类
I have a program where I can, with the mouse, draw a rectangle in any of four directions.
These rectangles are used on a pictureBox to crop parts of an image.
These rectangles must be drawn while maintaining the ratio of a given dimension for example 320 x 200.
I want this tool to behave pretty much exactly like the crop tool in Photoshop, or like in the crop example found here: https://imageresize.org/
I have most elements working correctly I'm just struggling on a few geometric calculations.
See the "Bottom right" example in my code. This works perfectly and basically I just want to apply this exact formula to the other directions.
I have been playing with different calculations for hours and I just can't seem to work it out.
Here is the working code:
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
Any help would be hugely appreciated. Thanks!
Below is how things currently work, you can see things go funky when drawing in the north west region. I need to get the same behavior as the south east (or bottom right per the code) for all quadrants.
I propose a slightly different method to calculate the current position of the cursor, when a Ratio is applied to the Rectangle dimensions.
You need of course to save the starting position of the Rectangle, using the MouseDown event of a Control, then track the Mouse movements, using the MouseMove event.
The current position of the Cursor is calculated as usual (swapping the current Cursor Location the and Starting Position when the Offsets are negative).
The only difference is the Height of the Rectangle, when the overall size is subject to a Ratio constraint.
In this case, the Rectangle.Location.Y
is determined by the Rectangle.Width / Ratio
measure. This becomes visible if the Cursor.Location.Y
is above the starting location (Cursor.Location.Y <= StartingPosition.Y
). Just like in the code you posted.
For the example, I'm using a custom Rectangle class that holds all the information needed to draw a shape, with or without a specific Ratio applied to its dimensions.
▶ Note that the Ratio
is hard-coded to 1.6
: it's just to for testing, of course it can be set to anything else.
Visual sample of the results:
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
The DrawingRectangle
class:
▶ Note: the class has an Owner
property, referencing the current Control where the shape is drawn: this allows to use a List(Of DrawingRectangle)
with different controls at the same time.
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
这篇关于使用比率绘制和缩放矩形的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!