I'm having some GUI letting a user draw costimized GraphicsPath.
I've created it using the GraphicsPath AddLine function.
Now I want to implement what you can see in the attached Microsoft Word image - "Edit Points".
I'm facing several problems:
My path has hunders of "Lines"->each is only one pixel sized. I want to select only "key Points". How do I do that? It's kind of revers of "Flatten", couldn't find such a function.
Is there an existing .Net function to draw the small blue rectangles, and the small green circle around the path? and what about the rectangles around each selected point?
Each help, even partial, would be appreciated.
For the first part of your question do have a look at this post, which has a reduction function for a List<Point>
. Note that the GraphicsPath.PathPoints
collection is read-only, so you have to re-create the path from the reduced points list.
A few remarks on the second part:
There is no built-in routine to create the handles. Nor to make them do anything. So you need to code for them.
I append a simple class MoveLabel
which can be used for this. It can be placed on a control or be added to its Controls
collection. Then you can move it around. I have added a callback function MoveAction
to process the result when the mouse is released.
You can either add a ..
public delegate void Moved(MoveLabel sender);
..to the form class, or, to avoid the Form1
reference, outside of the form class but in scope for the MoveLabel
.
It can be used directly to move points in points list:
Create it on a panel:
var lab= new MoveLabel(Color.CadetBlue, 9, Point.Round(points[i]), i);
lab.Parent = panel;
lab.MoveAction = moved;
A simple processing function:
void moved(MoveLabel sender)
{
points[sender.PointIndex] =
new Point(sender.Left - sender.Width / 2, sender.Top - sender.Height / 2);
panel.Invalidate();
}
Note the the GraphicsPath.PathPoints
are read-only, so we have to re-create the path from the new points list! Actually one can modify individual PathPoints
in code, but the result do not stick; so one has to copy the PathPoints
to a PointF[]
, modify them there and re-create the path. For complex paths best by using this overload..
If you want to implement a rotation (or other transformations) you can use the GraphicsPath.Transform function. You can use moveable labels to determine the rotation or scaling data..
Here is my minimal MoveLabel
class:
public class MoveLabel : Label
{
public Form1.Moved MoveAction { get; set; }
public int PointIndex { get; set; }
private Point mDown = Point.Empty;
public MoveLabel()
{
MouseDown += (ss, ee) => { mDown = ee.Location; };
MouseMove += (ss, ee) => {
if (ee.Button.HasFlag(MouseButtons.Left))
{
Location = new Point(Left + ee.X - Width / 2, Top + ee.Y - Height / 2);
mDown = Location;
}
};
MouseUp += (ss, ee) => { if (MoveAction != null) MoveAction(this); };
}
public MoveLabel(Color c, int size, Point location, int pointIndex) : this()
{
BackColor = Color.CadetBlue;
Size = new Size(size, size);
Location = location;
PointIndex = pointIndex;
}
}
This will also work fine to move points from bezier curves. By adding a call MoveAction(this);
to the MouseMove
linq code you can get live updates.. Make sure the Panels
are DoubleBuffered for this :-)
Example:
Btw: I just came upon this post which shows how easily the curve or any other GDI+ vector drawing can be saved to emf
, preserving the vector quality!