Skip to content

Instantly share code, notes, and snippets.

@dejager
Created October 21, 2022 14:17
Show Gist options
  • Save dejager/8e292be983cdc39f28560ed24c0a17be to your computer and use it in GitHub Desktop.
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.
//
// 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