GRIRiskGridView.swift 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. // LoopFollow
  2. // GRIRiskGridView.swift
  3. import Charts
  4. import SwiftUI
  5. struct GRIRiskGridView: View {
  6. let hypoComponent: Double
  7. let hyperComponent: Double
  8. let gri: Double
  9. private enum Zone: String, CaseIterable, Plottable {
  10. case a = "A"
  11. case b = "B"
  12. case c = "C"
  13. case d = "D"
  14. case e = "E"
  15. var color: Color {
  16. switch self {
  17. case .a: return .green.opacity(0.3)
  18. case .b: return .yellow.opacity(0.3)
  19. case .c: return .orange.opacity(0.3)
  20. case .d: return .red.opacity(0.3)
  21. case .e: return .red.opacity(0.5)
  22. }
  23. }
  24. }
  25. private struct GridPoint: Identifiable {
  26. let hypo: Double
  27. let hyper: Double
  28. let zone: Zone
  29. var id: String { "\(hypo)-\(hyper)" }
  30. }
  31. private var gridPoints: [GridPoint] {
  32. var points: [GridPoint] = []
  33. let step = 0.5
  34. for hypo in stride(from: 0.0, through: 30.0, by: step) {
  35. for hyper in stride(from: 0.0, through: 60.0, by: step) {
  36. let griValue = (3.0 * hypo) + (1.6 * hyper)
  37. guard griValue <= 100 else { continue }
  38. let zone: Zone
  39. if griValue <= 20 {
  40. zone = .a
  41. } else if griValue <= 40 {
  42. zone = .b
  43. } else if griValue <= 60 {
  44. zone = .c
  45. } else if griValue <= 80 {
  46. zone = .d
  47. } else {
  48. zone = .e
  49. }
  50. points.append(GridPoint(hypo: hypo, hyper: hyper, zone: zone))
  51. }
  52. }
  53. return points
  54. }
  55. var body: some View {
  56. Chart {
  57. ForEach(gridPoints) { point in
  58. PointMark(
  59. x: .value("hypo", point.hypo),
  60. y: .value("hyper", point.hyper)
  61. )
  62. .symbolSize(16)
  63. .foregroundStyle(by: .value("zone", point.zone))
  64. }
  65. PointMark(
  66. x: .value("hypo", hypoComponent),
  67. y: .value("hyper", hyperComponent)
  68. )
  69. .symbolSize(120)
  70. .foregroundStyle(Color(.label))
  71. }
  72. .chartForegroundStyleScale(
  73. domain: Zone.allCases,
  74. range: Zone.allCases.map(\.color)
  75. )
  76. .chartLegend(.hidden)
  77. .chartXScale(domain: 0 ... 30)
  78. .chartYScale(domain: 0 ... 61)
  79. .chartXAxis {
  80. AxisMarks(position: .bottom) { value in
  81. AxisTick()
  82. AxisValueLabel {
  83. if let v = value.as(Double.self) {
  84. Text(String(format: "%.0f", v))
  85. }
  86. }
  87. }
  88. }
  89. .chartYAxis {
  90. AxisMarks(position: .leading) { value in
  91. AxisTick()
  92. AxisValueLabel {
  93. if let v = value.as(Double.self) {
  94. Text(String(format: "%.0f", v))
  95. }
  96. }
  97. }
  98. }
  99. .allowsHitTesting(false)
  100. .background(Color(.systemBackground))
  101. }
  102. }