From fb65b0cd74f8a1f5adad557738110409f54637f6 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Sun, 19 Jan 2020 23:43:01 -0600 Subject: [PATCH 1/3] Fix interval multiple for dose chart y axis --- LoopUI/Charts/DoseChart.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LoopUI/Charts/DoseChart.swift b/LoopUI/Charts/DoseChart.swift index c8854e4446..8951d89826 100644 --- a/LoopUI/Charts/DoseChart.swift +++ b/LoopUI/Charts/DoseChart.swift @@ -51,7 +51,7 @@ public extension DoseChart { { let integerFormatter = NumberFormatter.integer - 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(basalDosePoints + bolusDosePoints + 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)) From e07ed802ed4328d02626656d127aee97c9f12c53 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 20 Jan 2020 12:46:19 -0600 Subject: [PATCH 2/3] Fix excursion of temp basal lines outside the left of graph area --- Loop.xcodeproj/project.pbxproj | 4 +++ LoopUI/Charts/DoseChart.swift | 2 +- .../Views/ChartPointsLineLayerClipped.swift | 29 +++++++++++++++++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 LoopUI/Views/ChartPointsLineLayerClipped.swift diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 2be8f19cba..a5977a215e 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -375,6 +375,7 @@ C17824A61E1AF91F00D9D25C /* BolusRecommendation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17824A41E1AD4D100D9D25C /* BolusRecommendation.swift */; }; C1814B86225E507C008D2D8E /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1814B85225E507C008D2D8E /* Sequence.swift */; }; C18C8C511D5A351900E043FB /* NightscoutDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */; }; + C19E96E223D62C10003F79B0 /* ChartPointsLineLayerClipped.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19E96E123D62C10003F79B0 /* ChartPointsLineLayerClipped.swift */; }; C1A3EED2235233E1007672E3 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1A3EED1235233E1007672E3 /* DerivedAssets.xcassets */; }; C1A3EED423523551007672E3 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1A3EED323523551007672E3 /* DerivedAssets.xcassets */; }; C1A3EED523535FFF007672E3 /* DefaultAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 894F71E11FFEC4D8007D365C /* DefaultAssets.xcassets */; }; @@ -1086,6 +1087,7 @@ C18A491422FCC22900FDA733 /* build-derived-watch-assets.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "build-derived-watch-assets.sh"; sourceTree = ""; }; C18A491522FCC22900FDA733 /* copy-plugins.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "copy-plugins.sh"; sourceTree = ""; }; C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutDataManager.swift; sourceTree = ""; }; + C19E96E123D62C10003F79B0 /* ChartPointsLineLayerClipped.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartPointsLineLayerClipped.swift; sourceTree = ""; }; C1A3EED1235233E1007672E3 /* DerivedAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssets.xcassets; sourceTree = ""; }; C1A3EED323523551007672E3 /* DerivedAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssets.xcassets; sourceTree = ""; }; C1C6591B1E1B1FDA0025CC58 /* recommend_temp_basal_dropping_then_rising.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = recommend_temp_basal_dropping_then_rising.json; sourceTree = ""; }; @@ -1702,6 +1704,7 @@ 43B371851CE583890013C5A6 /* BasalStateView.swift */, 4313EDDF1D8A6BF90060FA79 /* ChartContainerView.swift */, 4369618F1F19C86400447E89 /* ChartPointsContextFillLayer.swift */, + C19E96E123D62C10003F79B0 /* ChartPointsLineLayerClipped.swift */, 4F08DE831E7BB70B006741EA /* ChartPointsScatterDownTrianglesLayer.swift */, 4F08DE841E7BB70B006741EA /* ChartPointsTouchHighlightLayerViewCache.swift */, 4337615E1D52F487004A3647 /* GlucoseHUDView.swift */, @@ -2923,6 +2926,7 @@ 4F20AE631E6B87B100D07A06 /* ChartContainerView.swift in Sources */, 43FCEEAB221A61B40013DD30 /* IOBChart.swift in Sources */, 43BFF0C61E465A4400FF19A9 /* UIColor+HIG.swift in Sources */, + C19E96E223D62C10003F79B0 /* ChartPointsLineLayerClipped.swift in Sources */, 43FCEEB7221BCD160013DD30 /* InsulinModelChart.swift in Sources */, 43FCEEBB22211C860013DD30 /* CarbEffectChart.swift in Sources */, 4F7528A01DFE1F9D00C322D6 /* LoopStateView.swift in Sources */, diff --git a/LoopUI/Charts/DoseChart.swift b/LoopUI/Charts/DoseChart.swift index 8951d89826..416e4883a0 100644 --- a/LoopUI/Charts/DoseChart.swift +++ b/LoopUI/Charts/DoseChart.swift @@ -61,7 +61,7 @@ public extension DoseChart { // The dose area let lineModel = ChartLineModel(chartPoints: basalDosePoints, lineColor: colors.doseTint, lineWidth: 2, animDuration: 0, animDelay: 0) - let doseLine = ChartPointsLineLayer(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel]) + let doseLine = ChartPointsLineLayerClipped(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel]) let doseArea = ChartPointsFillsLayer( xAxis: xAxisLayer.axis, diff --git a/LoopUI/Views/ChartPointsLineLayerClipped.swift b/LoopUI/Views/ChartPointsLineLayerClipped.swift new file mode 100644 index 0000000000..db15dba06d --- /dev/null +++ b/LoopUI/Views/ChartPointsLineLayerClipped.swift @@ -0,0 +1,29 @@ +// +// ChartPointsLineLayerClipped.swift +// LoopUI +// +// Created by Pete Schwamb on 1/20/20. +// Copyright © 2020 LoopKit Authors. All rights reserved. +// + +import SwiftCharts + + +class ChartPointsLineLayerClipped: ChartPointsLineLayer { + + override func generateLineView(_ screenLine: ScreenLine, chart: Chart) -> ChartLinesView { + let view = super.generateLineView(screenLine, chart: chart) + + let lineMaskLayer = CAShapeLayer() + var maskRect = view.frame + maskRect.origin.y = 0 + maskRect.size.height = chart.bounds.height + let path = CGPath(rect: maskRect, transform: nil) + lineMaskLayer.path = path + + view.layer.mask = lineMaskLayer + return view + } + +} + From e1d1cd5ab2651fb07c22c38ca96e20440e885992 Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Mon, 27 Jan 2020 09:24:55 -0600 Subject: [PATCH 3/3] Avoid use of cpu clipping --- Loop.xcodeproj/project.pbxproj | 8 -- Loop/Managers/StatusChartsManager.swift | 2 +- LoopUI/Charts/DoseChart.swift | 98 +++++++++++-------- .../Views/ChartPointsLineLayerClipped.swift | 29 ------ Scripts/make_scenario.py | 4 +- 5 files changed, 62 insertions(+), 79 deletions(-) delete mode 100644 LoopUI/Views/ChartPointsLineLayerClipped.swift diff --git a/Loop.xcodeproj/project.pbxproj b/Loop.xcodeproj/project.pbxproj index 61b90f7899..8680204eae 100644 --- a/Loop.xcodeproj/project.pbxproj +++ b/Loop.xcodeproj/project.pbxproj @@ -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 */; }; @@ -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, ); }; }; @@ -377,7 +375,6 @@ C17824A61E1AF91F00D9D25C /* BolusRecommendation.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17824A41E1AD4D100D9D25C /* BolusRecommendation.swift */; }; C1814B86225E507C008D2D8E /* Sequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1814B85225E507C008D2D8E /* Sequence.swift */; }; C18C8C511D5A351900E043FB /* NightscoutDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */; }; - C19E96E223D62C10003F79B0 /* ChartPointsLineLayerClipped.swift in Sources */ = {isa = PBXBuildFile; fileRef = C19E96E123D62C10003F79B0 /* ChartPointsLineLayerClipped.swift */; }; C1A3EED2235233E1007672E3 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1A3EED1235233E1007672E3 /* DerivedAssets.xcassets */; }; C1A3EED423523551007672E3 /* DerivedAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C1A3EED323523551007672E3 /* DerivedAssets.xcassets */; }; C1A3EED523535FFF007672E3 /* DefaultAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 894F71E11FFEC4D8007D365C /* DefaultAssets.xcassets */; }; @@ -581,7 +578,6 @@ 430DA58D1D4AEC230097D1CA /* NSBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSBundle.swift; sourceTree = ""; }; 4311FB9A1F37FE1B00D4C0A7 /* TitleSubtitleTextFieldTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TitleSubtitleTextFieldTableViewCell.swift; sourceTree = ""; }; 4313EDDF1D8A6BF90060FA79 /* ChartContainerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ChartContainerView.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; - 4315D2861CA5CC3B00589052 /* CarbEntryEditTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CarbEntryEditTableViewController.swift; sourceTree = ""; }; 4315D2891CA5F45E00589052 /* DiagnosticLogger+LoopKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DiagnosticLogger+LoopKit.swift"; sourceTree = ""; }; 431A8C3F1EC6E8AB00823B9C /* CircleMaskView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CircleMaskView.swift; sourceTree = ""; }; 431E73471FF95A900069B5F7 /* PersistenceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PersistenceController.swift; sourceTree = ""; }; @@ -716,7 +712,6 @@ 43CB2B2A1D924D450079823D /* WCSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WCSession.swift; sourceTree = ""; }; 43CE7CDD1CA8B63E003CC1B0 /* Data.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Data.swift; sourceTree = ""; }; 43CEE6E51E56AFD400CB9116 /* NightscoutUploader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutUploader.swift; sourceTree = ""; }; - 43D2E8221F00425400AE5CBF /* BolusViewController+LoopDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BolusViewController+LoopDataManager.swift"; sourceTree = ""; }; 43D381611EBD9759007F8C8F /* HeaderValuesTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderValuesTableViewCell.swift; sourceTree = ""; }; 43D533BB1CFD1DD7009E3085 /* WatchApp Extension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "WatchApp Extension.entitlements"; sourceTree = ""; }; 43D848AF1E7DCBE100DADCBC /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = ""; }; @@ -1091,7 +1086,6 @@ C18A491422FCC22900FDA733 /* build-derived-watch-assets.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "build-derived-watch-assets.sh"; sourceTree = ""; }; C18A491522FCC22900FDA733 /* copy-plugins.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "copy-plugins.sh"; sourceTree = ""; }; C18C8C501D5A351900E043FB /* NightscoutDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NightscoutDataManager.swift; sourceTree = ""; }; - C19E96E123D62C10003F79B0 /* ChartPointsLineLayerClipped.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChartPointsLineLayerClipped.swift; sourceTree = ""; }; C1A3EED1235233E1007672E3 /* DerivedAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssets.xcassets; sourceTree = ""; }; C1A3EED323523551007672E3 /* DerivedAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = DerivedAssets.xcassets; sourceTree = ""; }; C1C6591B1E1B1FDA0025CC58 /* recommend_temp_basal_dropping_then_rising.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = recommend_temp_basal_dropping_then_rising.json; sourceTree = ""; }; @@ -1708,7 +1702,6 @@ 43B371851CE583890013C5A6 /* BasalStateView.swift */, 4313EDDF1D8A6BF90060FA79 /* ChartContainerView.swift */, 4369618F1F19C86400447E89 /* ChartPointsContextFillLayer.swift */, - C19E96E123D62C10003F79B0 /* ChartPointsLineLayerClipped.swift */, 4F08DE831E7BB70B006741EA /* ChartPointsScatterDownTrianglesLayer.swift */, 4F08DE841E7BB70B006741EA /* ChartPointsTouchHighlightLayerViewCache.swift */, 4337615E1D52F487004A3647 /* GlucoseHUDView.swift */, @@ -2930,7 +2923,6 @@ 4F20AE631E6B87B100D07A06 /* ChartContainerView.swift in Sources */, 43FCEEAB221A61B40013DD30 /* IOBChart.swift in Sources */, 43BFF0C61E465A4400FF19A9 /* UIColor+HIG.swift in Sources */, - C19E96E223D62C10003F79B0 /* ChartPointsLineLayerClipped.swift in Sources */, 43FCEEB7221BCD160013DD30 /* InsulinModelChart.swift in Sources */, 43FCEEBB22211C860013DD30 /* CarbEffectChart.swift in Sources */, 4F7528A01DFE1F9D00C322D6 /* LoopStateView.swift in Sources */, diff --git a/Loop/Managers/StatusChartsManager.swift b/Loop/Managers/StatusChartsManager.swift index d54d512797..a4ab76141c 100644 --- a/Loop/Managers/StatusChartsManager.swift +++ b/Loop/Managers/StatusChartsManager.swift @@ -104,7 +104,7 @@ extension StatusChartsManager { extension StatusChartsManager { func setDoseEntries(_ doseEntries: [DoseEntry]) { - dose.setDoseEntries(doseEntries) + dose.doseEntries = doseEntries invalidateChart(atIndex: ChartIndex.dose.rawValue) } diff --git a/LoopUI/Charts/DoseChart.swift b/LoopUI/Charts/DoseChart.swift index 416e4883a0..3c85992ffb 100644 --- a/LoopUI/Charts/DoseChart.swift +++ b/LoopUI/Charts/DoseChart.swift @@ -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 + } } } } @@ -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: log(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)) @@ -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 doseLine = ChartPointsLineLayerClipped(xAxis: xAxisLayer.axis, yAxis: yAxisLayer.axis, lineModels: [lineModel]) + 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? - - 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 } @@ -100,7 +113,7 @@ public extension DoseChart { xAxisLayer: xAxisLayer, yAxisLayer: yAxisLayer, axisLabelSettings: axisLabelSettings, - chartPoints: allDosePoints, + chartPoints: points.highlight, tintColor: colors.doseTint, gestureRecognizer: gestureRecognizer ) @@ -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) @@ -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 @@ -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 } } diff --git a/LoopUI/Views/ChartPointsLineLayerClipped.swift b/LoopUI/Views/ChartPointsLineLayerClipped.swift deleted file mode 100644 index db15dba06d..0000000000 --- a/LoopUI/Views/ChartPointsLineLayerClipped.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// ChartPointsLineLayerClipped.swift -// LoopUI -// -// Created by Pete Schwamb on 1/20/20. -// Copyright © 2020 LoopKit Authors. All rights reserved. -// - -import SwiftCharts - - -class ChartPointsLineLayerClipped: ChartPointsLineLayer { - - override func generateLineView(_ screenLine: ScreenLine, chart: Chart) -> ChartLinesView { - let view = super.generateLineView(screenLine, chart: chart) - - let lineMaskLayer = CAShapeLayer() - var maskRect = view.frame - maskRect.origin.y = 0 - maskRect.size.height = chart.bounds.height - let path = CGPath(rect: maskRect, transform: nil) - lineMaskLayer.path = path - - view.layer.mask = lineMaskLayer - return view - } - -} - diff --git a/Scripts/make_scenario.py b/Scripts/make_scenario.py index 103fd33cf2..0d7949b78f 100644 --- a/Scripts/make_scenario.py +++ b/Scripts/make_scenario.py @@ -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)) ]