CobIobChart.swift 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import Charts
  2. import Foundation
  3. import SwiftUI
  4. extension MainChartView {
  5. var cobIobChart: some View {
  6. Chart {
  7. drawCurrentTimeMarker()
  8. drawCOBIOBChart()
  9. if let selectedCOBValue {
  10. drawSelectedInnerPoint(
  11. xValue: selectedCOBValue.deliverAt ?? Date.now,
  12. yValue: Double(selectedCOBValue.cob),
  13. axis: "COB"
  14. )
  15. drawSelectedOuterPoint(
  16. xValue: selectedCOBValue.deliverAt ?? Date.now,
  17. yValue: Double(selectedCOBValue.cob),
  18. axis: "IOB",
  19. color: Color.orange
  20. )
  21. }
  22. if let selectedIOBValue {
  23. let rawAmount = selectedIOBValue.iob?.doubleValue ?? 0
  24. let amount: Double = rawAmount > 0 ? rawAmount * 8 : rawAmount * 9
  25. drawSelectedInnerPoint(
  26. xValue: selectedIOBValue.deliverAt ?? Date.now,
  27. yValue: amount,
  28. axis: "COB"
  29. )
  30. drawSelectedOuterPoint(
  31. xValue: selectedIOBValue.deliverAt ?? Date.now,
  32. yValue: amount,
  33. axis: "IOB",
  34. color: Color.darkerBlue
  35. )
  36. }
  37. }
  38. .chartForegroundStyleScale([
  39. "COB": Color.orange,
  40. "IOB": Color.darkerBlue
  41. ])
  42. .chartLegend(.hidden)
  43. .frame(minHeight: geo.size.height * 0.12)
  44. .frame(width: fullWidth(viewWidth: screenSize.width))
  45. .chartXScale(domain: startMarker ... endMarker)
  46. .backport.chartXSelection(value: $selection)
  47. .chartXAxis { basalChartXAxis }
  48. .chartYAxis { cobIobChartYAxis }
  49. .chartYScale(domain: combinedYDomain())
  50. }
  51. func combinedYDomain() -> ClosedRange<Double> {
  52. let minValue = min(state.minValueCobChart, state.minValueIobChart)
  53. let maxValue = max(state.maxValueCobChart, state.maxValueIobChart)
  54. return Double(minValue) ... Double(maxValue)
  55. }
  56. private func drawSelectedInnerPoint(xValue: Date, yValue: Double, axis: String) -> some ChartContent {
  57. PointMark(
  58. x: .value("Time", xValue, unit: .minute),
  59. y: .value("Value", yValue)
  60. )
  61. .symbolSize(CGSize(width: 6, height: 6))
  62. .foregroundStyle(Color.primary)
  63. .position(by: .value("Axis", axis))
  64. }
  65. private func drawSelectedOuterPoint(xValue: Date, yValue: Double, axis: String, color: Color) -> some ChartContent {
  66. PointMark(
  67. x: .value("Time", xValue, unit: .minute),
  68. y: .value("Value", yValue)
  69. )
  70. .symbolSize(CGSize(width: 15, height: 15))
  71. .foregroundStyle(color.opacity(0.8))
  72. .position(by: .value("Axis", axis))
  73. }
  74. func drawCOBIOBChart() -> some ChartContent {
  75. ForEach(state.enactedAndNonEnactedDeterminations) { item in
  76. // MARK: - COB line and area mark
  77. let amountCOB = Int(item.cob)
  78. let date: Date = item.deliverAt ?? Date()
  79. LineMark(x: .value("Time", date), y: .value("Value", amountCOB))
  80. .foregroundStyle(by: .value("Type", "COB"))
  81. .position(by: .value("Axis", "COB"))
  82. AreaMark(x: .value("Time", date), y: .value("Value", amountCOB))
  83. .foregroundStyle(by: .value("Type", "COB"))
  84. .position(by: .value("Axis", "COB"))
  85. .opacity(0.2)
  86. // MARK: - IOB line and area mark
  87. let rawAmount = item.iob?.doubleValue ?? 0
  88. // as iob and cob share the same y axis and cob is usually >> iob we need to weigh iob visually
  89. let amountIOB: Double = rawAmount > 0 ? rawAmount * 8 : rawAmount * 9
  90. AreaMark(x: .value("Time", date), y: .value("Amount", amountIOB))
  91. .foregroundStyle(by: .value("Type", "IOB"))
  92. .position(by: .value("Axis", "IOB"))
  93. .opacity(0.2)
  94. LineMark(x: .value("Time", date), y: .value("Amount", amountIOB))
  95. .foregroundStyle(by: .value("Type", "IOB"))
  96. .position(by: .value("Axis", "IOB"))
  97. }
  98. }
  99. }