Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 14 additions & 6 deletions Sources/Shapes/RegularPolygons/Decagon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@ import SwiftUI

public struct Decagon: InsettableShape {
let inset: CGFloat
let radius: CGFloat

public func inset(by amount: CGFloat) -> Decagon {
Decagon(inset: self.inset + amount)
Decagon(inset: self.inset + amount, radius: radius)
}

public func path(in rect: CGRect) -> Path {
Path.regularPolygon(sides: 10, in: rect, inset: inset)
Path.regularPolygon(sides: 10, in: rect, inset: inset, radius: radius)
}

public init() {
inset = 0
}
public init() {
inset = 0
radius = 0
}

public init(radius: CGFloat) {
self.inset = 0
self.radius = radius
}
}

extension Decagon {
init(inset: CGFloat) {
init(inset: CGFloat, radius: CGFloat) {
self.inset = inset
self.radius = radius
}
}

Expand Down
24 changes: 16 additions & 8 deletions Sources/Shapes/RegularPolygons/Heptagon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@ import SwiftUI

public struct Heptagon: InsettableShape {
let inset: CGFloat
let radius: CGFloat

public func inset(by amount: CGFloat) -> Heptagon {
Heptagon(inset: self.inset + amount)
Heptagon(inset: self.inset + amount, radius: radius)
}

public func path(in rect: CGRect) -> Path {
Path.regularPolygon(sides: 7, in: rect, inset: inset)
Path.regularPolygon(sides: 7, in: rect, inset: inset, radius: radius)
}

public init() {
inset = 0
}
public init() {
inset = 0
radius = 0
}

public init(radius: CGFloat) {
self.inset = 0
self.radius = radius
}
}

extension Heptagon {
init(inset: CGFloat) {
self.inset = inset
}
init(inset: CGFloat, radius: CGFloat) {
self.inset = inset
self.radius = radius
}
}

struct Heptagon_Previews: PreviewProvider {
Expand Down
20 changes: 14 additions & 6 deletions Sources/Shapes/RegularPolygons/Hexagon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@ import SwiftUI

public struct Hexagon: InsettableShape {
let inset: CGFloat
let radius: CGFloat

public func inset(by amount: CGFloat) -> Hexagon {
Hexagon(inset: self.inset + amount)
Hexagon(inset: self.inset + amount, radius: radius)
}

public func path(in rect: CGRect) -> Path {
Path.regularPolygon(sides: 6, in: rect, inset: inset)
Path.regularPolygon(sides: 6, in: rect, inset: inset, radius: radius)
}

public init() {
inset = 0
}
public init() {
inset = 0
radius = 0
}

public init(radius: CGFloat) {
self.inset = 0
self.radius = radius
}
}

extension Hexagon {
init(inset: CGFloat) {
init(inset: CGFloat, radius: CGFloat) {
self.inset = inset
self.radius = radius
}
}

Expand Down
20 changes: 14 additions & 6 deletions Sources/Shapes/RegularPolygons/Nonagon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@ import SwiftUI

public struct Nonagon: InsettableShape {
let inset: CGFloat
let radius: CGFloat

public func inset(by amount: CGFloat) -> Nonagon {
Nonagon(inset: self.inset + amount)
Nonagon(inset: self.inset + amount, radius: radius)
}

public func path(in rect: CGRect) -> Path {
Path.regularPolygon(sides: 9, in: rect, inset: inset)
Path.regularPolygon(sides: 9, in: rect, inset: inset, radius: radius)
}

public init() {
inset = 0
}
public init() {
inset = 0
radius = 0
}

public init(radius: CGFloat) {
self.inset = 0
self.radius = radius
}
}

extension Nonagon {
init(inset: CGFloat) {
init(inset: CGFloat, radius: CGFloat) {
self.inset = inset
self.radius = radius
}
}

Expand Down
14 changes: 11 additions & 3 deletions Sources/Shapes/RegularPolygons/Octagon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,31 @@ import SwiftUI

public struct Octagon: InsettableShape {
let inset: CGFloat
let radius: CGFloat

public func inset(by amount: CGFloat) -> Octagon {
Octagon(inset: self.inset + amount)
Octagon(inset: self.inset + amount, radius: radius)
}

public func path(in rect: CGRect) -> Path {
Path.regularPolygon(sides: 8, in: rect, inset: inset)
Path.regularPolygon(sides: 8, in: rect, inset: inset, radius: radius)
}

public init() {
inset = 0
radius = 0
}

public init(radius: CGFloat) {
self.inset = 0
self.radius = radius
}
}

extension Octagon {
init(inset: CGFloat) {
init(inset: CGFloat, radius: CGFloat) {
self.inset = inset
self.radius = radius
}
}

Expand Down
24 changes: 16 additions & 8 deletions Sources/Shapes/RegularPolygons/Pentagon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,32 @@ import SwiftUI

public struct Pentagon: InsettableShape {
let inset: CGFloat
let radius: CGFloat

public func inset(by amount: CGFloat) -> Pentagon {
Pentagon(inset: self.inset + amount)
Pentagon(inset: self.inset + amount, radius: radius)
}

public func path(in rect: CGRect) -> Path {
Path.regularPolygon(sides: 5, in: rect, inset: inset)
Path.regularPolygon(sides: 5, in: rect, inset: inset, radius: radius)
}

public init() {
inset = 0
}
public init() {
inset = 0
radius = 0
}

public init(radius: CGFloat) {
self.inset = 0
self.radius = radius
}
}

extension Pentagon {
init(inset: CGFloat) {
self.inset = inset
}
init(inset: CGFloat, radius: CGFloat) {
self.inset = inset
self.radius = radius
}
}

struct Pentagon_Previews: PreviewProvider {
Expand Down
20 changes: 12 additions & 8 deletions Sources/Shapes/RegularPolygons/RegularPolygon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,48 +3,52 @@ import SwiftUI
public struct RegularPolygon: InsettableShape {
let sides: Int
let inset: CGFloat
let radius: CGFloat

public func inset(by amount: CGFloat) -> RegularPolygon {
RegularPolygon(sides: self.sides, inset: self.inset + amount)
RegularPolygon(sides: self.sides, inset: self.inset + amount, radius: radius)
}

public func path(in rect: CGRect) -> Path {
Path.regularPolygon(sides: self.sides, in: rect, inset: inset)
Path.regularPolygon(sides: self.sides, in: rect, inset: inset, radius: radius)
}

public init(sides: Int) {
public init(sides: Int, radius: CGFloat = 0) {
self.sides = sides
self.inset = 0
self.radius = radius
}

public init(sides: Double) {
public init(sides: Double, radius: CGFloat = 0) {
self.sides = Int(sides.rounded(.down))
self.inset = 0
self.radius = radius
}
}

extension RegularPolygon {
init(sides: Int, inset: CGFloat) {
init(sides: Int, inset: CGFloat, radius: CGFloat = 0) {
self.sides = sides
self.inset = inset
self.radius = radius
}
}

struct RegularPolygon_Previews: PreviewProvider {
static var previews: some View {
Group {
RegularPolygon(sides: 4)
RegularPolygon(sides: 4, radius: 5)
.strokeBorder(lineWidth: 20)
.foregroundColor(.blue)

Pentagon()
Pentagon(radius: 5)
.strokeBorder(lineWidth: 20)
.foregroundColor(.yellow)

Hexagon()
.foregroundColor(.orange)

Heptagon()
Heptagon(radius: 5)
.foregroundColor(.blue)

Octagon()
Expand Down
65 changes: 61 additions & 4 deletions Sources/Shapes/RegularPolygons/RegularPolygonPath.swift
Original file line number Diff line number Diff line change
@@ -1,23 +1,80 @@
import SwiftUI

extension Path {
static func regularPolygon(sides: Int, in rect: CGRect, inset: CGFloat = 0) -> Path {
static func regularPolygon(sides: Int, in rect: CGRect, inset: CGFloat = 0, radius: CGFloat = 0) -> Path {
let width = rect.size.width - inset * 2
let height = rect.size.height - inset * 2
let hypotenuse = Double(min(width, height)) / 2.0
let centerPoint = CGPoint(x: width / 2.0, y: height / 2.0)

var testDistance: CGFloat = .zero
var usableRadius: CGFloat = .zero


return Path { path in
(0...sides).forEach { index in
let angle = ((Double(index) * (360.0 / Double(sides))) - 90) * Double.pi / 180

//control point
let point = CGPoint(
x: centerPoint.x + CGFloat(cos(angle) * hypotenuse),
y: centerPoint.y + CGFloat(sin(angle) * hypotenuse)
)

//the angle from the target control point to the next control point
let nextAngle = ((Double(index + 1) * (360.0 / Double(sides))) - 90) * Double.pi / 180

//coordinates of the next control point
let nextPoint = CGPoint(
x: centerPoint.x + CGFloat(cos(nextAngle) * hypotenuse),
y: centerPoint.y + CGFloat(sin(nextAngle) * hypotenuse)
)

if testDistance == .zero {
//The distance between two neighboring endpoints on your polygon
testDistance = sqrt(pow(( nextPoint.x - point.x ), 2) + pow(( nextPoint.y - point.y ), 2))

//Ensures that our 'radius' won't exceed a length of half our polygonside
usableRadius = radius > testDistance / 2 ? testDistance / 2 : radius
}

//source point
let currentPoint = index == 0 ? point : path.currentPoint!

//distance from source point to target control point
let distance = sqrt(pow(( point.x - currentPoint.x ), 2) + pow(( point.y - currentPoint.y ), 2))

//distance from target control point to the start of the curve we want to draw
let distanceToCurveStart = index == 0 ? usableRadius : distance - usableRadius

//angle from current point to the target control point
let angleToCurveStart = index == 0 ? 0 : atan2((point.y - currentPoint.y), (point.x - currentPoint.x))

//coordinates of where to start the curve
let curveStartPoint = CGPoint(
x: currentPoint.x + (distanceToCurveStart * CGFloat(cos(angleToCurveStart))),
y: currentPoint.y + (distanceToCurveStart * CGFloat(sin(angleToCurveStart)))
)

//angle from current control point to next control point
let angleToCurveEnd = atan2((nextPoint.y - point.y), (nextPoint.x - point.x))

//coordinates of where the curve shuold end
let curveEndPoint = CGPoint(
x: point.x + (usableRadius * CGFloat(cos(angleToCurveEnd))),
y: point.y + (usableRadius * CGFloat(sin(angleToCurveEnd)))
)

if index == 0 {
path.move(to: point)
let altStartPoint = CGPoint(
x: point.x + (usableRadius * CGFloat(cos(angleToCurveEnd))),
y: point.y + (usableRadius * CGFloat(sin(angleToCurveEnd)))
)
path.move(to: radius == 0 ? point : altStartPoint)
} else {
path.addLine(to: point)
path.addLine(to: radius > 0 ? curveStartPoint : point)
if radius > 0 {
path.addQuadCurve(to: curveEndPoint, control: point)
}
}
}
path.closeSubpath()
Expand Down