diff --git a/Sources/Shapes/RegularPolygons/Decagon.swift b/Sources/Shapes/RegularPolygons/Decagon.swift index 8425182..09ade6e 100644 --- a/Sources/Shapes/RegularPolygons/Decagon.swift +++ b/Sources/Shapes/RegularPolygons/Decagon.swift @@ -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 } } diff --git a/Sources/Shapes/RegularPolygons/Heptagon.swift b/Sources/Shapes/RegularPolygons/Heptagon.swift index 5f0a4bf..826efa9 100644 --- a/Sources/Shapes/RegularPolygons/Heptagon.swift +++ b/Sources/Shapes/RegularPolygons/Heptagon.swift @@ -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 { diff --git a/Sources/Shapes/RegularPolygons/Hexagon.swift b/Sources/Shapes/RegularPolygons/Hexagon.swift index f1aedde..beb0933 100644 --- a/Sources/Shapes/RegularPolygons/Hexagon.swift +++ b/Sources/Shapes/RegularPolygons/Hexagon.swift @@ -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 } } diff --git a/Sources/Shapes/RegularPolygons/Nonagon.swift b/Sources/Shapes/RegularPolygons/Nonagon.swift index 5eb4bb1..aadae2a 100644 --- a/Sources/Shapes/RegularPolygons/Nonagon.swift +++ b/Sources/Shapes/RegularPolygons/Nonagon.swift @@ -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 } } diff --git a/Sources/Shapes/RegularPolygons/Octagon.swift b/Sources/Shapes/RegularPolygons/Octagon.swift index 0372051..b8a909b 100644 --- a/Sources/Shapes/RegularPolygons/Octagon.swift +++ b/Sources/Shapes/RegularPolygons/Octagon.swift @@ -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 } } diff --git a/Sources/Shapes/RegularPolygons/Pentagon.swift b/Sources/Shapes/RegularPolygons/Pentagon.swift index 70de9ec..8fb56df 100644 --- a/Sources/Shapes/RegularPolygons/Pentagon.swift +++ b/Sources/Shapes/RegularPolygons/Pentagon.swift @@ -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 { diff --git a/Sources/Shapes/RegularPolygons/RegularPolygon.swift b/Sources/Shapes/RegularPolygons/RegularPolygon.swift index fc3fe0c..88a4aa0 100644 --- a/Sources/Shapes/RegularPolygons/RegularPolygon.swift +++ b/Sources/Shapes/RegularPolygons/RegularPolygon.swift @@ -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() diff --git a/Sources/Shapes/RegularPolygons/RegularPolygonPath.swift b/Sources/Shapes/RegularPolygons/RegularPolygonPath.swift index fad98a1..85d88bd 100644 --- a/Sources/Shapes/RegularPolygons/RegularPolygonPath.swift +++ b/Sources/Shapes/RegularPolygons/RegularPolygonPath.swift @@ -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()