Created
March 23, 2020 12:37
-
-
Save fewlinesofcode/7e3b398d840c4baa28e18a41d6cec4e0 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import UIKit | |
extension UIBezierPath { | |
private static func addCorner( | |
_ path: UIBezierPath, | |
p1: CGPoint, | |
p: CGPoint, | |
p2: CGPoint, | |
radius: CGFloat, | |
isStart: Bool = false | |
) { | |
// Override `radius` with maximum available radius | |
// which is the radius of the circle inscribed into triangle defined by our points | |
var radius = min( | |
radiusOfCircle(inscribedInto: (p1, p, p2)), | |
radius | |
) | |
// Find the angle defined by our points | |
let angle = CGFloat(atan2(p.y - p1.y, p.x - p1.x) - atan2(p.y - p2.y, p.x - p2.x)).positiveAngle | |
// Get the length of segment between angular point and the points of intersection with the circle. | |
var tangentLength = radius / abs(tan(angle / 2)) | |
// Check the length of segment and the minimal length from | |
let p_p1 = p.distance(to: p1) | |
let p_p2 = p.distance(to: p2) | |
// Update `tangentLenghth` according to the smaller triangle side | |
tangentLength = min(tangentLength, min(p_p1, p_p2)) | |
// Update `radius` according to the smaller triangle side | |
radius = tangentLength * abs(tan(angle / 2)) | |
// Find distance from angle vertex to circle origin | |
let p_o = sqrt(radius.sqr + tangentLength.sqr) | |
// Segment intersected by parallel lines is divided keeping proportion | |
// Project angle sides onto axis and calculate circle origin from the proportion | |
let c1 = CGPoint( | |
x: (p.x - (p.x - p1.x) * tangentLength / p_p1), | |
y: (p.y - (p.y - p1.y) * tangentLength / p_p1) | |
) | |
let c2 = CGPoint( | |
x: (p.x - (p.x - p2.x) * tangentLength / p_p2), | |
y: (p.y - (p.y - p2.y) * tangentLength / p_p2) | |
) | |
let dx = p.x * 2 - c1.x - c2.x | |
let dy = p.y * 2 - c1.y - c2.y | |
let p_c = (dx.sqr + dy.sqr).squareRoot() | |
// Find Circle origin | |
let o = CGPoint( | |
x: p.x - dx * p_o / p_c, | |
y: p.y - dy * p_o / p_c | |
) | |
// Find start and end angle (required for Arc drawing) | |
let startAngle = (atan2((c1.y - o.y), (c1.x - o.x))).positiveAngle | |
let endAngle = (atan2((c2.y - o.y), (c2.x - o.x))).positiveAngle | |
if isStart { | |
path.move(to: c1) | |
} else { | |
path.addLine(to: c1) | |
} | |
path.addArc(withCenter: o, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: angle < .pi) | |
} | |
public static func roundedCornersPath(_ pts: [CGPoint], _ r: CGFloat) -> UIBezierPath? { | |
guard pts.isEmpty == false else { | |
return nil | |
} | |
let path = UIBezierPath() | |
for i in 1...pts.count { | |
let prev = pts[i-1] | |
let curr = pts[i % pts.count] | |
let next = pts[(i + 1) % pts.count] | |
addCorner(path, p1: prev, p: curr, p2: next, radius: r, isStart: i == 1) | |
} | |
path.close() | |
return path | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment