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.
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).
Take all 3 points a, b, c in function.
Transform all 3 points a, b, c to the origin with respect to point a.
Find whether the 3rd point c lies left or right.
Rotate the 2nd point b to coincide the x-axis.
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.
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):
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):
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):
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):
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
"Reached the Max Number of Texture Atlases, Can Not Allocate More" Using Google Maps
Problems with Layout of Some Rows in Swiftui List
Wkwebview Allowslinkpreview to False Breaks Text Selection
How to Make a Countdown Timer Like in a Music Player
Collide Type Source Error - Spritekit Swift Game
Notification in Swift Every Day at a Set Time
Auto Layout How to Hide 1 View in a View with 3 Equal Width Views
Retrieve All Child Value to a Label in Table View Cell
How to Make a Phonegap App for iOS Without Mac
-Webkit-Overflow-Scrolling: Touch' Broken for Initially Offscreen Elements in iOS7
iOS 7 Weather App Expand/Collapse Transition
Filter Array of Custom Objects in Swift
Strange Custom Background Color on Uipickerview Swift
How to Make a Reusable Tableview for Different Screens in the Same Application
iOS 8 Code Working on iPhone 5S But Not iPhone 5
Realm Crashes with Rlmexception: Object Has Been Deleted or Invalidated
Detecting Double and Single Tap on Uitableviewcell to Perform Two Action