需要建议 - 如何编写绘图程序

时间:2009-12-30 15:28:01

标签: c# gdi+

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

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

  1. 我想在Panel中绘制形状。这看起来合理吗?
  2. 绘制圆形后,它成为Bitmap的一部分。当然,我确实将圆圈的细节保存在其他一些对象中。
  3. 但我不明白的是如何实现以下内容:

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

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

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

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

5 个答案:

答案 0 :(得分:3)

使用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

您还可以获得硬件加速,分辨率广告设备无关图形的优势。我认为你不能轻易地实现这一点:)

答案 1 :(得分:2)

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

Triangles

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;
        }
    }
}

答案 2 :(得分:2)

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

你也需要这样的东西。我想知道如果你有撤消缓冲区,你会有很多分页到磁盘。值得考虑的事情。

撤消缓冲区非常重要。除非你没有使用撤消功能。也许你保存矢量数据可能是位图。

还可以在正在绘制的对象下缓冲,无论如何,只有UI(不在内存中)才能真正快速地执行向量处理。取决于您要查找的内容,您需要使用的内容,您想要的对象数量(绘图)。双缓冲可能是好还是坏。

对于圈子命中测试: -

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

在平面上使用像素坐标。

但是要遍历你的z顺序。您可以通过Tab-bing或Shift-Tab-bing或父对象内的对象层次来选择SOme图形包。并首先针对鼠标点击测试顶级Z-order。

如果支持在屏幕上移动圆圈并在靠近边缘时自动滚动编辑区域,则需要考虑许多坐标,计时器和缓冲区。国家管理真的很难。

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

修改

修复了isInCircle代码段及其格式。

答案 3 :(得分:1)

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

答案 4 :(得分:0)

您应该使用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处理画布'paint事件并绘制所有形状对象。

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