Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
import UIKit
public extension CGPoint {
func distance(to point: CGPoint) -> CGFloat {
let xDist = x - point.x
let yDist = y - point.y
return (xDist * xDist + yDist * yDist).squareRoot()
}
}
public func radiusOfCircle(inscribedInto triangle: (pa: CGPoint, pb: CGPoint, pc: CGPoint)) -> CGFloat {
let (pa, pb, pc) = triangle
let a = pa.distance(to: pb)
let b = pb.distance(to: pc)
let c = pc.distance(to: pa)
let halfP = (a + b + c) / 2
return ((halfP - a) * (halfP - b) * (halfP - c) / halfP).squareRoot()
}
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
}
}
public extension CGFloat {
var sqr: CGFloat {
self * self
}
var positiveAngle: CGFloat {
self < 0
? self + 2 * .pi
: self
}
}
@miku1958

This comment has been minimized.

Copy link

@miku1958 miku1958 commented May 7, 2020

where is implement of CGFolat.positiveAngle?

@fewlinesofcode

This comment has been minimized.

Copy link
Owner Author

@fewlinesofcode fewlinesofcode commented May 7, 2020

@miku1958, sorry. Here it is:

public extension CGFloat {
    var positiveAngle: CGFloat {
        self < 0
            ? self + 2 * .pi
            : self
    }
}
@fewlinesofcode

This comment has been minimized.

Copy link
Owner Author

@fewlinesofcode fewlinesofcode commented May 7, 2020

@miku1958, implementation also edited.

@miku1958

This comment has been minimized.

Copy link

@miku1958 miku1958 commented May 7, 2020

thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.