import SwiftUI
struct ForecastArrow: View {
@Environment(ViewModel.self) private var globalVM: ViewModel
private var windSpeedKn : Int
private let frame : CGRect
init(size: CGFloat, windSpeedKn: Int) {
self.windSpeedKn = Swift.min(windSpeedKn, 150)
self.frame = CGRect(x: 0, y: 0, width: size, height: size)
}
var body: some View {
let allBarbs = getAllBarbs(from: windSpeedKn)
let min: CGFloat = min(frame.width, frame.height)
let frameY = min * 0.146
let gray: Color = Color.gray
let lineWidth : CGFloat = min * 0.008
let lineCap : CGLineCap = .square
let lineJoin : CGLineJoin = .miter
let miterLimit : CGFloat = lineWidth
let dash : [CGFloat] = []
let dashPhase : CGFloat = lineWidth
let style = StrokeStyle(
lineWidth: lineWidth,
lineCap: lineCap,
lineJoin: lineJoin,
miterLimit: miterLimit,
dash: dash,
dashPhase: dashPhase
)
Canvas { context, size in
let bottomCenter = CGPoint(x: size.width / 2, y: size.height)
let fraction: CGFloat = 0.2
let tenFiftyKnotLength = size.height * fraction
let fiveKnotLength = tenFiftyKnotLength / 2
let aBitLess = size.height * 0.02
let circleRadius = fiveKnotLength / 2
let circleDiameter = fiveKnotLength
let barbsStartPoint = CGPoint(x: bottomCenter.x, y: bottomCenter.y - tenFiftyKnotLength - circleRadius)
let barbAngle: Double = 45
let fiftyHypoteneuse = sqrt((tenFiftyKnotLength * tenFiftyKnotLength) * 2)
let tenHypoteneuse = fiftyHypoteneuse + aBitLess
let fiveKnotHypoteneuse = sqrt((fiveKnotLength * fiveKnotLength) * 2) + aBitLess
let allItemsWidth = fiveKnotLength * 0.2
let allItemsHalfWidth = allItemsWidth * 0.5
let twoLinesWidth = 2 * lineWidth
let turtle = Turtle(start: barbsStartPoint)
let shaftLength = size.height - tenFiftyKnotLength - twoLinesWidth - circleDiameter - circleDiameter
turtle.fd(shaftLength)
let turtleCirclePosition = turtle.getPosition()
let circleCenter = CGPoint(x: turtleCirclePosition.x, y: turtleCirclePosition.y - twoLinesWidth)
var shaftHead = Path()
shaftHead.addArc(center: circleCenter, radius: circleRadius, startAngle: .degrees(0), endAngle: .degrees(360), clockwise: true)
turtle.pu()
turtle.bk(shaftLength)
let nextStartPoint = tenFiftyKnotLength * 0.35
var stillCircle = Path()
var stillCircleCenter = Path()
let nextFiftyLength = tenFiftyKnotLength - circleRadius
var dimensionsDictionary: Dictionary<String, CGFloat> = [:]
dimensionsDictionary["barbAngle"] = barbAngle
dimensionsDictionary["tenFiftyKnotLength"] = tenFiftyKnotLength
dimensionsDictionary["fiveKnotLength"] = fiveKnotLength
dimensionsDictionary["aBitLess"] = aBitLess
dimensionsDictionary["circleRadius"] = circleRadius
dimensionsDictionary["circleDiameter"] = circleDiameter
dimensionsDictionary["fiftyHypoteneuse"] = fiftyHypoteneuse
dimensionsDictionary["tenHypoteneuse"] = tenHypoteneuse
dimensionsDictionary["fiveKnotHypoteneuse"] = fiveKnotHypoteneuse
dimensionsDictionary["allItemsWidth"] = allItemsWidth
dimensionsDictionary["allItemsHalfWidth"] = allItemsHalfWidth
dimensionsDictionary["twoLinesWidth"] = twoLinesWidth
dimensionsDictionary["shaftLength"] = shaftLength
dimensionsDictionary["nextFiftyLength"] = nextFiftyLength
for (i, barb) in allBarbs.enumerated() {
switch barb {
case .still:
let canvasCenter = CGPoint(x: size.width / 2, y: size.height / 2)
let reasonableRadius = Swift.min(size.width, size.height) / 5
let filledCircleRadius = reasonableRadius / 5
stillCircle.addArc(center: canvasCenter, radius: reasonableRadius,
startAngle: .degrees(0), endAngle: .degrees(360), clockwise: true)
stillCircleCenter.addArc(center: canvasCenter, radius: filledCircleRadius,
startAngle: .degrees(0), endAngle: .degrees(360), clockwise: true)
break
case .calm:
break
case .five:
let fiveKnotTurtle = getFiveKnotTurtle(start: turtle.getPosition(), numDict: dimensionsDictionary)
context.stroke(fiveKnotTurtle.path, with: .color(gray), style: style)
turtle.setPosition(fiveKnotTurtle.getPosition())
case .ten:
let tenKnotTurtle = getTenKnotTurtle(start: turtle.getPosition(), numDict: dimensionsDictionary)
context.stroke(tenKnotTurtle.path, with: .color(gray), style: style)
turtle.setPosition(tenKnotTurtle.getPosition())
case .fifty:
if i > 0,
allBarbs[i - 1] == .fifty && barb == .fifty {
turtle.fd(nextFiftyLength)
}
let fiftyKnotTurtle = getFiftyKnotTurtle(start: turtle.getPosition(), numDict: dimensionsDictionary)
context.stroke(fiftyKnotTurtle.path, with: .color(gray), style: style)
context.fill(fiftyKnotTurtle.path, with: .color(gray))
turtle.setPosition(fiftyKnotTurtle.getPosition())
}
turtle.fd(nextStartPoint)
}
if allBarbs.contains(.still) {
context.stroke(stillCircle, with: .color(gray), style: style)
context.stroke(stillCircleCenter, with: .color(gray), style: style)
context.fill(stillCircleCenter, with: .color(gray))
} else {
context.stroke(turtle.path, with: .color(gray), style: style)
context.stroke(shaftHead, with: .color(gray), style: style)
}
}
.frame(width: frameY, height: frameY, alignment: .center)
}
func getFiveKnotTurtle(start: CGPoint, numDict: Dictionary<String, CGFloat>) -> Turtle {
let turtle = Turtle(start: start)
turtle.lt(90 + numDict["barbAngle"]!)
turtle.pd()
turtle.fd(numDict["fiveKnotHypoteneuse"]!)
turtle.pu()
turtle.home()
return turtle
}
func getTenKnotTurtle(start: CGPoint, numDict: Dictionary<String, CGFloat>) -> Turtle {
let turtle = Turtle(start: start)
turtle.lt(90 + numDict["barbAngle"]!)
turtle.fd(numDict["fiftyHypoteneuse"]!)
turtle.pu()
turtle.home()
return turtle
}
func getFiftyKnotTurtle(start: CGPoint, numDict: Dictionary<String, CGFloat>) -> Turtle {
let turtle = Turtle(start: start)
turtle.lt(90 + numDict["barbAngle"]!)
turtle.fd(numDict["fiftyHypoteneuse"]!)
turtle.lt(90 + numDict["barbAngle"]!)
turtle.fd(numDict["tenFiftyKnotLength"]!)
turtle.home()
return turtle
}
func getAllBarbs(from windspeed: Int) -> [Barb] {
if windspeed < 1 { return [.still] }
else if windspeed < 5 { return [.calm] }
else {
var bigResult: [Barb]= []
var loopingWindSpeed= windspeed
while loopingWindSpeed - 50 >= -2 {
bigResult.append(.fifty)
loopingWindSpeed -= 50
}
while loopingWindSpeed - 10 >= -2 {
bigResult.append(.ten)
loopingWindSpeed -= 10
}
while loopingWindSpeed - 5 >= -2 {
bigResult.append(.five)
loopingWindSpeed -= 5
}
return bigResult
}
}
private func getWindSpeedRoundedUpToClosestFiveKnots(_ windSpeed: Int) -> Double {
(Double(windSpeed) / 5).rounded(.up) * 5
}
enum Barb: String {
case still
case calm
case five
case ten
case fifty
}
}
@Observable final class ViewModel { }