Last active
October 10, 2022 08:12
-
-
Save Enie/ef563325a84923be3808fa6c2810922d to your computer and use it in GitHub Desktop.
This file contains hidden or 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
import SwiftUI | |
import simd | |
struct Tick { | |
var position: CGPoint | |
var angle: Angle | |
} | |
func roundedRectTickCoordinates(count n:Int, in rect:CGRect, cornerRadius: CGFloat) -> [Tick] { | |
let width = rect.size.width | |
let height = rect.size.height | |
let radiusH = width/2 | |
let radiusV = height/2 | |
let cornerCenterH = radiusH - cornerRadius | |
let cornerCenterV = radiusV - cornerRadius | |
return Array(0..<n).map { i in | |
let angle = Angle(degrees: 360.0/Double(n)*Double(i)) | |
let d = simd_double2(x: cos(angle.radians), y: sin(angle.radians)) | |
let a = simd_double2(x: abs(d.x), y: abs(d.y)) | |
var s:simd_double2 = simd_double2(0, 0) | |
if a.x * radiusV > a.y * radiusH { | |
s.x = d.x.sign == .plus ? radiusH : -radiusH | |
s.y = d.y.sign == .plus ? radiusH * a.y / a.x : -radiusH * a.y / a.x | |
} else { | |
s.x = d.x.sign == .plus ? radiusV * a.x / a.y : -radiusV * a.x / a.y | |
s.y = d.y.sign == .plus ? radiusV : -radiusV | |
} | |
// part 2: set x and y on the square | |
var x:CGFloat = 0 | |
var y:CGFloat = 0 | |
// if position on square is where a corner radius should be, replace x and y with position on corner radius | |
if abs(s.x) > cornerCenterH && abs(s.y) > cornerCenterV { | |
// get vector from corner radius center to point on square | |
let cornerD = simd_double2(s.x < 0 ? s.x-(-cornerCenterH) : s.x-(cornerCenterH), s.y < 0 ? s.y-(-cornerCenterV) : s.y-(cornerCenterV)) | |
// get the angle of that vector | |
let cornerAngle = atan2(cornerD.y, cornerD.x) | |
// point on corner radius | |
let corner = simd_double2(cos(cornerAngle)*cornerRadius, sin(cornerAngle)*cornerRadius) | |
if s.x > 0 { | |
x = cornerCenterH + corner.x | |
} else { | |
x = -cornerCenterH + corner.x | |
} | |
if s.y > 0 { | |
y = cornerCenterV + corner.y | |
} else { | |
y = -cornerCenterV + corner.y | |
} | |
} else { | |
x = s.x | |
y = s.y | |
} | |
// shift by center coordinates | |
return Tick(position: CGPoint(x:x+radiusH, y:y+radiusV), angle: Angle(degrees: angle.degrees+90)) | |
} | |
} | |
struct RoundedRect: View { | |
var samples: Int = 60 | |
var rect: CGRect = CGRect(x: 0, y: 0, width: 200, height: 200) | |
var cornerRadius: CGFloat = 30 | |
var body: some View { | |
return VStack { | |
ZStack { | |
ForEach(roundedRectTickCoordinates(count: samples, in: rect, cornerRadius: cornerRadius), id: \.angle) {tick in | |
Rectangle() | |
.rotationEffect(tick.angle) | |
.frame(width: 4, height: 10) | |
.position(x: tick.position.x, y: tick.position.y) | |
} | |
} | |
.frame(width: rect.size.width, height: rect.size.height) | |
.padding() | |
} | |
} | |
} | |
fileprivate struct Preview: View { | |
@State var samples: CGFloat = 60 | |
@State var radius: CGFloat = 20 | |
@State var width: CGFloat = 200 | |
@State var height: CGFloat = 200 | |
var body: some View { | |
VStack(spacing: 0) { | |
RoundedRect(samples: Int(samples), rect: CGRect(origin: .zero, size: CGSize(width: width, height: height)), cornerRadius: radius) | |
.padding() | |
Spacer() | |
Group { | |
Slider(value: $samples, in: 4...100, minimumValueLabel: Text("4"), maximumValueLabel: Text("100")) { | |
Text("samples:") | |
} | |
Slider(value: $radius, in: 0...(min(width,height)/2), minimumValueLabel: Text("0"), maximumValueLabel: Text("\(Int(min(width,height)/2))")) { | |
Text("corner radius:") | |
} | |
Slider(value: $width, in: 10...200, minimumValueLabel: Text("0"), maximumValueLabel: Text("200")) { | |
Text("width:") | |
} | |
Slider(value: $height, in: 10...200, minimumValueLabel: Text("0"), maximumValueLabel: Text("200")) { | |
Text("height:") | |
} | |
} | |
} | |
.padding() | |
.frame(width: 400, height: 500) | |
} | |
} | |
struct RoundedRect_Previews: PreviewProvider { | |
static var previews: some View { | |
Preview() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment