使用缩放网格移动点(列表中的点)

Moving points with scaling grid (points in list)

我正在为我的学校作业做一些例子。

现在我卡在了一点。我会尽量包含尽可能少的代码,但我不确定你们都需要什么..

(抱歉在我的母语中加入了名字和评论)

这是我需要做的: 我得到了我的 "painting program" ,您可以在其中通过单击添加点,然后相互绘制线条。我编写了一个网格,当您选中一个复选框时,它会在 rastr(grid)list 中寻找最接近的可能点,然后将其坐标应用于您尝试用鼠标绘制的点。

您还可以按轨迹栏值缩放该网格。

现在我的问题来了:我需要在缩放网格时移动我绘制的点。我不知道,因为我不能简单地为 grid.X = p.X + trackbarValue 中的每个点做。 (出现错误)

这是我的网格和绘画代码:

List<Point> rastr = new List<Point>(); //rastr means grid
List<Point> body = new List<Point>(); //painting poits

   private void panel1_Paint(object sender, PaintEventArgs e)
   {
            Graphics kp = e.Graphics;
            foreach (Point p in body)
            {
                kp.FillEllipse(st, p.X-s/2, p.Y-s/2, s, s); 
            }
            //double buffer nezapomenout
            if (body.Count>1)
            {
                kp.DrawLines(pero, body.ToArray());//to array prevedeni na pole
            }
            for (int x = 0; x < panel1.Width; x += trackBarGrid.Value)
                for (int y = 0; y < panel1.Height; y += trackBarGrid.Value)
                {
                    if (showGrid == true)
                    {
                        kp.FillEllipse(grid, x-2, y-2, 4, 4);
                    }
                    Point gridpoint = new Point(x,y);
                    rastr.Add(gridpoint);
                }
        }

这里是在网格中寻找最近的一个然后应用坐标:

private void panel1_MouseClick(object sender, MouseEventArgs e)
    {
        if (useGrid == true)
        {
            //rozjedu cykly abych nasel nejblizsi bod (porovnavat jednotlivy koordinace)
            foreach (Point p in rastr)
            {
                if (e.Location.X - p.X < trackBarGrid.Value / 2) //kdyz odecteme pozici bodu v listu rastr.x od pozice x kliknuti nesmi byt vetsi nez polovina delky odsazeni rastrovych bodu
                    if (e.Location.Y - p.Y < trackBarGrid.Value / 2) // to same jen s pozici Y
                    {
                        Point zapsat = new Point(p.X, p.Y);
                        body.Add(zapsat); //od tyhle pozice najit nejblizsi point v gridu
                        break;//bod jsme uspesne nasli. Nyni musime cyklus uzavrit aby se nezacli pridavat body ktere nechceme
                    }
            }
            panel1.Refresh();
        }
        else {
            body.Add(e.Location);
            panel1.Refresh();
        }   
    }

还有我的轨迹栏事件:

    private void trackBarGrid_Scroll(object sender, EventArgs e)
    {
        rastr.Clear();
        panel1.Refresh();
    }

我在这方面的尝试很糟糕

    private void trackBarGrid_Scroll(object sender, EventArgs e)
    {
        foreach (Point p in rastr)
        {
            p.X += TrackBarValue;
            p.Y += TrackBarValue;
        }
        panel1.Refresh();
    }

最后一点,我不需要保留那幅 "freehand" 画。我可以只使用那个网格。

这是一些照片..

Before scaling

After scaling

我以某种方式设法将这些点从列表中取出,然后编辑它们并将它们放回原处。所以正如你所说,我只需要找出那些比例因子。它已经在移动但没有与网格对应。我说我对我的英语感到抱歉,所以你看我不可能再更正它了 :D 抱歉......我出去了你没有帮助我;((只是笑了一下那个专业档案笔记) 有些人 post 这里的问题甚至比这个更糟糕 :D 无论如何,thx 又有点自己解决了。 只是那个倍增的东西:)

 private void trackBarGrid_Scroll(object sender, EventArgs e)
        {
            if (trackBarGrid.Value > trackbarval)
            {
                for (int i = 0; i < body.Count; i++)
                {
                    Point point = body[i];
                    point.X += trackBarGrid.Value - trackbarval;
                    point.Y += trackBarGrid.Value - trackbarval;
                    body[i] = point;
                }
            }
            else if (trackBarGrid.Value < trackbarval)
            {
                for (int i = 0; i < body.Count; i++)
                {
                    Point point = body[i];
                    point.X -= trackbarval- trackBarGrid.Value;
                    point.Y -= trackbarval- trackBarGrid.Value ;
                    body[i] = point;
                }
            }
            rastr.Clear();
            panel1.Refresh();
            trackbarval = trackBarGrid.Value;
        }

您可以选择两种缩放图形的方式。一个非常简单,另一个有点复杂。

这是最简单的:

只需将此添加到 Paint 事件的顶部:

float scale = trackBar1.Value / 100f;
if (scale == 0) scale = 1;
e.Graphics.ScaleTransform(scale, scale);

这看起来有点复杂,但是当 Paint 事件最初被调用时,TrackBar 将没有值。

Graphics 对象本身现在已缩放,您绘制的所有内容 都将被缩放。

那真的很简单。

您可能遇到的一个可能问题是所有内容,包括 Pen 的宽度都会按比例放大或缩小。

你的问题想用更复杂的方式,所以让我们也这样做吧..:[=​​42=]

首先我们创建一个函数,根据 TrackBarValue 缩放一个点。这是:

PointF scaledPoint(PointF pt, float scale, bool unscale)
{
    if (unscale) return new PointF(pt.X / scale, pt.Y / scale);
    else return new PointF( pt.X * scale, (pt.Y * scale));
}

有两点值得注意:

  • 该函数实际上适用于 PointF 而不是 Point。这非常重要,因为缩放不能丢失 精度!从 5 下降到 33% 然后再次备份不会回到 5 而是 3..!

  • 有一个额外的参数可以移除点的缩放比例。我们暂时不需要它;但是你会的,一旦你想在缩放打开时从 MouseClick 捕捉到一个点!

现在让我们用它来缩放我们的点列表。

我们可以直接做,可能是这样的:

for (int p = 0; p < body.Count; p++)
     body[p] = scaledPoint(body[p], scale, false);

虽然这会起作用,但有一个 讨厌的 问题:当我们移动 TrackBar 时,它将 reapetedly 应用 增长 比例因子。这意味着图形会越来越大越来越快..不好。

相反,我们使用原始点值创建第二个 List<PointF> bodyO,需要保持..

这意味着我们向 bodyO 添加点,但使用 body 绘制并将 bodyO 缩放到 body..:[=​​42=]

void scalePoints(float scale)
{
    for (int p = 0; p < body.Count; p++)
        body[p] = scaledPoint(body0[p], scale, false);
}

我们在您的代码中几乎调用了:

private void trackBar1_Scroll(object sender, EventArgs e)
{
    scalePoints(trackBar1.Value / 100f);
    panel1.Refresh();
}

让我们看看这在 Paint 事件中是如何工作的:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    float scale = trackBar1.Value / 100f;
    if (scale == 0) scale = 1;

    bool showGrid = true;  // inserted for testing

    // these graphics work with the scaled points:
    foreach (PointF pt in body) e.Graphics.FillEllipse(Brushes.Red, 
                                  pt.X - 2, pt.Y - 2, scale * 4, scale * 4);
    if (body.Count > 1) e.Graphics.DrawLines(Pens.Black, body.ToArray());

    // here we don't use points but calculate the coordinates, so we need to scale
    for (int x = 0; x < panel1.Width; x += trackBar2.Value)
        for (int y = 0; y < panel1.Height; y += trackBar2.Value)
        {
            if (showGrid == true)
            {
                e.Graphics.FillEllipse(Brushes.Gray, 
                           scale * (x - 2), scale * (y - 2), scale * 4, scale * 4);
            }
            // Point gridpoint = new Point(x, y); // not sure what you do here..?
            // rastr.Add(gridpoint);   // maybe add the point scaled?
        }
}

请注意,我也缩放了点的大小。顺便说一句,要使两个版本看起来完全相同,您还必须缩放 Pen

using (Pen pen = new Pen(somecolor, penWidth * scale))

最后是在鼠标点击中添加新点的方法:

private void panel1_MouseClick(object sender, MouseEventArgs e)
{
    body0.Add(scaledPoint(e.Location, trackBar1.Value / 100f, true));
    body.Add(e.Location);
    panel1.Invalidate();
}

我们将其添加到原始列表 bodyO 中,按原样反转当前缩放比例,即将当前 'pixel size' 添加到缩放列表中..

顺便说一句:如果没有DoubleBuffering,很多网格点需要一些时间;这会产生一些闪烁。所以要么使用 Panel 子类 DoubleBuffering:

class DrawPanel : Panel
{ 
   public DrawPanel()
    { DoubleBuffered = true; }
}

或者选择 Picturebox(推荐)!