ChartPoint.swift 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. //
  2. // ChartPoint.swift
  3. // Naterade
  4. //
  5. // Created by Nathan Racklyeft on 2/19/16.
  6. // Copyright © 2016 Nathan Racklyeft. All rights reserved.
  7. //
  8. import Foundation
  9. import HealthKit
  10. import LoopKit
  11. import SwiftCharts
  12. struct TargetChartBar {
  13. let points: [ChartPoint]
  14. let isOverride: Bool
  15. }
  16. extension ChartPoint {
  17. static func barsForGlucoseRangeSchedule(_ glucoseRangeSchedule: GlucoseRangeSchedule, unit: HKUnit, xAxisValues: [ChartAxisValue], considering potentialOverride: TemporaryScheduleOverride? = nil) -> [TargetChartBar] {
  18. let targetRanges = glucoseRangeSchedule.quantityBetween(
  19. start: ChartAxisValueDate.dateFromScalar(xAxisValues.first!.scalar),
  20. end: ChartAxisValueDate.dateFromScalar(xAxisValues.last!.scalar)
  21. )
  22. let dateFormatter = DateFormatter()
  23. var result = [TargetChartBar?]()
  24. for (index, range) in targetRanges.enumerated() {
  25. var startDate = ChartAxisValueDate(date: range.startDate, formatter: dateFormatter)
  26. var endDate: ChartAxisValueDate
  27. if index == targetRanges.startIndex, let firstDate = xAxisValues.first as? ChartAxisValueDate {
  28. startDate = firstDate
  29. }
  30. if index == targetRanges.endIndex - 1, let lastDate = xAxisValues.last as? ChartAxisValueDate {
  31. endDate = lastDate
  32. } else {
  33. endDate = ChartAxisValueDate(date: targetRanges[index + 1].startDate, formatter: dateFormatter)
  34. }
  35. if let override = potentialOverride,
  36. startDate.date < endDate.date,
  37. (override.startDate...override.scheduledEndDate).overlaps(startDate.date...endDate.date)
  38. {
  39. result.append(createBar(value: range.value, unit: unit, startDate: startDate, endDate: ChartAxisValueDate(date: override.startDate, formatter: dateFormatter), isOverride: false))
  40. let targetDuringOverride = override.settings.targetRange ?? range.value
  41. result.append(createBar(
  42. value: targetDuringOverride,
  43. unit: unit,
  44. startDate: ChartAxisValueDate(date: max(override.startDate, startDate.date), formatter: dateFormatter),
  45. endDate: ChartAxisValueDate(date: min(override.scheduledEndDate, endDate.date), formatter: dateFormatter),
  46. isOverride: true))
  47. result.append(createBar(value: range.value, unit: unit, startDate: ChartAxisValueDate(date: override.scheduledEndDate, formatter: dateFormatter), endDate: endDate, isOverride: false))
  48. } else {
  49. result.append(createBar(value: range.value, unit: unit, startDate: startDate, endDate: endDate, isOverride: false))
  50. }
  51. }
  52. return result.compactMap { $0 }
  53. }
  54. static fileprivate func createBar(value: ClosedRange<HKQuantity>, unit: HKUnit, startDate: ChartAxisValueDate, endDate: ChartAxisValueDate, isOverride: Bool) -> TargetChartBar? {
  55. guard startDate.date < endDate.date else { return nil }
  56. let value = value.doubleRangeWithMinimumIncrement(in: unit)
  57. let minValue = ChartAxisValueDouble(value.minValue)
  58. let maxValue = ChartAxisValueDouble(value.maxValue)
  59. return TargetChartBar(
  60. points: [
  61. ChartPoint(x: startDate, y: maxValue),
  62. ChartPoint(x: endDate, y: maxValue),
  63. ChartPoint(x: endDate, y: minValue),
  64. ChartPoint(x: startDate, y: minValue)
  65. ],
  66. isOverride: isOverride)
  67. }
  68. static func pointsForGlucoseRangeScheduleOverride(_ override: TemporaryScheduleOverride, unit: HKUnit, xAxisValues: [ChartAxisValue], extendEndDateToChart: Bool = false) -> [ChartPoint] {
  69. guard let targetRange = override.settings.targetRange else {
  70. return []
  71. }
  72. return pointsForGlucoseRangeScheduleOverride(
  73. range: targetRange.doubleRangeWithMinimumIncrement(in: unit),
  74. activeInterval: override.activeInterval,
  75. unit: unit,
  76. xAxisValues: xAxisValues,
  77. extendEndDateToChart: extendEndDateToChart
  78. )
  79. }
  80. private static func pointsForGlucoseRangeScheduleOverride(range: DoubleRange, activeInterval: DateInterval, unit: HKUnit, xAxisValues: [ChartAxisValue], extendEndDateToChart: Bool) -> [ChartPoint] {
  81. guard let lastXAxisValue = xAxisValues.last as? ChartAxisValueDate else {
  82. return []
  83. }
  84. let dateFormatter = DateFormatter()
  85. let startDateAxisValue = ChartAxisValueDate(date: activeInterval.start, formatter: dateFormatter)
  86. let displayEndDate = min(lastXAxisValue.date, extendEndDateToChart ? .distantFuture : activeInterval.end)
  87. let endDateAxisValue = ChartAxisValueDate(date: displayEndDate, formatter: dateFormatter)
  88. let minValue = ChartAxisValueDouble(range.minValue)
  89. let maxValue = ChartAxisValueDouble(range.maxValue)
  90. return [
  91. ChartPoint(x: startDateAxisValue, y: maxValue),
  92. ChartPoint(x: endDateAxisValue, y: maxValue),
  93. ChartPoint(x: endDateAxisValue, y: minValue),
  94. ChartPoint(x: startDateAxisValue, y: minValue)
  95. ]
  96. }
  97. }
  98. extension ChartPoint: TimelineValue {
  99. public var startDate: Date {
  100. if let dateValue = x as? ChartAxisValueDate {
  101. return dateValue.date
  102. } else {
  103. return Date.distantPast
  104. }
  105. }
  106. }
  107. private extension ClosedRange where Bound == HKQuantity {
  108. func doubleRangeWithMinimumIncrement(in unit: HKUnit) -> DoubleRange {
  109. let increment = unit.chartableIncrement
  110. var minValue = self.lowerBound.doubleValue(for: unit)
  111. var maxValue = self.upperBound.doubleValue(for: unit)
  112. if (maxValue - minValue) < .ulpOfOne {
  113. minValue -= increment
  114. maxValue += increment
  115. }
  116. return DoubleRange(minValue: minValue, maxValue: maxValue)
  117. }
  118. }