Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions Loop.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
430B29952041F5CB00BA9F93 /* LoopSettings+Loop.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430B29942041F5CB00BA9F93 /* LoopSettings+Loop.swift */; };
430D85891F44037000AF2D4F /* HUDViewTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430D85881F44037000AF2D4F /* HUDViewTableViewCell.swift */; };
4311FB9B1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4311FB9A1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift */; };
4315D2871CA5CC3B00589052 /* CarbEntryEditTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4315D2861CA5CC3B00589052 /* CarbEntryEditTableViewController.swift */; };
4315D28A1CA5F45E00589052 /* DiagnosticLogger+LoopKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4315D2891CA5F45E00589052 /* DiagnosticLogger+LoopKit.swift */; };
431A8C401EC6E8AB00823B9C /* CircleMaskView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 431A8C3F1EC6E8AB00823B9C /* CircleMaskView.swift */; };
431EA87021EB29120076EC1A /* ExponentialInsulinModelPreset.swift in Sources */ = {isa = PBXBuildFile; fileRef = 435CB6241F37ABFC00C320C7 /* ExponentialInsulinModelPreset.swift */; };
Expand Down Expand Up @@ -181,7 +180,6 @@
43CB2B2B1D924D450079823D /* WCSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CB2B2A1D924D450079823D /* WCSession.swift */; };
43CE7CDE1CA8B63E003CC1B0 /* Data.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CE7CDD1CA8B63E003CC1B0 /* Data.swift */; };
43CEE6E61E56AFD400CB9116 /* NightscoutUploader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43CEE6E51E56AFD400CB9116 /* NightscoutUploader.swift */; };
43D2E8231F00425400AE5CBF /* BolusViewController+LoopDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D2E8221F00425400AE5CBF /* BolusViewController+LoopDataManager.swift */; };
43D381621EBD9759007F8C8F /* HeaderValuesTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D381611EBD9759007F8C8F /* HeaderValuesTableViewCell.swift */; };
43D9000B21EB0BE000AF44BF /* LoopCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D9FFCF21EAE05D00AF44BF /* LoopCore.framework */; };
43D9001E21EB209400AF44BF /* LoopCore.h in Headers */ = {isa = PBXBuildFile; fileRef = 43D9FFD121EAE05D00AF44BF /* LoopCore.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -580,7 +578,6 @@
430DA58D1D4AEC230097D1CA /* NSBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSBundle.swift; sourceTree = "<group>"; };
4311FB9A1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleSubtitleTextFieldTableViewCell.swift; sourceTree = "<group>"; };
4313EDDF1D8A6BF90060FA79 /* ChartContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ChartContainerView.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
4315D2861CA5CC3B00589052 /* CarbEntryEditTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbEntryEditTableViewController.swift; sourceTree = "<group>"; };
4315D2891CA5F45E00589052 /* DiagnosticLogger+LoopKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DiagnosticLogger+LoopKit.swift"; sourceTree = "<group>"; };
431A8C3F1EC6E8AB00823B9C /* CircleMaskView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMaskView.swift; sourceTree = "<group>"; };
431E73471FF95A900069B5F7 /* PersistenceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -715,7 +712,6 @@
43CB2B2A1D924D450079823D /* WCSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WCSession.swift; sourceTree = "<group>"; };
43CE7CDD1CA8B63E003CC1B0 /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = "<group>"; };
43CEE6E51E56AFD400CB9116 /* NightscoutUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutUploader.swift; sourceTree = "<group>"; };
43D2E8221F00425400AE5CBF /* BolusViewController+LoopDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BolusViewController+LoopDataManager.swift"; sourceTree = "<group>"; };
43D381611EBD9759007F8C8F /* HeaderValuesTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderValuesTableViewCell.swift; sourceTree = "<group>"; };
43D533BB1CFD1DD7009E3085 /* WatchApp Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "WatchApp Extension.entitlements"; sourceTree = "<group>"; };
43D848AF1E7DCBE100DADCBC /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
Expand Down
2 changes: 1 addition & 1 deletion Loop/Managers/StatusChartsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ extension StatusChartsManager {

extension StatusChartsManager {
func setDoseEntries(_ doseEntries: [DoseEntry]) {
dose.setDoseEntries(doseEntries)
dose.doseEntries = doseEntries
invalidateChart(atIndex: ChartIndex.dose.rawValue)
}

Expand Down
96 changes: 57 additions & 39 deletions LoopUI/Charts/DoseChart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,30 @@ import Foundation
import LoopKit
import SwiftCharts

fileprivate struct DosePointsCache {
let basal: [ChartPoint]
let basalFill: [ChartPoint]
let bolus: [ChartPoint]
let highlight: [ChartPoint]
}

public class DoseChart: ChartProviding {
public init() {
doseEntries = []
}

public var doseEntries: [DoseEntry] {
didSet {
pointsCache = nil
}
}

public private(set) var basalDosePoints: [ChartPoint] = []
public private(set) var bolusDosePoints: [ChartPoint] = []

/// Dose points selectable when highlighting
public private(set) var allDosePoints: [ChartPoint] = [] {
private var pointsCache: DosePointsCache? {
didSet {
if let lastDate = allDosePoints.last?.x as? ChartAxisValueDate {
endDate = lastDate.date
if let pointsCache = pointsCache {
if let lastDate = pointsCache.highlight.last?.x as? ChartAxisValueDate {
endDate = lastDate.date
}
}
}
}
Expand All @@ -41,17 +52,19 @@ public class DoseChart: ChartProviding {

public extension DoseChart {
func didReceiveMemoryWarning() {
basalDosePoints = []
bolusDosePoints = []
allDosePoints = []
pointsCache = nil
doseChartCache = nil
}

func generate(withFrame frame: CGRect, xAxisModel: ChartAxisModel, xAxisValues: [ChartAxisValue], axisLabelSettings: ChartLabelSettings, guideLinesLayerSettings: ChartGuideLinesLayerSettings, colors: ChartColorPalette, chartSettings: ChartSettings, labelsWidthY: CGFloat, gestureRecognizer: UIGestureRecognizer?, traitCollection: UITraitCollection) -> Chart
{
let integerFormatter = NumberFormatter.integer

let startDate = ChartAxisValueDate.dateFromScalar(xAxisValues.first!.scalar)

let points = generateDosePoints(startDate: startDate)

let yAxisValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(basalDosePoints + bolusDosePoints + doseDisplayRangePoints, minSegmentCount: 2, maxSegmentCount: 3, multiple: log10(2) / 2, axisValueGenerator: { ChartAxisValueDoubleLog(screenLocDouble: $0, formatter: integerFormatter, labelSettings: axisLabelSettings) }, addPaddingSegmentIfEdge: true)
let yAxisValues = ChartAxisValuesStaticGenerator.generateYAxisValuesWithChartPoints(points.basal + points.bolus + doseDisplayRangePoints, minSegmentCount: 2, maxSegmentCount: 3, multiple: log(2) / 2, axisValueGenerator: { ChartAxisValueDoubleLog(screenLocDouble: $0, formatter: integerFormatter, labelSettings: axisLabelSettings) }, addPaddingSegmentIfEdge: true)

let yAxisModel = ChartAxisModel(axisValues: yAxisValues, lineColor: colors.axisLine, labelSpaceReservationMode: .fixed(labelsWidthY))

Expand All @@ -60,23 +73,23 @@ public extension DoseChart {
let (xAxisLayer, yAxisLayer, innerFrame) = (coordsSpace.xAxisLayer, coordsSpace.yAxisLayer, coordsSpace.chartInnerFrame)

// The dose area
let lineModel = ChartLineModel(chartPoints: basalDosePoints, lineColor: colors.doseTint, lineWidth: 2, animDuration: 0, animDelay: 0)
let lineModel = ChartLineModel(chartPoints: points.basal, lineColor: colors.doseTint, lineWidth: 2, animDuration: 0, animDelay: 0)
let doseLine = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel])

let doseArea = ChartPointsFillsLayer(
xAxis: xAxisLayer.axis,
yAxis: yAxisLayer.axis,
fills: [ChartPointsFill(
chartPoints: basalDosePoints,
chartPoints: points.basalFill,
fillColor: colors.doseTint.withAlphaComponent(0.5),
createContainerPoints: false
)]
)

let bolusLayer: ChartPointsScatterDownTrianglesLayer<ChartPoint>?

if bolusDosePoints.count > 0 {
bolusLayer = ChartPointsScatterDownTrianglesLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, chartPoints: bolusDosePoints, displayDelay: 0, itemSize: CGSize(width: 12, height: 12), itemFillColor: colors.doseTint)
if points.bolus.count > 0 {
bolusLayer = ChartPointsScatterDownTrianglesLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, chartPoints: points.bolus, displayDelay: 0, itemSize: CGSize(width: 12, height: 12), itemFillColor: colors.doseTint)
} else {
bolusLayer = nil
}
Expand All @@ -100,7 +113,7 @@ public extension DoseChart {
xAxisLayer: xAxisLayer,
yAxisLayer: yAxisLayer,
axisLabelSettings: axisLabelSettings,
chartPoints: allDosePoints,
chartPoints: points.highlight,
tintColor: colors.doseTint,
gestureRecognizer: gestureRecognizer
)
Expand All @@ -119,17 +132,21 @@ public extension DoseChart {

return Chart(frame: frame, innerFrame: innerFrame, settings: chartSettings, layers: layers.compactMap { $0 })
}
}

public extension DoseChart {
func setDoseEntries(_ doseEntries: [DoseEntry]) {

private func generateDosePoints(startDate: Date) -> DosePointsCache {

guard pointsCache == nil else {
return pointsCache!
}

let dateFormatter = DateFormatter(timeStyle: .short)
let doseFormatter = NumberFormatter.dose

var basalDosePoints = [ChartPoint]()
var bolusDosePoints = [ChartPoint]()
var allDosePoints = [ChartPoint]()

var basalPoints = [ChartPoint]()
var basalFillPoints = [ChartPoint]()
var bolusPoints = [ChartPoint]()
var highlightPoints = [ChartPoint]()

for entry in doseEntries {
let time = entry.endDate.timeIntervalSince(entry.startDate)

Expand All @@ -138,11 +155,11 @@ public extension DoseChart {
let y = ChartAxisValueDoubleLog(actualDouble: entry.unitsInDeliverableIncrements, unitString: "U", formatter: doseFormatter)

let point = ChartPoint(x: x, y: y)
bolusDosePoints.append(point)
allDosePoints.append(point)
bolusPoints.append(point)
highlightPoints.append(point)
} else if time > 0 {
// TODO: Display the DateInterval
let startX = ChartAxisValueDate(date: entry.startDate, formatter: dateFormatter)
let startX = ChartAxisValueDate(date: max(startDate, entry.startDate), formatter: dateFormatter)
let endX = ChartAxisValueDate(date: entry.endDate, formatter: dateFormatter)
let zero = ChartAxisValueInt(0)
let rate = entry.netBasalUnitsPerHour
Expand All @@ -158,19 +175,20 @@ public extension DoseChart {
} else {
valuePoints = []
}

basalFillPoints += [ChartPoint(x: startX, y: zero)] + valuePoints + [ChartPoint(x: endX, y: zero)]

if entry.startDate > startDate {
basalPoints += [ChartPoint(x: startX, y: zero)]
}
basalPoints += valuePoints + [ChartPoint(x: endX, y: zero)]

basalDosePoints += [
ChartPoint(x: startX, y: zero)
] + valuePoints + [
ChartPoint(x: endX, y: zero)
]

allDosePoints += valuePoints
highlightPoints += valuePoints
}
}

self.basalDosePoints = basalDosePoints
self.bolusDosePoints = bolusDosePoints
self.allDosePoints = allDosePoints
let pointsCache = DosePointsCache(basal: basalPoints, basalFill: basalFillPoints, bolus: bolusPoints, highlight: highlightPoints)
self.pointsCache = pointsCache
return pointsCache
}
}
4 changes: 3 additions & 1 deletion Scripts/make_scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ def make_glucose_values():

def make_basal_doses():
return [
BasalDose(1.0, hours(-0.5), hours(0.5)),
BasalDose(1.2, hours(-1.5), hours(0.5)),
BasalDose(0.9, hours(-1.0), hours(0.5)),
BasalDose(0.8, hours(-0.5), hours(0.5))
]


Expand Down