【问题标题】:Need advise - How to write a drawing program需要建议 - 如何编写绘图程序
【发布时间】:2023-04-01 08:57:01
【问题描述】:

我想编写一个程序,让用户可以绘制圆形、三角形、矩形等几何形状。

我还希望能够拖放或调整大小/移动之前绘制的形状。

  1. 我想在面板内绘制形状。看起来合理吗?
  2. 在我画了一个圆之后,它就变成了位图的一部分。当然,我确实将圈子的详细信息保存在其他对象中。

但我不明白的是如何实现以下内容:

当鼠标悬停在圆圈上时,圆圈被选中,然后用户可以使用某个键来调整/移动它的大小。

我怎么知道鼠标在圆圈上?

我需要检查鼠标坐标与所有圆形像素坐标吗?

我正在寻找更简单的解决方案。

【问题讨论】:

  • 你应该看看 Paint.net。我很确定源代码可用。
  • 仅适用于 3.36 及更早版本。对于最新版本,源代码不会被共享,因为创建者厌倦了人们在某些情况下将其伪装成自己的并在 Ebay 上出售。

标签: c# gdi+


【解决方案1】:

使用 WPF 图形和多媒体。 http://msdn.microsoft.com/en-us/library/ms752061.aspx

Windows Presentation Foundation (WPF) 包括对高质量 2-D 和 3-D 图形、动画和媒体的支持。图形平台的主要特点包括:

矢量图形支持。

硬件加速。

分辨率和设备无关的图形。

最小的屏幕重绘和集成动画系统。

它有你需要的一切——为什么要重新发明轮子?

您需要将对象本身保留为图形对象,以便它们可以响应鼠标悬停事件。将它们放入位图中后,您将不得不重新发明轮子。

例如,

这是形状对象:

http://msdn.microsoft.com/en-us/library/ms747393.aspx#shapes

这里是命中测试:

http://msdn.microsoft.com/en-us/library/ms752097.aspx

您还可以获得硬件加速、分辨率和独立于设备的图形的优势。我不认为您将能够轻松地自己实现这一点:)

【讨论】:

  • 我喜欢将对象保留为图形对象的想法。这些预定义的圆形/矩形/三角形等图形类是否具有鼠标事件的内置功能,并且还考虑了形状的笔宽?
  • 是的 - 请参阅此链接 msdn.microsoft.com/en-us/library/ms747393.aspx 您还应该获得良好的性能,因为 WPF 使用了专门的图形。所以一定要使用 WPF 而不是 windows 窗体。
【解决方案2】:

在 Office、Visio、PaintShop 等所有绘图包中,您都有 Z-Order 的概念。

你也需要这样的东西。我想知道如果您有撤消缓冲区,您是否会有大量的磁盘分页。需要考虑的事情。

撤消缓冲区很重要。除非您不想拥有撤消功能。也许你保存矢量数据或者位图。

还为正在绘制的对象下方提供缓冲区,除非您的显卡非常快地仅在 UI 上(不在内存中)进行矢量处理。取决于你在寻找什么,你必须使用什么,你想要多少(绘图)对象。双缓冲有好有坏。

对于圆形命中测试:-

isInCircle = (((cursorx-circlecentrex)*(cursorx-circlecentrex)+
  (cursory-circlecentrey)*(cursory-circlecentrey)) < circleradius)

使用平面上的像素坐标。

但遍历您的 z 顺序。您可以通过 Tab-bing 或 Shift-Tab-bing 或父对象中的对象层次结构选择一些图形包。并首先针对鼠标点击测试顶部的 Z 顺序。

如果支持在屏幕上移动圆圈和在靠近边缘时自动滚动编辑区域,则需要考虑大量坐标、计时器和缓冲区问题。状态管理真的很难。

考虑坐标变换(特别是如果编辑区域可缩放/可滚动并且您想要对齐网格、亚像素精度或其他功能等)。

编辑

修正 isInCircle 代码 sn-p 及其格式。

【讨论】:

    【解决方案3】:

    假设你有两个这样的三角形:

    var red = new Triangle(new Point(10, 10), new Point(30, 20), new Point(20, 50));
    var blue = new Triangle(new Point(0, 10), new Point(20, 20), new Point(10, 30));
    

    要表示图片,您可以将这些存储在列表中:

    var picture = new List<Triangle> { red, blue };
    

    当您绘制图片时,您会枚举列表并单独绘制每个三角形。因为red 在列表中位于blue 之前,所以蓝色三角形会覆盖红色三角形。

    foreach (var triangle in picture)
    {
        DrawTriangle(graphics, triangle);
    }
    

    为了让用户修改图片,您可以检测鼠标左键按下的坐标。然后你以崇高的顺序枚举列表并检查三角形的最近角。

    foreach (var triangle in picture.Reverse())
    {
        for (int i = 0; i < 3; i++)
        {
            if (Distance(mouse, triangle.Corner[i]) < 5)
            {
                // drag corner until mouse is released
                return;
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      您必须使用自定义控件并在其上绘制任何内容。您的每个形状都是具有某些属性的对象。创建一个名为IShape 的接口,然后创建几个实现它的类,例如RectangleCircle。当用户点击屏幕时,您必须通过每个对象位置比较光标位置,然后执行某些操作并使屏幕无效。

      【讨论】:

        【解决方案5】:

        您应该使用 Paint 事件在自定义双缓冲控件内绘制形状。

        例如:

        ///<summary>A blank control for drawing on.</summary>
        [DefaultEvent("Paint")]
        [Description("A blank control for drawing on.")]
        [ToolboxBitmap(typeof(Canvas), "Images.Canvas.png")]
        public class Canvas : Control {
            ///<summary>Creates a new Canvas control.</summary>
            public Canvas() {
                SetStyle(ControlStyles.AllPaintingInWmPaint
                       | ControlStyles.UserPaint
                       | ControlStyles.Opaque
                       | ControlStyles.OptimizedDoubleBuffer
                       | ControlStyles.ResizeRedraw,
                         true);
            }
        
            ///<summary>Gets or sets whether the control should completely repaint when it is resized.</summary>
            [Description("Gets or sets whether the control should completely repaint when it is resized.")]
            [DefaultValue(true)]
            [Category("Appearance")]
            public new bool ResizeRedraw {
                get { return base.ResizeRedraw; }
                set { base.ResizeRedraw = value; }
            }
        
        
            ///<summary>Raises the Paint event.</summary>
            [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "CA Bug")]
            protected override void OnPaint(PaintEventArgs e) {
                if (e == null) throw new ArgumentNullException("e");
                if (ShowDesignMessage && DesignMode) {
                    using (var brush = new LinearGradientBrush(ClientRectangle, Color.AliceBlue, Color.DodgerBlue, LinearGradientMode.Vertical)) {
                        e.Graphics.FillRectangle(brush, ClientRectangle);
                    }
                    using (var font = new Font("Segoe UI", 18))
                    using (var format = new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center }) {
                        e.Graphics.DrawString("Canvas Control", font, Brushes.DarkBlue, ClientRectangle, format);
                    }
                } else
                    base.OnPaint(e);
            }
            ///<summary>Gets whether to paint a message in design mode instead of firing the Paint event.</summary>
            [Browsable(false)]
            protected virtual bool ShowDesignMessage { get { return true; } }
        }
        

        您应该处理画布的绘制事件并使用e.Graphics 绘制所有形状对象。

        在鼠标事件中,您需要遍历所有形状并找到包含鼠标的最后一个形状。要检查一个圆是否包含鼠标,请使用勾股定理得到鼠标与圆心之间的距离。

        【讨论】:

          猜你喜欢
          • 2014-12-16
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-02-22
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多