Reputation: 41
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.
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.
Upvotes: 4
Views: 2972
Reputation: 217293
Let's say you have two triangles like this:
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;
}
}
}
Upvotes: 2
Reputation: 3467
You must use custom control and draw anything on that. Each of your shapes are objects that have some properties. Create an interface named IShape
and then create several classes that implement it for example Rectangle
and Circle
. When the user clicks on the screen you must compare cursor position by each object position and then do something and invalidate screen.
Upvotes: 1
Reputation: 10184
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 :)
Upvotes: 3
Reputation: 3812
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.
Upvotes: 2
Reputation: 887443
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.
Upvotes: 0