ForeCastChart.swift 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import Charts
  2. import CoreData
  3. import Foundation
  4. import SwiftUI
  5. struct ForeCastChart: View {
  6. @StateObject var state: Bolus.StateModel
  7. @Environment(\.colorScheme) var colorScheme
  8. @Binding var units: GlucoseUnits
  9. @State private var startMarker = Date(timeIntervalSinceNow: -4 * 60 * 60)
  10. @State private var endMarker = Date(timeIntervalSinceNow: 3 * 60 * 60)
  11. private var conversionFactor: Decimal {
  12. units == .mmolL ? 0.0555 : 1
  13. }
  14. var body: some View {
  15. forecastChart
  16. }
  17. private var forecastChart: some View {
  18. Chart {
  19. drawGlucose()
  20. drawCurrentTimeMarker()
  21. // drawForecasts()
  22. drawForecastArea()
  23. }
  24. .chartXAxis { forecastChartXAxis }
  25. .chartXScale(domain: startMarker ... endMarker)
  26. .chartYAxis { forecastChartYAxis }
  27. }
  28. private func drawGlucose() -> some ChartContent {
  29. ForEach(state.glucoseFromPersistence) { item in
  30. if item.glucose > Int(state.highGlucose) {
  31. PointMark(
  32. x: .value("Time", item.date ?? Date(), unit: .second),
  33. y: .value("Value", Decimal(item.glucose) * conversionFactor)
  34. )
  35. .foregroundStyle(Color.orange.gradient)
  36. .symbolSize(20)
  37. } else if item.glucose < Int(state.lowGlucose) {
  38. PointMark(
  39. x: .value("Time", item.date ?? Date(), unit: .second),
  40. y: .value("Value", Decimal(item.glucose) * conversionFactor)
  41. )
  42. .foregroundStyle(Color.red.gradient)
  43. .symbolSize(20)
  44. } else {
  45. PointMark(
  46. x: .value("Time", item.date ?? Date(), unit: .second),
  47. y: .value("Value", Decimal(item.glucose) * conversionFactor)
  48. )
  49. .foregroundStyle(Color.green.gradient)
  50. .symbolSize(20)
  51. }
  52. }
  53. }
  54. private func timeForIndex(_ index: Int32) -> Date {
  55. let currentTime = Date()
  56. let timeInterval = TimeInterval(index * 300)
  57. return currentTime.addingTimeInterval(timeInterval)
  58. }
  59. private func drawForecastArea() -> some ChartContent {
  60. ForEach(state.minForecast.indices, id: \.self) { index in
  61. AreaMark(
  62. x: .value("Time", timeForIndex(Int32(index))),
  63. yStart: .value("Min Value", Decimal(state.minForecast[index]) * conversionFactor),
  64. yEnd: .value("Max Value", Decimal(state.maxForecast[index]) * conversionFactor)
  65. )
  66. .foregroundStyle(Color.blue.opacity(0.3))
  67. }
  68. }
  69. // private func drawForecasts() -> some ChartContent {
  70. // let predictions = state.predictionsForChart
  71. //
  72. // let predictionData = [
  73. // ("IOB", predictions?.iob),
  74. // ("ZT", predictions?.zt),
  75. // ("COB", predictions?.cob),
  76. // ("UAM", predictions?.uam)
  77. // ]
  78. //
  79. // return ForEach(predictionData, id: \.0) { name, values in
  80. // if let values = values {
  81. // ForEach(values.indices, id: \.self) { index in
  82. // LineMark(
  83. // x: .value("Time", timeForIndex(Int32(index))),
  84. // y: .value("Value", Decimal(values[index]) * conversionFactor)
  85. // )
  86. // .foregroundStyle(by: .value("Prediction Type", name))
  87. // }
  88. // }
  89. // }
  90. // }
  91. private func drawCurrentTimeMarker() -> some ChartContent {
  92. RuleMark(
  93. x: .value(
  94. "",
  95. Date(timeIntervalSince1970: TimeInterval(NSDate().timeIntervalSince1970)),
  96. unit: .second
  97. )
  98. ).lineStyle(.init(lineWidth: 2, dash: [3])).foregroundStyle(Color(.systemGray2))
  99. }
  100. private var forecastChartXAxis: some AxisContent {
  101. AxisMarks(values: .stride(by: .hour, count: 2)) { _ in
  102. AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
  103. AxisValueLabel(format: .dateTime.hour(.defaultDigits(amPM: .narrow)), anchor: .top)
  104. .font(.footnote)
  105. .foregroundStyle(Color.primary)
  106. }
  107. }
  108. private var forecastChartYAxis: some AxisContent {
  109. AxisMarks(position: .trailing) { _ in
  110. AxisGridLine(stroke: .init(lineWidth: 0.5, dash: [2, 3]))
  111. AxisTick(length: 3, stroke: .init(lineWidth: 3)).foregroundStyle(Color.secondary)
  112. AxisValueLabel().font(.footnote).foregroundStyle(Color.primary)
  113. }
  114. }
  115. }