Вместо addLine вы должны использовать addArc
. Значения для addAr c зависят от предыдущей / следующей точки. Таким образом, любая вспомогательная функция должна принимать весь массив точек, которые вы хотите использовать.
Если вы можете понять, как нарисовать кривую между двумя линиями, один и тот же алгоритм можно повторить для всей фигуры.
Чтобы упростить понимание, обратитесь к этой диаграмме (код также будет ссылаться на эти точки):
Цель состоит в том, чтобы выяснить центр круга и начальный / конечный углы.
Сначала вы должны взять расширения CGFloat и CGPoint отсюда .
Далее добавьте эти помощники Функции:
extension Collection where Index == Int {
func items(at index: Index) -> (previous: Element, current: Element, next: Element) {
precondition(count > 2)
let previous = self[index == 0 ? count - 1 : index - 1]
let current = self[index]
let next = self[(index + 1) % count]
return (previous, current, next)
}
}
/// Returns ∠abc (i.e. clockwise degrees from ba to bc)
//
// b - - - a
// \
// \
// \
// c
//
func angleBetween3Points(_ a: CGPoint, _ b: CGPoint, _ c: CGPoint) -> CGFloat {
let xbaAngle = (a - b).angle
let xbcAngle = (c - b).angle // if you were to put point b at the origin, `xbc` refers to the angle formed from the x-axis to the bc line (clockwise)
let abcAngle = xbcAngle - xbaAngle
return CGPoint(angle: abcAngle).angle // normalize angle between -π to π
}
func arcInfo(
previous: CGPoint,
current: CGPoint,
next: CGPoint,
radius: CGFloat)
-> (center: CGPoint, startAngle: CGFloat, endAngle: CGFloat, clockwise: Bool)
{
let a = previous
let b = current
let bCornerRadius: CGFloat = radius
let c = next
let abcAngle: CGFloat = angleBetween3Points(a, b, c)
let xbaAngle = (a - b).angle
let abeAngle = abcAngle / 2
let deLength: CGFloat = bCornerRadius
let bdLength = bCornerRadius / tan(abeAngle)
let beLength = sqrt(deLength*deLength + bdLength*bdLength)
let beVector: CGPoint = CGPoint(angle: abcAngle/2 + xbaAngle)
let e: CGPoint = b + beVector * beLength
let xebAngle = (b - e).angle
let bedAngle = (π/2 - abs(abeAngle)) * abeAngle.sign() * -1
return (
center: e,
startAngle: xebAngle - bedAngle,
endAngle: xebAngle + bedAngle,
clockwise: abeAngle < 0)
}
func addArcs(to path: UIBezierPath, pointsAndRadii: [(point: CGPoint, radius: CGFloat)]) {
precondition(pointsAndRadii.count > 2)
for i in 0..<pointsAndRadii.count {
let (previous, current, next) = pointsAndRadii.items(at: i)
let (center, startAngle, endAngle, clockwise) = arcInfo(
previous: previous.point,
current: current.point,
next: next.point,
radius: current.radius)
path.addArc(withCenter: center, radius: current.radius, startAngle: startAngle, endAngle: endAngle, clockwise: clockwise)
}
}
Наконец, теперь вы можете использовать эти функции для простой визуализации любого вектора, определенного в приложении для рисования:
override func draw(_ rect: CGRect) {
let grayPath = UIBezierPath()
addArcs(
to: grayPath,
pointsAndRadii: [
(point: CGPoint(x: 100, y: 203), radius: 0),
(point: CGPoint(x: 100, y: 138.62), radius: 33),
(point: CGPoint(x: 173.78, y: 100), radius: 0),
(point: CGPoint(x: 139.14, y: 172.51), radius: 17),
(point: CGPoint(x: 231, y: 203), radius: 3),
])
grayPath.close()
grayPath.lineWidth = 5
UIColor.gray.setStroke()
grayPath.stroke()
}
, что даст точную копию: