Created
October 21, 2022 14:17
-
-
Save dejager/8e292be983cdc39f28560ed24c0a17be to your computer and use it in GitHub Desktop.
A SwiftUI Shape that draws an insettable polygon with a given number of corners and a corner radius.
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
// | |
// RoundedInsettablePolygon.swift | |
// | |
// Created by Nate on 2022-10-17. | |
// | |
import SwiftUI | |
struct RoundedPolygon: Shape, InsettableShape { | |
private let radius: CGFloat | |
private let points: Int | |
init(cornerRadius: CGFloat, points: Int) { | |
self.radius = cornerRadius | |
self.points = points | |
} | |
private func calculateCorners(in rect: CGRect, | |
points: Int) -> [CGPoint] { | |
var corners = [CGPoint]() | |
let step: CGFloat = (.pi * 2) / CGFloat(points) | |
let radius: CGFloat = min(rect.width, rect.height) / 2 | |
for i in 0 ..< points { | |
let theta = CGFloat(i) * step | |
let x = rect.minX + radius + cos(theta) * radius | |
let y = rect.minY + radius + sin(theta) * radius | |
corners.append(CGPoint(x: x, y: y)) | |
} | |
return corners | |
} | |
func path(in rect: CGRect) -> Path { | |
let corners = calculateCorners(in: rect, | |
points: points) | |
guard corners.count >= 3 else { return Path() } | |
var path = Path() | |
let c1 = corners[0] | |
let c2 = corners[corners.count - 1] | |
let startPoint = CGPoint(x: (c1.x + c2.x) / 2, | |
y: (c1.y + c2.y) / 2) | |
path.move(to: startPoint) | |
for n in 0..<corners.count { | |
let current = corners[n] | |
let next = n < (corners.count - 1) ? corners[n + 1] : corners[0] | |
path.addArc(tangent1End: current, tangent2End: next, radius: radius) | |
} | |
return path | |
} | |
// MARK: - InsettableShape | |
func inset(by amount: CGFloat) -> some InsettableShape { | |
return RoundedPolygon_Inset(base: self, amount: amount) | |
} | |
struct RoundedPolygon_Inset: InsettableShape { | |
var base: RoundedPolygon | |
var amount: CGFloat | |
var animatableData: CGFloat { | |
get { amount } | |
set { amount = newValue } | |
} | |
func path(in rect: CGRect) -> Path { | |
return RoundedPolygon(cornerRadius: base.radius, | |
points: base.points) | |
.path(in: rect.insetBy(dx: amount, dy: amount)) | |
} | |
func inset(by amount: CGFloat) -> some InsettableShape { | |
var copy = self | |
copy.amount += amount | |
return copy | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment