Как разрешить перемещение объекта только по линиям сетки?
Пишу на c# winforms. По кнопке отрисовываю GraphicsPath объект на собственном control (наследуюсь от UserControl), который ещё и сетку рисует 20X20 с шириной и высотой клетки 20x20. Добавил возможность его перемещать мышкой. Такой вопрос: как разрешить перемещение этого объекта только по линиям сетки (шаг = 20)? Допустим объект - прямоугольник или линия с длиной кратной ширине/высоте клетки. Нужен наглядный пример. Ниже код перемещения:
protected override void OnMouseMove(MouseEventArgs e)
{
if (moving)
{
var d = new Point(e.X - previousPoint.X, e.Y - previousPoint.Y);
selectedShape.Move(d);
previousPoint = e.Location;
this.Invalidate();
}
base.OnMouseMove(e);
}
, где previousPoint - точка нажатия (получаю в Down) и Move объекта:
public void Move(Point d)
{
Matrix translateMatrix = new Matrix();
translateMatrix.Translate(d.X, d.Y);
path_element_.Transform(translateMatrix);
path_circle_.Transform(translateMatrix);
}
Ответы (3 шт):
Как вариант:
- Допустим у вас объект в
20:40, а курсор нажали в25:44 - Запоминаете в каких координатах объект взяли (я обычно запоминаю смещение от курсора, тут это
-5:-4) - Перетаскиваете и рассчитываете новое положение объекта, Например курсор стал
14:88, значит объект должен оказаться в14-5:88-4- в9:84 - Округляете положение объекта до шага сетки. Самое простое - делить на 20, округлить, умножить на 20. Получите
0:80.
Полный пример приложения.
Копируем, запускаем.
Водим мышкой по форме - квадрат перемещается по сетке.
using System.Drawing;
using System.Windows.Forms;
namespace WinForm
{
public partial class Form1 : Form
{
int x;
int y;
const int step = 50;
public Form1()
{
//InitializeComponent();
DoubleBuffered = true;
Paint += Form1_Paint;
MouseMove += Form1_MouseMove;
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawGrid(e.Graphics, e.ClipRectangle, new Size(step, step), Color.White);
e.Graphics.FillRectangle(Brushes.Green, x, y, 50, 50);
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
int xNew = e.X / step * step;
int yNew = e.Y / step * step;
if (x != xNew || y != yNew)
{
x = xNew;
y = yNew;
Invalidate();
}
}
}
}
Если перемещать дискретно мышкой, то тогда добавьте на форму четыре кнопки навигации со стрелками и по ним щёлкайте, но лучше перемещать объект стандартно клавишами WASD и если оставлять копию, то по нажатию пробела space. Так делается в одном из редакторов террейнов в GLScene - Terrain-Node System for creation landscapes по регулярной сетке. В классе главной формы объявляем экземпляры типа: регулярной сетки - Grid2D: TGLXYZGrid; (* шаг приращения Step в XSamplingScale и YSamplingScale пусть равен 20 как у вас, ZSamplingScale 0 ); фриформы меш - Preview_Mesh: TGLFreeForm; ( перемещаемого объекта в окне порта TGLSceneViewer *); и копии объекта для отображения в окне навигации - Pre_MeshCopy: TGLFreeForm;
Затем на событие нажатия клавиши дописываем
// Moving the preview-node
if IsKeyDown('a') then
Preview_Mesh.Position.X := Preview_Mesh.Position.X + 20;
if IsKeyDown('d') then
Preview_Mesh.Position.X := Preview_Mesh.Position.X - 20;
if IsKeyDown('w') then
Preview_Mesh.Position.Z := Preview_Mesh.Position.Z + 20;
if IsKeyDown('s') then
Preview_Mesh.Position.Z := Preview_Mesh.Position.Z - 20;
...
и т.д. и т.п. При необходимости копии меш-объекта оставляем в ячейке и перемешаемся дальше или вверх-вниз.
