Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect curve #6

Open
wojciech-kulik opened this issue Nov 10, 2018 · 4 comments
Open

Incorrect curve #6

wojciech-kulik opened this issue Nov 10, 2018 · 4 comments

Comments

@wojciech-kulik
Copy link

wojciech-kulik commented Nov 10, 2018

Hi,

There is some issue with this code, it doesn't work well with all data. Please replace your points with:
"100, 200, 300, 120, 400, 350, 350, 170, 200".

You will see this:
simulator screen shot - iphone 8 - 2018-11-10 at 18 45 24

Top of the curve should be exactly in point, not above/below. Do you know how to fix it?

@roussetjc
Copy link

I tried to modify the code for a closed curve and I also have a problem. Couldn't find where it is yet though. I keep digging!

@DominicHolmes
Copy link

pretty sure this isn't solvable with Bezier curves -- afaik they are an approximation, so they'll never match the data exactly. You can get closer with higher-order curves, but they get computationally very expensive.

@wojciech-kulik
Copy link
Author

wojciech-kulik commented Jun 26, 2019

It is solvable. The curve doesn't have to be pixel perfect, however as you can see on the screenshot the curve doesn't have turns in the middle of points.

I ended up with this code and it works perfectly fine ;)

(C# code)

    public static class BezierCurve
    {
        public static UIBezierPath GetCurvedPath(CGPoint[] points)
        {
            var path = new UIBezierPath();
            if (points.Length == 0)
            {
                return path;
            }

            var p1 = points[0];
            path.MoveTo(p1);

            if (points.Length == 2)
            {
                path.AddLineTo(points[1]);
                return path;
            }

            for (int k = 1; k < points.Length; k++)
            {
                var p2 = points[k];
                var midPoint = MidPointFor(p1, p2);

                path.AddQuadCurveToPoint(midPoint, ControlPointFor(midPoint, p1));
                path.AddQuadCurveToPoint(p2, ControlPointFor(midPoint, p2));

                p1 = p2;
            }

            return path;
        }

        private static CGPoint MidPointFor(CGPoint p1, CGPoint p2)
        {
            return new CGPoint((p1.X + p2.X) / 2, (p1.Y + p2.Y) / 2);
        }

        private static CGPoint ControlPointFor(CGPoint p1, CGPoint p2)
        {
            var controlPoint = MidPointFor(p1, p2);
            var diffY = (nfloat)Math.Abs(p2.Y - controlPoint.Y);

            if (p1.Y < p2.Y)
                controlPoint.Y += diffY;
            else if (p1.Y > p2.Y)
                controlPoint.Y -= diffY;

            return controlPoint;
        }
    }

@snowtema
Copy link

snowtema commented May 9, 2021

I can confirm, @wojciech-kulik's solution works fine.
Here is swift version:

func path() -> UIBezierPath {
    ...
    let linePath = UIBezierPath()
    guard graphPoints.count > 0 else { return }
        
    var p1: CGPoint = graphPoints[0]
    linePath.move(to: p1)
        
    for i in 1..<graphPoints.count {
        let p2 = graphPoints[i]
        let midPoint = midPointFor(p1: p1, p2: p2)
            
        linePath.addQuadCurve(to: midPoint, controlPoint: controlPointFor(p1: midPoint, p2: p1))
        linePath.addQuadCurve(to: p2, controlPoint: controlPointFor(p1: midPoint, p2: p2))
            
        p1 = p2
    }
    return linePath
}
private func midPointFor(p1: CGPoint, p2: CGPoint) -> CGPoint {
    return CGPoint(x: (p1.x + p2.x) / 2, y: (p1.y + p2.y) / 2)
}

private func controlPointFor(p1: CGPoint , p2: CGPoint ) -> CGPoint {
    var controlPoint = midPointFor(p1: p1, p2: p2)
    var diffY = abs(p2.y - controlPoint.y)
    
    if (p1.y < p2.y) {
        controlPoint.y += diffY
    } else if (p1.y > p2.y) {
        controlPoint.y -= diffY
    }
    
    return controlPoint
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants