4

I'm creating a rather dense GraphicsPath by following the MouseMove event. Besides filtering during the movement, is there a routine to simplify the GraphicsPath after the fact?

I also want to implement a 'vector-based flood fill' now and this will create another really dense path.

I guess I will have to step through it and compare the directions of each line until it changes more than a limit or until the changes add up to this limit. Or I could simply erase every other point. Rather crude.

I had hoped for a built-in routine or a standard algorithm; but maybe I have not used the right search words..?

All suggestions appreciated.

TaW
  • 53,122
  • 8
  • 69
  • 111

2 Answers2

2

Reading this question I was reminded of my old post; so I decided to finally implement a simple reduction scheme:

List<PointF> ReducePath(List<PointF> points, float epsilon)
{
    if (points.Count < 3) return points;
    var newPoints = new List<PointF>();
    newPoints.Add(points[0]);
    float delta = 0f;
    float prevAngle = (float)(Angle(points[0], points[1]) /10f);
    for (int i = 1; i < points.Count - 1; i++)
    {
        float ang = Angle(points[i-1], points[i])/10f;
        delta += ang - prevAngle; 
        prevAngle = ang;
        if (Math.Abs(delta) > epsilon)
        {
            delta = 0;
            newPoints.Add(points[i]);
        }
    }
    newPoints.Add(points[ points.Count -1]);
    return newPoints;
}

float Angle(PointF p1, PointF p2)
{
    if (p1.Y == p2.Y) return p1.X > p2.Y ? 0 : 180;
    else if (p1.X == p2.X) return p1.Y > p2.Y ? 90 : 270;
    else return (float)Math.Atan((p1.Y - p2.Y)/(p1.X - p2.X));
}

//float Slope(PointF p1, PointF p2)
//{
//    if (p1.Y == p2.Y) return 0;
//    else if (p1.X == p2.X) return 12345;
//    else return (p1.Y - p2.Y)/(p1.X - p2.X);
//}

Here is a result with epsilon values of 1, 0.1 and 0.01:

enter image description here

Note the the GraphicsPath.PathPoints are read-only, so we have to re-create the path from the new points list!

Update: I have updated the math to work with 1°/10 instead of slopes, replacing the Slope function with an Angle function.. This should give more uniform results over various directions..

Update 2:

Kudos to ephraim; I have added the suggested edits to use a proper starting angle..

TaW
  • 53,122
  • 8
  • 69
  • 111
  • 1
    This doesn't help in case of y=2x , which has a big delta, but needs only 2 points... More complex Idea would compare difference between different slopes. THKS – ephraim Jun 10 '18 at 13:25
  • Not sure if your example is right but thanks for reminding me to fix the math. I hope I'll soon be able to correct it. It really ought to measure the angle properly by using tangens.. – TaW Jun 10 '18 at 13:29
  • @ephraim : Just did the math update. I'd much appreciate if you could do a few test.. – TaW Jun 10 '18 at 14:05
  • 1
    You're still suffering from the same problem. with y=2x I meant the case where you have a linear line, nothing changes between all points, that's why I added float prevAngle = (float)(Angle(points[0], points[1]) /10); before loop, and inisde: delta += ang - prevAngle; prevAngle = ang; – ephraim Jun 11 '18 at 06:24
  • 1
    Ah, you're right; thanks a lot!! - (I had this before starting the code and then decided it wasn't needed. But better is better ;-) – TaW Jun 11 '18 at 07:28
1

Have you looked into using GraphicsPath.GetBounds ? There's a nice example on that page to.

"The size of the returned bounding rectangle is influenced by the type of end caps, pen width, and pen miter limit, and therefore produces a "loose fit" to the bounded path. The approximate formula is: the initial bounding rectangle is inflated by pen width, and this result is multiplied by the miter limit, plus some additional margin to allow for end caps."

Paul Zahra
  • 9,522
  • 8
  • 54
  • 76
  • I don't have a problem with the Path's bounds, I have way too many Points on it. – TaW Mar 21 '14 at 17:38