I have two points left and right by adding some static value to Y and X is AVG of both left and right I have create 3rd point centre
After that I draw curve using bezier path between them And all works fine.
In above curve
Now I want to create two more points (5 Points curve).
One Between centre and left and one between centre and right .
I tried to take again Average and draw 5 Point curve using
func drawFivePoint(_ startPoint: CGPoint?, leftCenterPoint: CGPoint?, toControlPoint controlPoint: CGPoint?, toRightControlPoint rightPoint: CGPoint?, toEnd endPoint: CGPoint?) {
var arrPoints = [NSValue]()
if startPoint != nil {
arrPoints.append(NSValue(cgPoint: startPoint!))
}
if leftCenterPoint != nil && !(__CGPointEqualToPoint(leftCenterPoint!, .zero)) {
arrPoints.append(NSValue(cgPoint: leftCenterPoint!))
}
if controlPoint != nil {
arrPoints.append(NSValue(cgPoint: controlPoint!))
}
if rightPoint != nil && !(__CGPointEqualToPoint(rightPoint!, .zero)) {
arrPoints.append(NSValue(cgPoint: rightPoint!))
}
if endPoint != nil {
arrPoints.append(NSValue(cgPoint: endPoint!))
}
guard let bezierPath = UIBezierPath.interpolateCGPoints(withHermite: arrPoints, closed: false) else {
print("path is nil")
return
}
curveSize = bezierPath.bounds
let strokeColor = UIColor.white
if curveLayer != nil {
curveLayer?.removeFromSuperlayer()
curveLayer = nil
}
curveLayer = CAShapeLayer()
curveLayer?.lineWidth = 1.0 / self.zoomScale
curveLayer?.fillColor = UIColor.clear.cgColor
curveLayer?.path = bezierPath.cgPath
curveLayer?.strokeColor = strokeColor.cgColor
viewBase.layer.addSublayer(curveLayer!)
}
WRONG RESULT
Question : How To Calculate Points so shape is not affect and I get 5 Points on curve
it is hard to say, what are you trying to achieve, but if your goal is to find points which are part of the Bezier curve which you defined in the first step, see how the Bezier curve is defined first. Wikipedia. Generally, only first and last control point of Bezier curve is also the part of the curve.
To find the point P(t) on the Bezier curve for a particular t (0..1) you can use De Casteljau's Algorithm
Try this simple snippet in your playground. I choose control points so, that x is linearly dependant on Bezier t parameter. The shape of y has the same shape as Bezier curve and is easy to see it as a graph of values in the playground
//: Playground - noun: a place where people can play
// point
struct Point {
var x: Double
var y: Double
}
// linear bezier
func linearBezier(p1: Point, p2: Point, t: Double)->Point {
let px = p1.x + t*(p2.x - p1.x)
let py = p1.y + t*(p2.y - p1.y)
return Point(x: px, y: py)
}
// quadratic bezier
func quadraticBezier(p1: Point, p2: Point, p3: Point, t: Double)->Point {
let p12 = linearBezier(p1: p1, p2: p2, t: t)
let p23 = linearBezier(p1: p2, p2: p3, t: t)
return linearBezier(p1: p12, p2: p23, t: t)
}
// cubic bezier
func cubicBezier(p1: Point, p2: Point, p3: Point, p4: Point, t: Double)->Point {
let p12 = linearBezier(p1: p1, p2: p2, t: t)
let p23 = linearBezier(p1: p2, p2: p3, t: t)
let p34 = linearBezier(p1: p3, p2: p4, t: t)
return quadraticBezier(p1: p12, p2: p23, p3: p34, t: t)
}
let p1 = Point(x: 0.0, y: 0.0)
let p2 = Point(x: 15.0, y: 10.0)
let p3 = Point(x: 30.0, y: 5.0)
for t in stride(from: 0.0, through: 1.0, by: 0.025) {
let p = quadraticBezier(p1: p1, p2: p2, p3: p3, t: t)
print(p.x, p.y)
p.x
p.y // see the values as a graph
}
let p4 = Point(x: 45.0, y: 10.0)
for t in stride(from: 0.0, through: 1.0, by: 0.025) {
let p = cubicBezier(p1: p1, p2: p2, p3: p3, p4: p4, t: t)
print(p.x, p.y)
p.x
p.y // see the values as a graph
}
One could further do it more generic
func bezier(controlPoints: [Point], t: Double)->[Point] {
if controlPoints.count == 1 { return controlPoints }
var reducedPoints: [Point] = []
for i in 0..<(controlPoints.count - 1) {
let p = linearBezier(p1: controlPoints[i], p2: controlPoints[i+1], t: t)
reducedPoints.append(p)
}
return bezier(controlPoints: reducedPoints, t: t)
}
let points = [p1,p2,p3,p4]
for t in stride(from: 0.0, through: 1.0, by: 0.0125) {
let p = bezier(controlPoints: points, t: t)
p.count // it is alway 1 :-)
p[0].x
p[0].y
}
which gives you the same results. The function bezier
could be used for 'any' number of control points. Take in the account, that the bezier function is valid only for t in the interval (0...1.0) by the definition, even though you can calculate the values in any interval.