|
1 | 1 | import SwiftUI |
2 | 2 |
|
3 | 3 | extension Path { |
4 | | - static func regularPolygon(sides: Int, in rect: CGRect, inset: CGFloat = 0) -> Path { |
| 4 | + static func regularPolygon(sides: Int, in rect: CGRect, inset: CGFloat = 0, radius: CGFloat = 0) -> Path { |
5 | 5 | let width = rect.size.width - inset * 2 |
6 | 6 | let height = rect.size.height - inset * 2 |
7 | 7 | let hypotenuse = Double(min(width, height)) / 2.0 |
8 | 8 | let centerPoint = CGPoint(x: width / 2.0, y: height / 2.0) |
9 | | - |
| 9 | + var testDistance: CGFloat = .zero |
| 10 | + var usableRadius: CGFloat = .zero |
| 11 | + |
| 12 | + |
10 | 13 | return Path { path in |
11 | 14 | (0...sides).forEach { index in |
12 | 15 | let angle = ((Double(index) * (360.0 / Double(sides))) - 90) * Double.pi / 180 |
| 16 | + |
| 17 | + //control point |
13 | 18 | let point = CGPoint( |
14 | 19 | x: centerPoint.x + CGFloat(cos(angle) * hypotenuse), |
15 | 20 | y: centerPoint.y + CGFloat(sin(angle) * hypotenuse) |
16 | 21 | ) |
| 22 | + |
| 23 | + //the angle from the target control point to the next control point |
| 24 | + let nextAngle = ((Double(index + 1) * (360.0 / Double(sides))) - 90) * Double.pi / 180 |
| 25 | + |
| 26 | + //coordinates of the next control point |
| 27 | + let nextPoint = CGPoint( |
| 28 | + x: centerPoint.x + CGFloat(cos(nextAngle) * hypotenuse), |
| 29 | + y: centerPoint.y + CGFloat(sin(nextAngle) * hypotenuse) |
| 30 | + ) |
| 31 | + |
| 32 | + if testDistance == .zero { |
| 33 | + //The distance between two neighboring endpoints on your polygon |
| 34 | + testDistance = sqrt(pow(( nextPoint.x - point.x ), 2) + pow(( nextPoint.y - point.y ), 2)) |
| 35 | + |
| 36 | + //Ensures that our 'radius' won't exceed a length of half our polygonside |
| 37 | + usableRadius = radius > testDistance / 2 ? testDistance / 2 : radius |
| 38 | + } |
| 39 | + |
| 40 | + //source point |
| 41 | + let currentPoint = index == 0 ? point : path.currentPoint! |
| 42 | + |
| 43 | + //distance from source point to target control point |
| 44 | + let distance = sqrt(pow(( point.x - currentPoint.x ), 2) + pow(( point.y - currentPoint.y ), 2)) |
| 45 | + |
| 46 | + //distance from target control point to the start of the curve we want to draw |
| 47 | + let distanceToCurveStart = index == 0 ? usableRadius : distance - usableRadius |
| 48 | + |
| 49 | + //angle from current point to the target control point |
| 50 | + let angleToCurveStart = index == 0 ? 0 : atan2((point.y - currentPoint.y), (point.x - currentPoint.x)) |
| 51 | + |
| 52 | + //coordinates of where to start the curve |
| 53 | + let curveStartPoint = CGPoint( |
| 54 | + x: currentPoint.x + (distanceToCurveStart * CGFloat(cos(angleToCurveStart))), |
| 55 | + y: currentPoint.y + (distanceToCurveStart * CGFloat(sin(angleToCurveStart))) |
| 56 | + ) |
| 57 | + |
| 58 | + //angle from current control point to next control point |
| 59 | + let angleToCurveEnd = atan2((nextPoint.y - point.y), (nextPoint.x - point.x)) |
| 60 | + |
| 61 | + //coordinates of where the curve shuold end |
| 62 | + let curveEndPoint = CGPoint( |
| 63 | + x: point.x + (usableRadius * CGFloat(cos(angleToCurveEnd))), |
| 64 | + y: point.y + (usableRadius * CGFloat(sin(angleToCurveEnd))) |
| 65 | + ) |
| 66 | + |
17 | 67 | if index == 0 { |
18 | | - path.move(to: point) |
| 68 | + let altStartPoint = CGPoint( |
| 69 | + x: point.x + (usableRadius * CGFloat(cos(angleToCurveEnd))), |
| 70 | + y: point.y + (usableRadius * CGFloat(sin(angleToCurveEnd))) |
| 71 | + ) |
| 72 | + path.move(to: radius == 0 ? point : altStartPoint) |
19 | 73 | } else { |
20 | | - path.addLine(to: point) |
| 74 | + path.addLine(to: radius > 0 ? curveStartPoint : point) |
| 75 | + if radius > 0 { |
| 76 | + path.addQuadCurve(to: curveEndPoint, control: point) |
| 77 | + } |
21 | 78 | } |
22 | 79 | } |
23 | 80 | path.closeSubpath() |
|
0 commit comments