Skip to content

Instantly share code, notes, and snippets.

@akhilcb
Created May 7, 2017 23:22
Show Gist options
  • Star 29 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save akhilcb/8d03f1f88f87e996aec24748bdf0ce78 to your computer and use it in GitHub Desktop.
Save akhilcb/8d03f1f88f87e996aec24748bdf0ce78 to your computer and use it in GitHub Desktop.
CGPoint extension with some useful functions for angle and point calculation on arc and circles
// Taken from ACBRadialMenuView Project
// BSD 3-Clause License
// Copyright (c) 2017, akhilcb (https://github.com/akhilcb)
extension CGPoint {
static func pointOnCircle(center: CGPoint, radius: CGFloat, angle: CGFloat) -> CGPoint {
let x = center.x + radius * cos(angle)
let y = center.y + radius * sin(angle)
return CGPoint(x: x, y: y)
}
static func angleBetweenThreePoints(center: CGPoint, firstPoint: CGPoint, secondPoint: CGPoint) -> CGFloat {
let firstAngle = atan2(firstPoint.y - center.y, firstPoint.x - center.x)
let secondAnlge = atan2(secondPoint.y - center.y, secondPoint.x - center.x)
var angleDiff = firstAngle - secondAnlge
if angleDiff < 0 {
angleDiff *= -1
}
return angleDiff
}
func angleBetweenPoints(firstPoint: CGPoint, secondPoint: CGPoint) -> CGFloat {
return CGPoint.angleBetweenThreePoints(center: self, firstPoint: firstPoint, secondPoint: secondPoint)
}
func angleToPoint(pointOnCircle: CGPoint) -> CGFloat {
let originX = pointOnCircle.x - self.x
let originY = pointOnCircle.y - self.y
var radians = atan2(originY, originX)
while radians < 0 {
radians += CGFloat(2 * Double.pi)
}
return radians
}
static func pointOnCircleAtArcDistance(center: CGPoint,
point: CGPoint,
radius: CGFloat,
arcDistance: CGFloat,
clockwise: Bool) -> CGPoint {
var angle = center.angleToPoint(pointOnCircle: point);
if clockwise {
angle = angle + (arcDistance / radius)
} else {
angle = angle - (arcDistance / radius)
}
return self.pointOnCircle(center: center, radius: radius, angle: angle)
}
func distanceToPoint(otherPoint: CGPoint) -> CGFloat {
return sqrt(pow((otherPoint.x - x), 2) + pow((otherPoint.y - y), 2))
}
static func CGPointRound(_ point: CGPoint) -> CGPoint {
return CGPoint(x: CoreGraphics.round(point.x), y: CoreGraphics.round(point.y))
}
static func intersectingPointsOfCircles(firstCenter: CGPoint, secondCenter: CGPoint, firstRadius: CGFloat, secondRadius: CGFloat ) -> (firstPoint: CGPoint?, secondPoint: CGPoint?) {
let distance = firstCenter.distanceToPoint(otherPoint: secondCenter)
let m = firstRadius + secondRadius
var n = firstRadius - secondRadius
if n < 0 {
n = n * -1
}
//no intersection
if distance > m {
return (firstPoint: nil, secondPoint: nil)
}
//circle is inside other circle
if distance < n {
return (firstPoint: nil, secondPoint: nil)
}
//same circle
if distance == 0 && firstRadius == secondRadius {
return (firstPoint: nil, secondPoint: nil)
}
let a = ((firstRadius * firstRadius) - (secondRadius * secondRadius) + (distance * distance)) / (2 * distance)
let h = sqrt(firstRadius * firstRadius - a * a)
var p = CGPoint.zero
p.x = firstCenter.x + (a / distance) * (secondCenter.x - firstCenter.x)
p.y = firstCenter.y + (a / distance) * (secondCenter.y - firstCenter.y)
//only one point intersecting
if distance == firstRadius + secondRadius {
return (firstPoint: p, secondPoint: nil)
}
var p1 = CGPoint.zero
var p2 = CGPoint.zero
p1.x = p.x + (h / distance) * (secondCenter.y - firstCenter.y)
p1.y = p.y - (h / distance) * (secondCenter.x - firstCenter.x)
p2.x = p.x - (h / distance) * (secondCenter.y - firstCenter.y)
p2.y = p.y + (h / distance) * (secondCenter.x - firstCenter.x)
//return both points
return (firstPoint: p1, secondPoint: p2)
}
}
@sungjin0213
Copy link

sungjin0213 commented Oct 13, 2020

https://gist.github.com/akhilcb/8d03f1f88f87e996aec24748bdf0ce78#file-cgpoint-extension-swift-L20

Need to update

        if angleDiff < 0 {
            angleDiff *= -1
        }

to something similar to

        while radians < 0 {
            radians += CGFloat(2 * Double.pi)
        }

In order to add 360 degrees when it's negative angle.
Just converting to non-negative will not give you correct angle.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment