Calculate Controlpoints While Drawing in iOS

Calculate controlPoints while drawing in iOS

The problem you're having is that when the user drags his finger slowly, the control points you get are too far apart, so you get large line segments rather than the small pixel changes that would give you a smooth-looking curve.

Calcualting Bezier control points from an existing curve is really hard, since the control points are not on the line.

Better to use Catmull-Rom curves. There is an excellent "Recipe" in Erica Sadun's OUTSTANDING book "The Advanced iOS 6 Developer's Cookbook" that includes working code on Catmull-Rom spline based smoothing. Catmull-Rom splines use control points that are on the curve.

I highly recommend buying that book. The sample code for Recipe 4.3 will do exactly what you want.

how to compute the control points for a smooth path given a set of points?

The image you linked to is an example that doesn't use quadratic curves, so I'm going to run
with the image and not the code.

A bezier path on ios (and os x) underneath is basically a list of drawing commands and points. eg:

[path moveTo:CGMakePoint(1,1)];
[path curveToPoint:(10,10) controPoint1:(3,7) controlPoint2:(4,1)];
[path curveToPoint:(10,10) controPoint1:(15,17) controlPoint2:(21,11)];
[path closePath];

Results in:

moveto (1,1)      
curveto (10,10) (3,7) (4,1)
curveto (20,0) (15,17) (21,11)
closepath

Control points on a bezier path control the direction and rate of curve out of a point. The first control point(cp) controls the direction and rate of curve exiting the previous point and the second cp controls the same for the point you're curving to. For a quadratic curve (what you get using addQuadCurveToPoint:controlPoint: ), both of these points are the same, as you can see in the docs for the method here.

Getting a smooth curve along a set of points involves making cp1 and cp2 be colinear with each other and that line be parallel to the points at either end of that segment.

Annotated curve

This would look something like:

[path moveTo:2];
[path curveTo:3 controlPoint1:cp1 controlPoint2:cp2];

cp1 and cp2 can be computed by choosing some constant line length and doing some geometry (i forget all my line equations right now but they're easily googleable )

Going to use #-># to designate a segment and #->#(cp#) to designate a control point for that segment's curveto call.

The next issue is making the curve smooth from the 2->3 segment going into 3->4 segment. At this point in your code, you should have a control point calculated for 2->3(cp2). Given your constant line length from before (this will control how sharp of a curve you get), you can calculate a cp1 for 3->4 by getting a point colinear with 2->3(cp2) and point 3 in the diagram. Then calculate a 3->4(cp2) that's colinear with 3->4(cp1) and parallel to the line that point 3 and point 4 form. Rinse and repeat through your points array.

Drawing curve working for all the quadrants / Finding dynamic control points for the Bézier curve

Instead of going through the math , I figured to draw the curve perfect for all the quadrant programmatically.

The algorithm for this is as follows:
(This is an algorithm to find the control points for the Bézier curve perfect for all the quadrants that means you will get the dynamic control points for the Bézier curve.)

Problem: Given 3 points a, b, c, the task is to draw the curve at the angle abc (curve structure is fixed as shown in the figure in the question).

  1. Take all 3 points a, b, c in function.

  2. Transform all 3 points a, b, c to the origin with respect to point a.

  3. Find whether the 3rd point c lies left or right.

  4. Rotate the 2nd point b to coincide the x-axis.

  5. After step 4, you are in the zero position.

    (Here you can choose the control points for the Bézier curve like you desire. You do not have to solve any relation for the control points. You can set the control points using simple add/subtract math only.) The control points obtained here will be perfect for all the quadrants.

  6. After step 5, we get all the control points for the Bézier curve, now take all those points to the original position,

    a. First rotate point b and the two control points (by the rotation angle of b in step 4)

    b. Translate back all points to their original location (i.e. with respect to point a - reversing transformation from step 1).

Now you get the required control points for cubic Bézier suitable for all the quadrants.

Draw the curve using the Bézier curve function.

Calculating addCurveToPoint control points

I assume you want to add a 90 degree arc for the rounded corner. Use addArc instead of addCurveToPoint.

In Swift 3:

var path = UIBezierPath()

...

let center = CGPoint(x: topLeft.x + width - radius, y: topLeft.y)
path.addArc(withCenter: center, radius: radius, startAngle: CGFloat.pi, endAngle: CGFloat.pi * CGFloat(1.5), clockwise: true)

Of course, your parameters will vary.

Update

Based on your code, it should look like this:

UIBezierPath * rectangle = [UIBezierPath bezierPath];
[rectangle moveToPoint:CGPointMake(0, 8)];
[rectangle addArcWithCenter:CGPointMake(8, 8) radius:8 startAngle:M_PI endAngle:M_PI*1.5 clockwise:YES];
[rectangle addLineToPoint:CGPointMake(208, 0)];
[rectangle addArcWithCenter:CGPointMake(208, 16) radius:16 startAngle:M_PI*1.5 endAngle:0 clockwise:YES];
[rectangle addLineToPoint:CGPointMake(224, 175)];
[rectangle addArcWithCenter:CGPointMake(208, 175) radius:32 startAngle:0 endAngle:M_PI*0.5 clockwise:YES];
[rectangle addLineToPoint:CGPointMake(64, 207)];
[rectangle addArcWithCenter:CGPointMake(64, 175) radius:64 startAngle:M_PI*0.5 endAngle:M_PI clockwise:YES];
[rectangle closePath];

Calculate UIBezierPath Curved Control Point

It looks to me like a connected set of cubic Bezier curves where the beginning and end are the same point. If you watch the animation, it looks like as you drag different points, the point that's the beginning/end of the curve gets changed.

Watch carefully and you'll see that 3 of the 4 corners are smooth curves, and one has a "kink" in it. The kinked corner seems to be a point other than the one that's being moved. That's probably the begin/end point.

how to calculate number of point between three points for curve drawing?

For the first segment of the curve, you can probably use addQuadCurveToPoint, picking a control point with the same y value as the second point (and I picked an x value half way between the two end points):

part1

For the second portion of the curve, you can't use quad curve, because you need two control points (or, you'd have to break it up into two quad curves, which is more hassle than its worth, IMHO). So use addCurveToPoint, using control point y values that are the same value as the y value of the point to which the control point refers (and, again, I picked x values half way between the x values of the two end points):

part2

There are lots of permutations of this idea, but I hope this illustrates the concept. I'd suggest you start playing around with UIBezierPath and addCurveToPoint until you achieve the desired effect.

Draw Graph curves with UIBezierPath

SO I found a work around based on @Fogmeister's answer.

    UIBezierPath *path = [UIBezierPath bezierPath];
[path setLineWidth:3.0];
[path setLineCapStyle:kCGLineCapRound];
[path setLineJoinStyle:kCGLineJoinRound];

// actualPoints are my points array stored as NSValue

NSValue *value = [actualPoints objectAtIndex:0];
CGPoint p1 = [value CGPointValue];
[path moveToPoint:p1];

for (int k=1; k<[actualPoints count];k++) {

NSValue *value = [actualPoints objectAtIndex:k];
CGPoint p2 = [value CGPointValue];

CGPoint centerPoint = CGPointMake((p1.x+p2.x)/2, (p1.y+p2.y)/2);

// See if your curve is decreasing or increasing
// You can optimize it further by finding point on normal of line passing through midpoint

if (p1.y<p2.y) {
centerPoint = CGPointMake(centerPoint.x, centerPoint.y+(abs(p2.y-centerPoint.y)));
}else if(p1.y>p2.y){
centerPoint = CGPointMake(centerPoint.x, centerPoint.y-(abs(p2.y-centerPoint.y)));
}

[path addQuadCurveToPoint:p2 controlPoint:centerPoint];
p1 = p2;
}

[path stroke];

how to calculate number of point between three points for curve drawing?

For the first segment of the curve, you can probably use addQuadCurveToPoint, picking a control point with the same y value as the second point (and I picked an x value half way between the two end points):

part1

For the second portion of the curve, you can't use quad curve, because you need two control points (or, you'd have to break it up into two quad curves, which is more hassle than its worth, IMHO). So use addCurveToPoint, using control point y values that are the same value as the y value of the point to which the control point refers (and, again, I picked x values half way between the x values of the two end points):

part2

There are lots of permutations of this idea, but I hope this illustrates the concept. I'd suggest you start playing around with UIBezierPath and addCurveToPoint until you achieve the desired effect.



Related Topics



Leave a reply



Submit