user1001753
user1001753

Reputation: 51

Smoothing a 2d line from an array of points

When a user draws, I capture the position of their input every time the input event fires and then draw straight lines between each point.

Unfortunately, this creates a very jagged look, and it gets worse depending on how quickly the user moves their input relative to how quickly the input event fires.

What I'd like is a function that takes an array of points and returns an array of points that have been smoothed out, both in terms of filling any large gaps between points, but also removing jagged edges and replacing them with smooth curves.

I know this is a well known problem, just haven't had any luck forming search queries to find what I'm looking for.

Thanks!

Bonus points if the function is written in c# already :)

Thanks!

Upvotes: 3

Views: 14591

Answers (4)

Mohsen Najafzadeh
Mohsen Najafzadeh

Reputation: 421

Extension method for float array c# (sample)

  public static NoiseReduction(this float[] src, int severity = 1)
    {
        for (int i = 1; i < src.Length; i++)
        {
            //---------------------------------------------------------------avg
            var start = (i - severity > 0 ? i - severity : 0);
            var end = (i + severity < src.Length ? i + severity : src.Length);

            float sum = 0;

            for (int j = start; j < end; j++)
            {
                sum += src[j];
            }

            var avg = sum / (end - start);
            //---------------------------------------------------------------
            src[i] = avg;

        }
    }

Upvotes: 11

Dave
Dave

Reputation: 29

Thanks folks! I've written the following function based on the links posted. It cleans up any points spaced too close together then uses Catmull-Rom to smooth the list of points.

    public static void Smooth(this List<Vector2> pointList)
    {
        List<Vector2> smoothedPoints = new List<Vector2>();

        for (int i = 1; i < pointList.Count; i++)
        {
            if (Vector2.Distance(pointList[i - 1], pointList[i]) < 30f)
            {
                pointList.RemoveAt(i);
                i--;
            }
        }

        if (pointList.Count < 4) return;

        smoothedPoints.Add(pointList[0]);

        for (int i = 1; i < pointList.Count - 2; i++)
        {
            smoothedPoints.Add(pointList[i]);

            smoothedPoints.Add(Vector2.CatmullRom(pointList[i - 1], pointList[i], pointList[i + 1], pointList[i + 2], .5f));
            //smoothedPoints.Add(Vector2.CatmullRom(pointList[i - 1], pointList[i], pointList[i + 1], pointList[i + 2], .2f));
            //smoothedPoints.Add(Vector2.CatmullRom(pointList[i - 1], pointList[i], pointList[i + 1], pointList[i + 2], .3f));
            //smoothedPoints.Add(Vector2.CatmullRom(pointList[i - 1], pointList[i], pointList[i + 1], pointList[i + 2], .7f));
            //smoothedPoints.Add(Vector2.CatmullRom(pointList[i - 1], pointList[i], pointList[i + 1], pointList[i + 2], .8f));
            //smoothedPoints.Add(Vector2.CatmullRom(pointList[i - 1], pointList[i], pointList[i + 1], pointList[i + 2], .9f));
        }

        smoothedPoints.Add(pointList[pointList.Count - 2]);
        smoothedPoints.Add(pointList[pointList.Count - 1]);

        pointList.Clear();
        pointList.AddRange(smoothedPoints);
    }

Upvotes: 2

Thymine
Thymine

Reputation: 9195

What are you using to do the drawing?

If you're using System.Drawing, could you pass the points into DrawBeziers which should anti-alias it for you fairly well.

Upvotes: 1

cli_hlt
cli_hlt

Reputation: 7164

I won't get the bonus points (is this a homework exercise? ;), but there is a solution that is quite easy to understand and implement. It's called Catmull-Rom interpolation.

Have a look here

http://en.wikipedia.org/wiki/Catmull-Rom_spline

and here

http://www.mvps.org/directx/articles/catmull/

(ignore the DirectX part on the last one)

Upvotes: 6

Related Questions