捕捉网格鼠标锁定

发布于 2024-07-10 07:41:17 字数 2267 浏览 11 评论 0原文

我正在开发一个绘制简单点网格的应用程序。 我希望鼠标在网格上的点之间捕捉,最终在网格上绘制线条。

我有一个方法,它接受当前鼠标位置(X,Y)并计算最近的网格坐标。

当我创建一个事件并尝试将鼠标移动到新坐标时,整个系统变得不稳定。 鼠标在网格点之间不能平滑地捕捉。

我复制了下面的代码示例来说明我正在尝试做什么。 有谁可以向我提供有关如何消除鼠标移动中的跳跃的建议吗?


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GridTest
{
    public partial class Form1 : Form
    {
        Graphics g;
        const int gridsize = 20;

        public Form1()
        {
            InitializeComponent();
            g = splitContainer1.Panel2.CreateGraphics();
            splitContainer1.Panel2.Invalidate();
        }

        private void splitContainer1_Panel2_Paint(object sender, PaintEventArgs e)
        {
            Drawgrid();
        }

        private void Drawgrid()
        {
            for (int x = 0; x < splitContainer1.Panel2.ClientSize.Width; x += gridsize)
            {
                for (int y = 0; y < splitContainer1.Panel2.ClientSize.Height; y += gridsize)
                { g.DrawLine(Pens.Black, new Point(x, y), new Point(x + 1, y)); }
            }
        }

        private void splitContainer1_Panel2_MouseMove(object sender, MouseEventArgs e)
        {
            Point newPosition = new Point();
            newPosition = RoundToNearest(gridsize, e.Location);
            Cursor.Position = splitContainer1.Panel2.PointToScreen(newPosition);
        }

        private Point RoundToNearest(int nearestRoundValue, Point currentPoint)
        {
            Point newPoint = new Point();
            int lastDigit;

            lastDigit = currentPoint.X % nearestRoundValue;

            if (lastDigit >= (nearestRoundValue/2))
            { newPoint.X = currentPoint.X - lastDigit + nearestRoundValue; }
            else
            { newPoint.X = currentPoint.X - lastDigit; }

            lastDigit = currentPoint.Y % nearestRoundValue;
            if (lastDigit >= (nearestRoundValue / 2))
            { newPoint.Y = currentPoint.Y - lastDigit + nearestRoundValue; }
            else
            { newPoint.Y = currentPoint.Y - lastDigit; }

            return newPoint;
        }
    }
}

I am working on an application which draws a simple dot grid. I would like the mouse to snap between the points on the grid, eventually to draw lines on the grid.

I have a method which takes in the current mouse location (X,Y) and calculates the nearest grid coordinate.

When I create an event and attempt to move the mouse to the new coordinate the whole system becomes jerky. The mouse doesn't snap smoothly between grid points.

I have copied a code sample below to illustrate what I am attempting to do. Does anyone have any advice they could offer me as to how I can eliminate the jumpiness within the mouse movement?


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace GridTest
{
    public partial class Form1 : Form
    {
        Graphics g;
        const int gridsize = 20;

        public Form1()
        {
            InitializeComponent();
            g = splitContainer1.Panel2.CreateGraphics();
            splitContainer1.Panel2.Invalidate();
        }

        private void splitContainer1_Panel2_Paint(object sender, PaintEventArgs e)
        {
            Drawgrid();
        }

        private void Drawgrid()
        {
            for (int x = 0; x < splitContainer1.Panel2.ClientSize.Width; x += gridsize)
            {
                for (int y = 0; y < splitContainer1.Panel2.ClientSize.Height; y += gridsize)
                { g.DrawLine(Pens.Black, new Point(x, y), new Point(x + 1, y)); }
            }
        }

        private void splitContainer1_Panel2_MouseMove(object sender, MouseEventArgs e)
        {
            Point newPosition = new Point();
            newPosition = RoundToNearest(gridsize, e.Location);
            Cursor.Position = splitContainer1.Panel2.PointToScreen(newPosition);
        }

        private Point RoundToNearest(int nearestRoundValue, Point currentPoint)
        {
            Point newPoint = new Point();
            int lastDigit;

            lastDigit = currentPoint.X % nearestRoundValue;

            if (lastDigit >= (nearestRoundValue/2))
            { newPoint.X = currentPoint.X - lastDigit + nearestRoundValue; }
            else
            { newPoint.X = currentPoint.X - lastDigit; }

            lastDigit = currentPoint.Y % nearestRoundValue;
            if (lastDigit >= (nearestRoundValue / 2))
            { newPoint.Y = currentPoint.Y - lastDigit + nearestRoundValue; }
            else
            { newPoint.Y = currentPoint.Y - lastDigit; }

            return newPoint;
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

哀由 2024-07-17 07:41:17

不要修改光标位置。 你不需要。

相反,绘制就好像它已捕捉到网格一样。 当用户单击某处时,只需从最近的网格点绘制线条即可。

例如,如果用户单击 (197,198),但您知道最近的点实际上是 (200,200),则只需绘制一条线到 (200,200) 而不是 (197,198)。

请不要弄乱实际光标位置。


不知道有没有办法隐藏鼠标光标。 如果有,你可以隐藏它并自己绘制它,不修改真实位置

Don't modify the cursor position. You don't need to.

Instead, draw as if it was snapped to the grid. When the user clicks somewhere, just draw the line from the nearest grid points.

For instance, if the user clicks on (197,198), but you know that the nearest point actually is (200,200), just draw a line to (200,200) instead of (197,198).

And please, don't mess with the actual cursor position.


I don't know if there's some way to hide the mouse cursor. If there is, you can hide it and draw it yourself, without modifying the real position.

谜泪 2024-07-17 07:41:17

如果您尝试移动鼠标,您的鼠标会不断捕捉到同一点 - 因为它仍然最接近该点...如果您向左移动鼠标,请将光标移动到当前点左侧的点,而不是重新计算当前。 申请其他 3 个方向...

不过我不推荐这种行为,它会引起很多刺激。 将控件对齐到网格,而不是鼠标。

Your mouse keeps snapping to the same point if you try to move it -- because it's still closest to that point... If you move the mouse left, move the cursor to the point to the left of the current one instead of recalculating at the current. Apply for the 3 other directions...

I wouldn't recommend this behaviour though, it will cause a lot of irritation. Snap your controls to the grid, not the mouse.

时光是把杀猪刀 2024-07-17 07:41:17

我想我明白你来自哪里。 在捕捉到新点之前,您只需与原始捕捉点保持一定的距离(单击鼠标左键)即可。

下面的 50 行代码说明了我的意思:
(启动一个新的 VB.NET 项目,添加一个新模块,复制并粘贴代码,添加对 System、System.drawing 和 System.Windows.Forms 的引用)


Imports System
Imports System.Drawing
Imports System.Windows.Forms

Module modSnap

    Public Const strApplicationTitle As String = "Snap Demo"
    Public frmSnap As SnapForm
    Public ptSnap, ptStart, ptEnd As Point

    Public Class SnapForm
        Inherits Form
        Public Sub New()
            Me.Text = "Snap Demo"
            Me.ClientSize = New Size(800, 600)
            Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
            Me.MaximizeBox = False
            Me.StartPosition = FormStartPosition.CenterScreen
            Me.DoubleBuffered = True
        End Sub
        Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
            MyBase.OnPaint(e)
            e.Graphics.Clear(Color.Black)
            For row As Integer = 20 To 780 Step 20
                For col As Integer = 20 To 580 Step 20
                    e.Graphics.DrawEllipse(Pens.Blue, New Rectangle(row - 2, col - 2, 4, 4))
                Next
            Next
            e.Graphics.DrawLine(Pens.Red, ptStart, ptEnd)
        End Sub
        Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseDown(e)
            Dim x As Integer = CInt(e.X / 20) * 20
            Dim y As Integer = CInt(e.Y / 20) * 20
            ptStart = New Point(x, y)
            ptSnap = New Point(x, y)
            Windows.Forms.Cursor.Position = Me.PointToScreen(ptSnap)
        End Sub
        Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseMove(e)
            If e.Button = Windows.Forms.MouseButtons.Left Then
                Dim x As Integer = CInt(e.X / 20) * 20
                Dim y As Integer = CInt(e.Y / 20) * 20
                ' must be some delta away from original snap point
                If (x < ptSnap.X - 15 Or x > ptSnap.X + 15) Or (y < ptSnap.Y - 15 Or y > ptSnap.Y + 15) Then
                    ptSnap = New Point(x, y)
                    ptEnd = New Point(x, y)
                    Me.Invalidate(False)
                    Windows.Forms.Cursor.Position = Me.PointToScreen(ptSnap)
                End If
            End If
        End Sub
    End Class

    Public Sub main()
        Try
            frmSnap = New SnapForm
            Application.Run(frmSnap)
        Catch ex As Exception
            MessageBox.Show(ex.Message, strApplicationTitle, MessageBoxButtons.OK, MessageBoxIcon.Error)
        Finally
            frmSnap.Dispose()
        End Try
    End Sub

End Module

I think I understand where you're coming from. You simply need to be some delta away from the original snap point (the left mouse click) before you snap to the new point.

Here's 50 lines of code illustrating what I mean:
(Start a new VB.NET project, add a new module, copy and paste the code, add a reference, to System, System.drawing, and System.Windows.Forms)


Imports System
Imports System.Drawing
Imports System.Windows.Forms

Module modSnap

    Public Const strApplicationTitle As String = "Snap Demo"
    Public frmSnap As SnapForm
    Public ptSnap, ptStart, ptEnd As Point

    Public Class SnapForm
        Inherits Form
        Public Sub New()
            Me.Text = "Snap Demo"
            Me.ClientSize = New Size(800, 600)
            Me.FormBorderStyle = Windows.Forms.FormBorderStyle.FixedSingle
            Me.MaximizeBox = False
            Me.StartPosition = FormStartPosition.CenterScreen
            Me.DoubleBuffered = True
        End Sub
        Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
            MyBase.OnPaint(e)
            e.Graphics.Clear(Color.Black)
            For row As Integer = 20 To 780 Step 20
                For col As Integer = 20 To 580 Step 20
                    e.Graphics.DrawEllipse(Pens.Blue, New Rectangle(row - 2, col - 2, 4, 4))
                Next
            Next
            e.Graphics.DrawLine(Pens.Red, ptStart, ptEnd)
        End Sub
        Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseDown(e)
            Dim x As Integer = CInt(e.X / 20) * 20
            Dim y As Integer = CInt(e.Y / 20) * 20
            ptStart = New Point(x, y)
            ptSnap = New Point(x, y)
            Windows.Forms.Cursor.Position = Me.PointToScreen(ptSnap)
        End Sub
        Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
            MyBase.OnMouseMove(e)
            If e.Button = Windows.Forms.MouseButtons.Left Then
                Dim x As Integer = CInt(e.X / 20) * 20
                Dim y As Integer = CInt(e.Y / 20) * 20
                ' must be some delta away from original snap point
                If (x < ptSnap.X - 15 Or x > ptSnap.X + 15) Or (y < ptSnap.Y - 15 Or y > ptSnap.Y + 15) Then
                    ptSnap = New Point(x, y)
                    ptEnd = New Point(x, y)
                    Me.Invalidate(False)
                    Windows.Forms.Cursor.Position = Me.PointToScreen(ptSnap)
                End If
            End If
        End Sub
    End Class

    Public Sub main()
        Try
            frmSnap = New SnapForm
            Application.Run(frmSnap)
        Catch ex As Exception
            MessageBox.Show(ex.Message, strApplicationTitle, MessageBoxButtons.OK, MessageBoxIcon.Error)
        Finally
            frmSnap.Dispose()
        End Try
    End Sub

End Module

心是晴朗的。 2024-07-17 07:41:17

我同意 Ruijoel 的观点,不要弄乱光标位置。
最好在捕捉点处绘制一个十字或一个环,以向用户显示单击事件时将捕捉到哪个点。

为了使其正常工作,您可能需要查看异或绘图,以便一旦移动到新的捕捉点,该项目就会被删除。

I agree with ruijoel, do not mess with the cursor position.
It is better to have a cross or a ring that is drawn at the snap point to show the user which point is the one that will be snapped to at a click event.

For this to work well you may want to look at xor-drawing so that item is erased once you move to a new snap point.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文