tags:

views:

276

answers:

6

I would like to write a program that enable user to draw geometric shapes like circles, triangles, rectangles and so on.

I would like also to be able to drag and drop or resize/move a shape that was previously drawn.

  1. I thought to draw the shapes inside a Panel. Does it seems reasonable ?
  2. After I draw a circle, it becomes a part of a Bitmap. Of course, I do save circle's details in some other object.

But what I don't understand is how to implement the following:

When the mouse is over the circle, the circle is chosen, and then using some key enables the user to resize/move it.

How can I know that the mouse is over the circle ?

Do I need to check the mouse coordinates vs all circle pixel coordinates ?

I'm looking for simpler solution.

+2  A: 

Use the WPF Graphics and Multi-media. http://msdn.microsoft.com/en-us/library/ms752061.aspx

Windows Presentation Foundation (WPF) includes support for high quality 2-D and 3-D graphics, animation, and media. Key features of the graphic platform include:

Vector graphic support.

Hardware acceleration.

Resolution and device-independent graphics.

Minimal screen redraw and integrated animation system.

It has everything you need - why re-invent the wheel?

You need to keep the objects as graphical objects themselves, so they can respond to mouseover events. Once you put them into a bitmap, then you are going to have to essentially re-invent the wheel.

For example,

Here's shape objects:

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

Here's hit testing:

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

You also get the advantage of hardware acceleration, resolution ad device-independent graphics. I don't think you are going to be able to implement this yourself easily :)

Larry Watanabe
I like the idea to keep the objects as graphical objects.Are these predefined graphical classes of circle/rectangle/triangle and so on that have a built-in functionality of mouse events, and also take into account the pen width of the shape ?
Yes - see this linkhttp://msdn.microsoft.com/en-us/library/ms747393.aspxYou should also get good performance since WPF makes use of specialized graphics. So definitely go with WPF rather than windows forms.
Larry Watanabe
A: 

You must use custom control and draw anything on that. each of your shapes are objects that have some properties. create a interface that named IShape and then create several classes that implement it for example Rectable and circle. when user click on the screen you must compare cursor position by each object position and then do something and invalidate screen.

Navid Farhadi
A: 

You should draw the shapes inside a custom double-buffered control using the Paint event.

For example:

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

You should handle the canvas' paint event and draw all of your shape objects using e.Graphics.

In mouse events, you'll need to loop through all of the shapes and find the last shape that contains the mouse. To check whether a circle contains the mouse, use Pythagorean's theorem to get the distance between the mouse and the center of the circle.

SLaks
+1  A: 

Let's say you have two triangles like this:

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

To represent the picture, you could store these in a list:

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

When you draw the picture, you enumerate the list and draw each triangle individually. Because red comes before blue in the list, the blue triangle overwrites the red triangle.

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

In order to let the user modify the picture, you could detect at which coordinates the left mouse button was pressed. Then you enumerate the list in revered order and check for the closest corner of a 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;
        }
    }
}
dtb
+2  A: 

In Office, Visio, PaintShop, all the drawing packages, you have the concept of Z-Order.

You need something like this too. I wonder maybe if you'll have lots of paging to disk if you have undo buffers. SOmething to consider.

Buffers for undo are important. Unless you don't wabnt to have undo feature. Maybe you save vector data maybe bitmap.

Also buffers for under the object being drawn, uinless you're graphics card is real fast to do vector on the UI only (not in memory). Depends what you looking for, what you have to work with, how many (drawing) objects you want. Double buffering may be good or bad.

For circle hit-test:-

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

Using pixel coordinates on the plane.

But iterate through your z-order. SOme graphics packages you can select by Tab-bing or Shift-Tab-bing or with hierachies of objects within a parent object. And test the top Z-orders against mouse clicks first.

If supporting move circle on screen and auto-scroll edit area when near edges, lots of coordinates, timer and buffers issues to consider. State management real hard.

Consider coordinate transforms (esopecially if edit area zoomable/scrollable and you want snap-to-grid, sub-pixel accuracy or other feature etc).

EDIT

Fixed isInCircle code snippet and its formatting.

martinr
A: 

Thanks Larry ! WPF is really what I need ! Could you suggest a good C# book/online tutorial to start with ?

moroshko