From 3dbd57b5a3f78e242409d60fe0b157d995b61e81 Mon Sep 17 00:00:00 2001 From: Daniel Cohen Gindi Date: Thu, 14 Jul 2016 11:48:49 +0300 Subject: [PATCH] Work on x-values instead of x-indices (free for all :-) --- Charts/Charts.xcodeproj/project.pbxproj | 84 +-- .../Animation/ChartAnimationEasing.swift | 138 ++-- Charts/Classes/Animation/ChartAnimator.swift | 8 +- Charts/Classes/Charts/BarChartView.swift | 108 ++- .../Classes/Charts/BarLineChartViewBase.swift | 354 ++++------ Charts/Classes/Charts/BubbleChartView.swift | 36 - .../Classes/Charts/CandleStickChartView.swift | 8 - Charts/Classes/Charts/ChartViewBase.swift | 114 ++- Charts/Classes/Charts/CombinedChartView.swift | 49 +- .../Charts/HorizontalBarChartView.swift | 61 +- Charts/Classes/Charts/LineChartView.swift | 11 - Charts/Classes/Charts/PieChartView.swift | 27 +- .../Charts/PieRadarChartViewBase.swift | 29 +- Charts/Classes/Charts/RadarChartView.swift | 44 +- Charts/Classes/Charts/ScatterChartView.swift | 15 - Charts/Classes/Components/ChartAxisBase.swift | 188 ++++- Charts/Classes/Components/ChartXAxis.swift | 99 +-- Charts/Classes/Components/ChartYAxis.swift | 154 +---- .../Implementations/ChartBaseDataSet.swift | 67 +- .../Standard/BarChartData.swift | 92 ++- .../Standard/BarChartDataEntry.swift | 46 +- .../Standard/BarChartDataSet.swift | 100 ++- .../BarLineScatterCandleBubbleChartData.swift | 9 +- .../Standard/BubbleChartData.swift | 11 +- .../Standard/BubbleChartDataEntry.swift | 16 +- .../Standard/BubbleChartDataSet.swift | 91 +-- .../Standard/CandleChartData.swift | 9 +- .../Standard/CandleChartDataEntry.swift | 14 +- .../Standard/CandleChartDataSet.swift | 40 +- .../Implementations/Standard/ChartData.swift | 647 +++++++----------- .../Standard/ChartDataEntry.swift | 72 +- .../Standard/ChartDataEntryBase.swift | 106 +++ .../Standard/ChartDataSet.swift | 288 ++++---- .../Standard/CombinedChartData.swift | 50 +- .../Standard/LineChartData.swift | 9 +- .../Standard/LineChartDataSet.swift | 4 +- .../Standard/PieChartData.swift | 20 +- .../Standard/PieChartDataEntry.swift | 70 ++ .../Standard/PieChartDataSet.swift | 4 +- .../Standard/RadarChartData.swift | 16 +- .../Standard/RadarChartDataSet.swift | 4 +- .../Standard/ScatterChartData.swift | 9 +- .../Data/Interfaces/IBarChartDataSet.swift | 3 - .../Data/Interfaces/IBubbleChartDataSet.swift | 2 - .../Data/Interfaces/IChartDataSet.swift | 70 +- .../Filters/ChartDataApproximator.swift | 122 ++++ .../Filters/ChartDataApproximatorFilter.swift | 214 ------ .../Classes/Filters/ChartDataBaseFilter.swift | 28 - .../Formatters/ChartAxisValueFormatter.swift | 30 + .../ChartDefaultAxisValueFormatter.swift | 63 ++ .../ChartDefaultXAxisValueFormatter.swift | 25 - .../Formatters/ChartXAxisValueFormatter.swift | 30 - .../Highlight/BarChartHighlighter.swift | 140 +--- Charts/Classes/Highlight/ChartHighlight.swift | 90 +-- .../Classes/Highlight/ChartHighlighter.swift | 160 ++++- .../Highlight/CombinedHighlighter.swift | 31 +- .../HorizontalBarChartHighlighter.swift | 88 +-- .../Interfaces/BarChartDataProvider.swift | 1 - ...ScatterCandleBubbleChartDataProvider.swift | 5 +- .../Interfaces/ChartDataProvider.swift | 6 +- Charts/Classes/Jobs/AnimatedMoveViewJob.swift | 6 +- Charts/Classes/Jobs/AnimatedViewPortJob.swift | 6 +- Charts/Classes/Jobs/AnimatedZoomViewJob.swift | 10 +- Charts/Classes/Jobs/ChartViewPortJob.swift | 6 +- Charts/Classes/Jobs/MoveChartViewJob.swift | 6 +- Charts/Classes/Jobs/ZoomChartViewJob.swift | 12 +- .../Classes/Renderers/BarChartRenderer.swift | 525 +++++++------- ...LineScatterCandleBubbleChartRenderer.swift | 62 ++ .../Renderers/BubbleChartRenderer.swift | 71 +- .../Renderers/CandleStickChartRenderer.swift | 123 ++-- .../Renderers/ChartAxisRendererBase.swift | 168 ++++- .../Renderers/ChartDataRendererBase.swift | 14 +- .../Renderers/ChartLegendRenderer.swift | 17 +- .../Classes/Renderers/ChartRendererBase.swift | 22 +- .../Renderers/ChartXAxisRenderer.swift | 158 ++++- .../ChartXAxisRendererBarChart.swift | 165 ----- ...ChartXAxisRendererHorizontalBarChart.swift | 142 ++-- .../ChartXAxisRendererRadarChart.swift | 43 +- .../Renderers/ChartYAxisRenderer.swift | 298 +++----- ...ChartYAxisRendererHorizontalBarChart.swift | 194 +++--- .../ChartYAxisRendererRadarChart.swift | 153 +++-- .../Renderers/CombinedChartRenderer.swift | 27 +- .../HorizontalBarChartRenderer.swift | 412 ++++++----- .../Classes/Renderers/LineChartRenderer.swift | 298 +++----- .../Renderers/LineRadarChartRenderer.swift | 5 +- .../LineScatterCandleRadarChartRenderer.swift | 8 +- .../Classes/Renderers/PieChartRenderer.swift | 82 ++- .../Renderers/RadarChartRenderer.swift | 41 +- .../Renderers/ScatterChartRenderer.swift | 46 +- .../Classes/Utils/ChartSelectionDetail.swift | 77 +-- Charts/Classes/Utils/ChartTransformer.swift | 64 +- Charts/Classes/Utils/ChartUtils.swift | 67 +- .../Classes/Utils/ChartViewPortHandler.swift | 6 +- .../ChartsDemo.xcodeproj/project.pbxproj | 41 +- .../Classes/Components/BalloonMarker.swift | 2 +- .../Components/MyCustomXValueFormatter.h | 14 - .../Components/MyCustomXValueFormatter.m | 36 - ChartsDemo/Classes/DemoBaseViewController.h | 1 - ChartsDemo/Classes/DemoBaseViewController.m | 13 - ChartsDemo/Classes/DemoListViewController.m | 1 + .../Demos/AnotherBarChartViewController.m | 20 +- .../Classes/Demos/BarChartViewController.m | 45 +- .../Classes/Demos/BubbleChartViewController.m | 28 +- .../Demos/CandleStickChartViewController.m | 16 +- .../Demos/ColoredLineChartViewController.m | 13 +- .../Demos/CombinedChartViewController.m | 70 +- .../Demos/CubicLineChartViewController.m | 22 +- .../Demos/HorizontalBarChartViewController.m | 30 +- .../Classes/Demos/LineChart1ViewController.m | 25 +- .../Classes/Demos/LineChart2ViewController.m | 31 +- .../Demos/MultipleBarChartViewController.m | 65 +- .../Demos/MultipleBarChartViewController.xib | 5 +- .../Demos/MultipleLinesChartViewController.m | 13 +- .../NegativeStackedBarChartViewController.m | 61 +- .../Classes/Demos/PieChartViewController.m | 13 +- .../Demos/PiePolylineChartViewController.m | 13 +- .../PositiveNegativeBarChartViewController.m | 56 +- .../Classes/Demos/RadarChartViewController.m | 102 ++- .../Demos/RadarChartViewController.xib | 24 +- .../Demos/ScatterChartViewController.m | 25 +- .../Demos/SinusBarChartViewController.m | 17 +- .../Demos/StackedBarChartViewController.m | 33 +- .../Formatters/DayAxisValueFormatter.h | 16 + .../Formatters/DayAxisValueFormatter.m | 162 +++++ .../Formatters/IntAxisValueFormatter.h | 14 + .../Formatters/IntAxisValueFormatter.m | 21 + .../RealmBase/RealmDemoBaseViewController.m | 24 +- ChartsDemo/Classes/RealmBase/RealmDemoData.h | 49 +- ChartsDemo/Classes/RealmBase/RealmDemoData.m | 75 +- ChartsDemo/Classes/RealmBase/Score.h | 4 +- ChartsDemo/Classes/RealmBase/Score.m | 2 +- .../RealmDemos/RealmBarChartViewController.m | 10 +- .../RealmBubbleChartViewController.m | 6 +- .../RealmCandleChartViewController.m | 8 +- .../RealmHorizontalBarChartViewController.m | 9 +- .../RealmHorizontalBarChartViewController.xib | 4 +- .../RealmDemos/RealmLineChartViewController.m | 8 +- .../RealmDemos/RealmPieChartViewController.m | 6 +- .../RealmRadarChartViewController.m | 9 +- .../RealmScatterChartViewController.m | 8 +- .../RealmWikiExampleChartViewController.m | 47 +- .../ChartsRealm.xcodeproj/project.pbxproj | 56 -- ChartsRealm/Classes/Data/RealmBarData.swift | 34 - .../Classes/Data/RealmBarDataSet.swift | 136 ++-- .../Classes/Data/RealmBaseDataSet.swift | 249 +++---- .../Classes/Data/RealmBubbleData.swift | 34 - .../Classes/Data/RealmBubbleDataSet.swift | 110 +-- .../Classes/Data/RealmCandleData.swift | 34 - .../Classes/Data/RealmCandleDataSet.swift | 58 +- ChartsRealm/Classes/Data/RealmLineData.swift | 34 - ChartsRealm/Classes/Data/RealmPieData.swift | 34 - .../Classes/Data/RealmPieDataSet.swift | 28 + ChartsRealm/Classes/Data/RealmRadarData.swift | 34 - .../Classes/Data/RealmScatterData.swift | 34 - .../Classes/Utils/RealmChartUtils.swift | 22 - 155 files changed, 4628 insertions(+), 5349 deletions(-) create mode 100644 Charts/Classes/Data/Implementations/Standard/ChartDataEntryBase.swift create mode 100644 Charts/Classes/Data/Implementations/Standard/PieChartDataEntry.swift create mode 100644 Charts/Classes/Filters/ChartDataApproximator.swift delete mode 100644 Charts/Classes/Filters/ChartDataApproximatorFilter.swift delete mode 100644 Charts/Classes/Filters/ChartDataBaseFilter.swift create mode 100644 Charts/Classes/Formatters/ChartAxisValueFormatter.swift create mode 100644 Charts/Classes/Formatters/ChartDefaultAxisValueFormatter.swift delete mode 100644 Charts/Classes/Formatters/ChartDefaultXAxisValueFormatter.swift delete mode 100644 Charts/Classes/Formatters/ChartXAxisValueFormatter.swift create mode 100644 Charts/Classes/Renderers/BarLineScatterCandleBubbleChartRenderer.swift delete mode 100644 Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift delete mode 100644 ChartsDemo/Classes/Components/MyCustomXValueFormatter.h delete mode 100644 ChartsDemo/Classes/Components/MyCustomXValueFormatter.m create mode 100644 ChartsDemo/Classes/Formatters/DayAxisValueFormatter.h create mode 100644 ChartsDemo/Classes/Formatters/DayAxisValueFormatter.m create mode 100644 ChartsDemo/Classes/Formatters/IntAxisValueFormatter.h create mode 100644 ChartsDemo/Classes/Formatters/IntAxisValueFormatter.m delete mode 100644 ChartsRealm/Classes/Data/RealmBarData.swift delete mode 100644 ChartsRealm/Classes/Data/RealmBubbleData.swift delete mode 100644 ChartsRealm/Classes/Data/RealmCandleData.swift delete mode 100644 ChartsRealm/Classes/Data/RealmLineData.swift delete mode 100644 ChartsRealm/Classes/Data/RealmPieData.swift delete mode 100644 ChartsRealm/Classes/Data/RealmRadarData.swift delete mode 100644 ChartsRealm/Classes/Data/RealmScatterData.swift diff --git a/Charts/Charts.xcodeproj/project.pbxproj b/Charts/Charts.xcodeproj/project.pbxproj index de73c217be..2cfcf4f048 100644 --- a/Charts/Charts.xcodeproj/project.pbxproj +++ b/Charts/Charts.xcodeproj/project.pbxproj @@ -11,9 +11,8 @@ 06AEE7AE1BDC3FC6009875AE /* LineChartTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06AEE7AD1BDC3FC6009875AE /* LineChartTests.swift */; }; 06AEE7C21BDC4277009875AE /* FBSnapshotTestCase.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 06AEE7C11BDC4277009875AE /* FBSnapshotTestCase.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 5597263B1BDABBDB00E05E59 /* Charts.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B680D3C1A9D1AD90026A057 /* Charts.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5597263C1BDABC0500E05E59 /* ChartDefaultXAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D51BB0A86F00890030 /* ChartDefaultXAxisValueFormatter.swift */; }; + 5597263C1BDABC0500E05E59 /* ChartDefaultAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D51BB0A86F00890030 /* ChartDefaultAxisValueFormatter.swift */; }; 5597263D1BDABC0500E05E59 /* ChartFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D61BB0A86F00890030 /* ChartFillFormatter.swift */; }; - 5597263E1BDABC0500E05E59 /* ChartXAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D71BB0A86F00890030 /* ChartXAxisValueFormatter.swift */; }; 5597263F1BDABC0500E05E59 /* CombinedHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAAA8551BB08E1D00B20D4D /* CombinedHighlighter.swift */; }; 559726401BDABC0500E05E59 /* BarChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD4C5751BCDBF6C00462351 /* BarChartDataProvider.swift */; }; 559726411BDABC0500E05E59 /* BarLineScatterCandleBubbleChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD4C5761BCDBF6C00462351 /* BarLineScatterCandleBubbleChartDataProvider.swift */; }; @@ -29,6 +28,9 @@ 5B0032491B6525FC00B6A2FE /* ChartHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0032481B6525FC00B6A2FE /* ChartHighlighter.swift */; }; 5B00324B1B652BF900B6A2FE /* BarChartHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B00324A1B652BF900B6A2FE /* BarChartHighlighter.swift */; }; 5B00324D1B65351C00B6A2FE /* HorizontalBarChartHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B00324C1B65351C00B6A2FE /* HorizontalBarChartHighlighter.swift */; }; + 5B185BFF1D390C4800B9A953 /* PieChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B185BFE1D390C4800B9A953 /* PieChartDataEntry.swift */; }; + 5B185C001D390C4800B9A953 /* PieChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B185BFE1D390C4800B9A953 /* PieChartDataEntry.swift */; }; + 5B185C011D390C4800B9A953 /* PieChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B185BFE1D390C4800B9A953 /* PieChartDataEntry.swift */; }; 5B1B1BC71C7B8C0F000464B5 /* Charts.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B680D3C1A9D1AD90026A057 /* Charts.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5B378F171AD500A4009414A4 /* ChartAnimationEasing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B378F161AD500A4009414A4 /* ChartAnimationEasing.swift */; }; 5B4AC1721C4AB2AF0028D1A6 /* ChartBaseDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4AC1711C4AB2AF0028D1A6 /* ChartBaseDataSet.swift */; }; @@ -39,9 +41,8 @@ 5B4AC1CE1C58F55B0028D1A6 /* LineRadarChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4AC1CC1C58F55B0028D1A6 /* LineRadarChartRenderer.swift */; }; 5B4BCD401AA9C4930063F019 /* ChartTransformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4BCD3F1AA9C4930063F019 /* ChartTransformer.swift */; }; 5B6556F71AB72BA000FFBFD3 /* ChartComponentBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6556F61AB72BA000FFBFD3 /* ChartComponentBase.swift */; }; - 5B6654D91BB0A86F00890030 /* ChartDefaultXAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D51BB0A86F00890030 /* ChartDefaultXAxisValueFormatter.swift */; }; + 5B6654D91BB0A86F00890030 /* ChartDefaultAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D51BB0A86F00890030 /* ChartDefaultAxisValueFormatter.swift */; }; 5B6654DA1BB0A86F00890030 /* ChartFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D61BB0A86F00890030 /* ChartFillFormatter.swift */; }; - 5B6654DB1BB0A86F00890030 /* ChartXAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D71BB0A86F00890030 /* ChartXAxisValueFormatter.swift */; }; 5B680D1F1A9D17C30026A057 /* ChartAxisBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6A1A9D151C00CE82E1 /* ChartAxisBase.swift */; }; 5B680D201A9D17C30026A057 /* ChartLegend.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6B1A9D151C00CE82E1 /* ChartLegend.swift */; }; 5B680D211A9D17C30026A057 /* ChartLimitLine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6C1A9D151C00CE82E1 /* ChartLimitLine.swift */; }; @@ -55,7 +56,6 @@ 5B6A546E1AA5D2DC000F57C2 /* ChartAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A546D1AA5D2DC000F57C2 /* ChartAnimator.swift */; }; 5B6A54701AA5DB34000F57C2 /* ChartRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A546F1AA5DB34000F57C2 /* ChartRendererBase.swift */; }; 5B6A54741AA5DEDC000F57C2 /* ChartXAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54731AA5DEDC000F57C2 /* ChartXAxisRenderer.swift */; }; - 5B6A54761AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54751AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift */; }; 5B6A54781AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54771AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift */; }; 5B6A547C1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547B1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift */; }; 5B6A547E1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547D1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift */; }; @@ -81,6 +81,9 @@ 5B6A54A91AA66BBA000F57C2 /* RadarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54A81AA66BBA000F57C2 /* RadarChartView.swift */; }; 5B6A54AB1AA66BC8000F57C2 /* ScatterChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54AA1AA66BC8000F57C2 /* ScatterChartView.swift */; }; 5B6A54AC1AA66C1E000F57C2 /* ChartAxisRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54711AA5DCA8000F57C2 /* ChartAxisRendererBase.swift */; }; + 5B72BF931D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B72BF921D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift */; }; + 5B72BF941D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B72BF921D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift */; }; + 5B72BF951D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B72BF921D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift */; }; 5B8FE2B11B68FFF900910C9E /* LineScatterCandleRadarChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8FE2B01B68FFF900910C9E /* LineScatterCandleRadarChartRenderer.swift */; }; 5B9A0C241C83A7E200ED8ED8 /* ChartViewPortJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA7491C7CAA4E00F83F3B /* ChartViewPortJob.swift */; }; 5B9A0C251C83A7E200ED8ED8 /* MoveChartViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BCAA74C1C7CAB4900F83F3B /* MoveChartViewJob.swift */; }; @@ -91,8 +94,7 @@ 5B9D74191C688BDD00267DDD /* ChartDefaultFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8E0AAD1C63737400438BAF /* ChartDefaultFillFormatter.swift */; }; 5B9D741A1C688BDD00267DDD /* ChartDefaultFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8E0AAD1C63737400438BAF /* ChartDefaultFillFormatter.swift */; }; 5BA8EC7D1A9D151C00CE82E1 /* ChartViewBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC671A9D151C00CE82E1 /* ChartViewBase.swift */; }; - 5BA8EC881A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift */; }; - 5BA8EC891A9D151C00CE82E1 /* ChartDataBaseFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */; }; + 5BA8EC881A9D151C00CE82E1 /* ChartDataApproximator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximator.swift */; }; 5BAAA8561BB08E1D00B20D4D /* CombinedHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAAA8551BB08E1D00B20D4D /* CombinedHighlighter.swift */; }; 5BB6EC1D1ACC28AB006E9C25 /* ChartTransformerHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BB6EC1C1ACC28AB006E9C25 /* ChartTransformerHorizontalBarChart.swift */; }; 5BBBD9E11C80976300D01235 /* ZoomChartViewJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BBBD9E01C80976300D01235 /* ZoomChartViewJob.swift */; }; @@ -119,6 +121,10 @@ 5BD4C5821BCDBF6C00462351 /* ScatterChartDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD4C57B1BCDBF6C00462351 /* ScatterChartDataProvider.swift */; }; 5BD8F06D1AB897D500566E05 /* ChartViewPortHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BD8F06C1AB897D500566E05 /* ChartViewPortHandler.swift */; }; 5BD8F06E1AB89AD800566E05 /* HorizontalBarChartView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54A01AA66B6A000F57C2 /* HorizontalBarChartView.swift */; }; + 5BE377E01D44A17A006EB34F /* ChartAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE377DF1D44A17A006EB34F /* ChartAxisValueFormatter.swift */; }; + 5BE377E91D44D6D0006EB34F /* ChartDataEntryBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE377E81D44D6D0006EB34F /* ChartDataEntryBase.swift */; }; + 5BE377EA1D44D6D0006EB34F /* ChartDataEntryBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE377E81D44D6D0006EB34F /* ChartDataEntryBase.swift */; }; + 5BE377EB1D44D6D0006EB34F /* ChartDataEntryBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BE377E81D44D6D0006EB34F /* ChartDataEntryBase.swift */; }; 659400A01BF463C1004F9C27 /* BarChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 659400871BF463C1004F9C27 /* BarChartData.swift */; }; 659400A11BF463C1004F9C27 /* BarChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 659400871BF463C1004F9C27 /* BarChartData.swift */; }; 659400A21BF463C1004F9C27 /* BarChartDataEntry.swift in Sources */ = {isa = PBXBuildFile; fileRef = 659400881BF463C1004F9C27 /* BarChartDataEntry.swift */; }; @@ -227,12 +233,10 @@ 65B3F5FE1C7370E5000983D0 /* RadarChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6594009D1BF463C1004F9C27 /* RadarChartDataSet.swift */; }; 65B3F5FF1C7370E5000983D0 /* ScatterChartData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6594009E1BF463C1004F9C27 /* ScatterChartData.swift */; }; 65B3F6001C7370E5000983D0 /* ScatterChartDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6594009F1BF463C1004F9C27 /* ScatterChartDataSet.swift */; }; - 65B3F6011C7370E5000983D0 /* ChartDataApproximatorFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift */; }; - 65B3F6021C7370E5000983D0 /* ChartDataBaseFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */; }; + 65B3F6011C7370E5000983D0 /* ChartDataApproximator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximator.swift */; }; 65B3F6031C7370E5000983D0 /* ChartDefaultFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B8E0AAD1C63737400438BAF /* ChartDefaultFillFormatter.swift */; }; - 65B3F6041C7370E5000983D0 /* ChartDefaultXAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D51BB0A86F00890030 /* ChartDefaultXAxisValueFormatter.swift */; }; + 65B3F6041C7370E5000983D0 /* ChartDefaultAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D51BB0A86F00890030 /* ChartDefaultAxisValueFormatter.swift */; }; 65B3F6051C7370E5000983D0 /* ChartFillFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D61BB0A86F00890030 /* ChartFillFormatter.swift */; }; - 65B3F6061C7370E5000983D0 /* ChartXAxisValueFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D71BB0A86F00890030 /* ChartXAxisValueFormatter.swift */; }; 65B3F6071C7370E5000983D0 /* ChartHighlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0032441B6524AD00B6A2FE /* ChartHighlight.swift */; }; 65B3F6081C7370E5000983D0 /* ChartRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0032461B6524D300B6A2FE /* ChartRange.swift */; }; 65B3F6091C7370E5000983D0 /* ChartHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0032481B6525FC00B6A2FE /* ChartHighlighter.swift */; }; @@ -262,7 +266,6 @@ 65B3F6211C7370E5000983D0 /* ScatterChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54841AA669C9000F57C2 /* ScatterChartRenderer.swift */; }; 65B3F6221C7370E5000983D0 /* ChartAxisRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54711AA5DCA8000F57C2 /* ChartAxisRendererBase.swift */; }; 65B3F6231C7370E5000983D0 /* ChartXAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54731AA5DEDC000F57C2 /* ChartXAxisRenderer.swift */; }; - 65B3F6241C7370E5000983D0 /* ChartXAxisRendererBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54751AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift */; }; 65B3F6251C7370E5000983D0 /* ChartXAxisRendererHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547B1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift */; }; 65B3F6261C7370E5000983D0 /* ChartXAxisRendererRadarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54771AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift */; }; 65B3F6271C7370E5000983D0 /* ChartYAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547D1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift */; }; @@ -324,8 +327,7 @@ A52C5C511BAC5D1100594CDD /* ChartMarker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A546A1AA5C23F000F57C2 /* ChartMarker.swift */; }; A52C5C521BAC5D1100594CDD /* ChartXAxis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6D1A9D151C00CE82E1 /* ChartXAxis.swift */; }; A52C5C531BAC5D1100594CDD /* ChartYAxis.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC6E1A9D151C00CE82E1 /* ChartYAxis.swift */; }; - A52C5C6D1BAC5D1100594CDD /* ChartDataApproximatorFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift */; }; - A52C5C6E1BAC5D1100594CDD /* ChartDataBaseFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */; }; + A52C5C6D1BAC5D1100594CDD /* ChartDataApproximator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximator.swift */; }; A52C5C6F1BAC5D1100594CDD /* ChartHighlight.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0032441B6524AD00B6A2FE /* ChartHighlight.swift */; }; A52C5C701BAC5D1100594CDD /* ChartRange.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0032461B6524D300B6A2FE /* ChartRange.swift */; }; A52C5C711BAC5D1100594CDD /* ChartHighlighter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B0032481B6525FC00B6A2FE /* ChartHighlighter.swift */; }; @@ -346,7 +348,6 @@ A52C5C801BAC5D1200594CDD /* ChartRendererBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A546F1AA5DB34000F57C2 /* ChartRendererBase.swift */; }; A52C5C811BAC5D1200594CDD /* ScatterChartRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54841AA669C9000F57C2 /* ScatterChartRenderer.swift */; }; A52C5C821BAC5D1200594CDD /* ChartXAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54731AA5DEDC000F57C2 /* ChartXAxisRenderer.swift */; }; - A52C5C831BAC5D1200594CDD /* ChartXAxisRendererBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54751AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift */; }; A52C5C841BAC5D1200594CDD /* ChartXAxisRendererHorizontalBarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547B1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift */; }; A52C5C851BAC5D1200594CDD /* ChartXAxisRendererRadarChart.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A54771AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift */; }; A52C5C861BAC5D1200594CDD /* ChartYAxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B6A547D1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift */; }; @@ -396,22 +397,21 @@ 5B0032481B6525FC00B6A2FE /* ChartHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartHighlighter.swift; sourceTree = ""; }; 5B00324A1B652BF900B6A2FE /* BarChartHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartHighlighter.swift; sourceTree = ""; }; 5B00324C1B65351C00B6A2FE /* HorizontalBarChartHighlighter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HorizontalBarChartHighlighter.swift; sourceTree = ""; }; + 5B185BFE1D390C4800B9A953 /* PieChartDataEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PieChartDataEntry.swift; sourceTree = ""; }; 5B378F161AD500A4009414A4 /* ChartAnimationEasing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAnimationEasing.swift; sourceTree = ""; }; 5B4AC1711C4AB2AF0028D1A6 /* ChartBaseDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartBaseDataSet.swift; sourceTree = ""; }; 5B4AC1C91C58A2B90028D1A6 /* ChartFill.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartFill.swift; sourceTree = ""; }; 5B4AC1CC1C58F55B0028D1A6 /* LineRadarChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineRadarChartRenderer.swift; sourceTree = ""; }; 5B4BCD3F1AA9C4930063F019 /* ChartTransformer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartTransformer.swift; sourceTree = ""; }; 5B6556F61AB72BA000FFBFD3 /* ChartComponentBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartComponentBase.swift; sourceTree = ""; }; - 5B6654D51BB0A86F00890030 /* ChartDefaultXAxisValueFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDefaultXAxisValueFormatter.swift; sourceTree = ""; }; + 5B6654D51BB0A86F00890030 /* ChartDefaultAxisValueFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDefaultAxisValueFormatter.swift; sourceTree = ""; }; 5B6654D61BB0A86F00890030 /* ChartFillFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartFillFormatter.swift; sourceTree = ""; }; - 5B6654D71BB0A86F00890030 /* ChartXAxisValueFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisValueFormatter.swift; sourceTree = ""; }; 5B680D3C1A9D1AD90026A057 /* Charts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Charts.h; sourceTree = ""; }; 5B6A546A1AA5C23F000F57C2 /* ChartMarker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartMarker.swift; sourceTree = ""; }; 5B6A546D1AA5D2DC000F57C2 /* ChartAnimator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAnimator.swift; sourceTree = ""; }; 5B6A546F1AA5DB34000F57C2 /* ChartRendererBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartRendererBase.swift; sourceTree = ""; }; 5B6A54711AA5DCA8000F57C2 /* ChartAxisRendererBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAxisRendererBase.swift; sourceTree = ""; }; 5B6A54731AA5DEDC000F57C2 /* ChartXAxisRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisRenderer.swift; sourceTree = ""; }; - 5B6A54751AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisRendererBarChart.swift; sourceTree = ""; }; 5B6A54771AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisRendererRadarChart.swift; sourceTree = ""; }; 5B6A547B1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxisRendererHorizontalBarChart.swift; sourceTree = ""; }; 5B6A547D1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartYAxisRenderer.swift; sourceTree = ""; }; @@ -437,6 +437,7 @@ 5B6A54A61AA66BA7000F57C2 /* PieRadarChartViewBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PieRadarChartViewBase.swift; sourceTree = ""; }; 5B6A54A81AA66BBA000F57C2 /* RadarChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadarChartView.swift; sourceTree = ""; }; 5B6A54AA1AA66BC8000F57C2 /* ScatterChartView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScatterChartView.swift; sourceTree = ""; }; + 5B72BF921D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarLineScatterCandleBubbleChartRenderer.swift; sourceTree = ""; }; 5B8E0AAD1C63737400438BAF /* ChartDefaultFillFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDefaultFillFormatter.swift; sourceTree = ""; }; 5B8FE2B01B68FFF900910C9E /* LineScatterCandleRadarChartRenderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineScatterCandleRadarChartRenderer.swift; sourceTree = ""; }; 5BA8EC401A9D14DC00CE82E1 /* Charts.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Charts.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -447,8 +448,7 @@ 5BA8EC6C1A9D151C00CE82E1 /* ChartLimitLine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartLimitLine.swift; sourceTree = ""; }; 5BA8EC6D1A9D151C00CE82E1 /* ChartXAxis.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartXAxis.swift; sourceTree = ""; }; 5BA8EC6E1A9D151C00CE82E1 /* ChartYAxis.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartYAxis.swift; sourceTree = ""; }; - 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataApproximatorFilter.swift; sourceTree = ""; }; - 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataBaseFilter.swift; sourceTree = ""; }; + 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataApproximator.swift; sourceTree = ""; }; 5BA8EC791A9D151C00CE82E1 /* ChartColorTemplates.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartColorTemplates.swift; sourceTree = ""; }; 5BA8EC7B1A9D151C00CE82E1 /* ChartSelectionDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartSelectionDetail.swift; sourceTree = ""; }; 5BA8EC7C1A9D151C00CE82E1 /* ChartUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartUtils.swift; sourceTree = ""; }; @@ -471,6 +471,8 @@ 5BD4C57A1BCDBF6C00462351 /* LineChartDataProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LineChartDataProvider.swift; sourceTree = ""; }; 5BD4C57B1BCDBF6C00462351 /* ScatterChartDataProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ScatterChartDataProvider.swift; sourceTree = ""; }; 5BD8F06C1AB897D500566E05 /* ChartViewPortHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartViewPortHandler.swift; sourceTree = ""; }; + 5BE377DF1D44A17A006EB34F /* ChartAxisValueFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartAxisValueFormatter.swift; sourceTree = ""; }; + 5BE377E81D44D6D0006EB34F /* ChartDataEntryBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChartDataEntryBase.swift; sourceTree = ""; }; 659400871BF463C1004F9C27 /* BarChartData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartData.swift; sourceTree = ""; }; 659400881BF463C1004F9C27 /* BarChartDataEntry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartDataEntry.swift; sourceTree = ""; }; 659400891BF463C1004F9C27 /* BarChartDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BarChartDataSet.swift; sourceTree = ""; }; @@ -595,10 +597,10 @@ 5B6654D41BB0A86F00890030 /* Formatters */ = { isa = PBXGroup; children = ( + 5BE377DF1D44A17A006EB34F /* ChartAxisValueFormatter.swift */, + 5B6654D51BB0A86F00890030 /* ChartDefaultAxisValueFormatter.swift */, 5B8E0AAD1C63737400438BAF /* ChartDefaultFillFormatter.swift */, - 5B6654D51BB0A86F00890030 /* ChartDefaultXAxisValueFormatter.swift */, 5B6654D61BB0A86F00890030 /* ChartFillFormatter.swift */, - 5B6654D71BB0A86F00890030 /* ChartXAxisValueFormatter.swift */, ); path = Formatters; sourceTree = ""; @@ -616,6 +618,7 @@ isa = PBXGroup; children = ( 5B6A54961AA66AD2000F57C2 /* BarChartRenderer.swift */, + 5B72BF921D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift */, 55E3565A1ADC63EB00A57971 /* BubbleChartRenderer.swift */, 5B6A54941AA66AC0000F57C2 /* CandleStickChartRenderer.swift */, 5B6A54921AA66AAB000F57C2 /* CombinedChartRenderer.swift */, @@ -631,7 +634,6 @@ 5B6A54841AA669C9000F57C2 /* ScatterChartRenderer.swift */, 5B6A54711AA5DCA8000F57C2 /* ChartAxisRendererBase.swift */, 5B6A54731AA5DEDC000F57C2 /* ChartXAxisRenderer.swift */, - 5B6A54751AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift */, 5B6A547B1AA5DF02000F57C2 /* ChartXAxisRendererHorizontalBarChart.swift */, 5B6A54771AA5DEF0000F57C2 /* ChartXAxisRendererRadarChart.swift */, 5B6A547D1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift */, @@ -735,8 +737,7 @@ 5BA8EC741A9D151C00CE82E1 /* Filters */ = { isa = PBXGroup; children = ( - 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift */, - 5BA8EC761A9D151C00CE82E1 /* ChartDataBaseFilter.swift */, + 5BA8EC751A9D151C00CE82E1 /* ChartDataApproximator.swift */, ); path = Filters; sourceTree = ""; @@ -808,6 +809,7 @@ 659400911BF463C1004F9C27 /* CandleChartDataSet.swift */, 659400921BF463C1004F9C27 /* ChartData.swift */, 659400931BF463C1004F9C27 /* ChartDataEntry.swift */, + 5BE377E81D44D6D0006EB34F /* ChartDataEntryBase.swift */, 659400941BF463C1004F9C27 /* ChartDataSet.swift */, 659400951BF463C1004F9C27 /* CombinedChartData.swift */, 659400961BF463C1004F9C27 /* LineChartData.swift */, @@ -815,6 +817,7 @@ 659400981BF463C1004F9C27 /* LineRadarChartDataSet.swift */, 659400991BF463C1004F9C27 /* LineScatterCandleRadarChartDataSet.swift */, 6594009A1BF463C1004F9C27 /* PieChartData.swift */, + 5B185BFE1D390C4800B9A953 /* PieChartDataEntry.swift */, 6594009B1BF463C1004F9C27 /* PieChartDataSet.swift */, 6594009C1BF463C1004F9C27 /* RadarChartData.swift */, 6594009D1BF463C1004F9C27 /* RadarChartDataSet.swift */, @@ -1049,21 +1052,21 @@ 659400BE1BF463C1004F9C27 /* LineChartData.swift in Sources */, 5B6A54AB1AA66BC8000F57C2 /* ScatterChartView.swift in Sources */, 5B6A548B1AA66A3D000F57C2 /* LineChartRenderer.swift in Sources */, + 5BE377E91D44D6D0006EB34F /* ChartDataEntryBase.swift in Sources */, 5B6A54821AA5DF34000F57C2 /* ChartYAxisRendererRadarChart.swift in Sources */, 659400B81BF463C1004F9C27 /* ChartDataEntry.swift in Sources */, 5BCAA74A1C7CAA4E00F83F3B /* ChartViewPortJob.swift in Sources */, 5BD4C5821BCDBF6C00462351 /* ScatterChartDataProvider.swift in Sources */, 5B6A54931AA66AAB000F57C2 /* CombinedChartRenderer.swift in Sources */, 659400C01BF463C1004F9C27 /* LineChartDataSet.swift in Sources */, + 5B72BF931D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift in Sources */, 659400CE1BF463C2004F9C27 /* ScatterChartData.swift in Sources */, 659400D01BF463C2004F9C27 /* ScatterChartDataSet.swift in Sources */, 659400B21BF463C1004F9C27 /* CandleChartDataEntry.swift in Sources */, 659400B61BF463C1004F9C27 /* ChartData.swift in Sources */, 5B680D221A9D17C30026A057 /* ChartXAxis.swift in Sources */, - 5BA8EC891A9D151C00CE82E1 /* ChartDataBaseFilter.swift in Sources */, 659400A21BF463C1004F9C27 /* BarChartDataEntry.swift in Sources */, 5BCAA7501C7CAD4C00F83F3B /* AnimatedViewPortJob.swift in Sources */, - 5B6654DB1BB0A86F00890030 /* ChartXAxisValueFormatter.swift in Sources */, 5B6A54A31AA66B7C000F57C2 /* LineChartView.swift in Sources */, 5B6A54891AA66A1A000F57C2 /* PieChartRenderer.swift in Sources */, 5B6A54991AA66B14000F57C2 /* BarChartView.swift in Sources */, @@ -1100,11 +1103,10 @@ 65B3F6321C739F7F000983D0 /* ChartPlatform.swift in Sources */, 5BD4C5811BCDBF6C00462351 /* LineChartDataProvider.swift in Sources */, 659400AC1BF463C1004F9C27 /* BubbleChartDataEntry.swift in Sources */, - 5B6A54761AA5DEE3000F57C2 /* ChartXAxisRendererBarChart.swift in Sources */, 5B6A54851AA669C9000F57C2 /* ScatterChartRenderer.swift in Sources */, 65F06FAD1BE826010074498D /* IPieChartDataSet.swift in Sources */, 5B6A549D1AA66B3C000F57C2 /* CandleStickChartView.swift in Sources */, - 5BA8EC881A9D151C00CE82E1 /* ChartDataApproximatorFilter.swift in Sources */, + 5BA8EC881A9D151C00CE82E1 /* ChartDataApproximator.swift in Sources */, 5B6A549B1AA66B2C000F57C2 /* BarLineChartViewBase.swift in Sources */, 5B6A54A51AA66B92000F57C2 /* PieChartView.swift in Sources */, 5B6A54911AA66A8D000F57C2 /* ChartDataRendererBase.swift in Sources */, @@ -1132,9 +1134,10 @@ 65F06FA11BE815220074498D /* IBubbleChartDataSet.swift in Sources */, 55E356531ADC63BF00A57971 /* BubbleChartView.swift in Sources */, 659400B01BF463C1004F9C27 /* CandleChartData.swift in Sources */, - 5B6654D91BB0A86F00890030 /* ChartDefaultXAxisValueFormatter.swift in Sources */, + 5B6654D91BB0A86F00890030 /* ChartDefaultAxisValueFormatter.swift in Sources */, 659400AA1BF463C1004F9C27 /* BubbleChartData.swift in Sources */, 65F06F981BE812D90074498D /* ILineScatterCandleRadarChartDataSet.swift in Sources */, + 5B185BFF1D390C4800B9A953 /* PieChartDataEntry.swift in Sources */, 5B6A547E1AA5DF1A000F57C2 /* ChartYAxisRenderer.swift in Sources */, 659400A61BF463C1004F9C27 /* BarLineScatterCandleBubbleChartData.swift in Sources */, 65F06FA41BE816050074498D /* ILineChartDataSet.swift in Sources */, @@ -1148,6 +1151,7 @@ 5BCAA7561C7CB0DE00F83F3B /* AnimatedZoomViewJob.swift in Sources */, 5B0032471B6524D300B6A2FE /* ChartRange.swift in Sources */, 5BD8F06E1AB89AD800566E05 /* HorizontalBarChartView.swift in Sources */, + 5BE377E01D44A17A006EB34F /* ChartAxisValueFormatter.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1218,16 +1222,16 @@ 65B3F5FF1C7370E5000983D0 /* ScatterChartData.swift in Sources */, 65B3F6001C7370E5000983D0 /* ScatterChartDataSet.swift in Sources */, 65B3F6341C739F7F000983D0 /* ChartPlatform.swift in Sources */, - 65B3F6011C7370E5000983D0 /* ChartDataApproximatorFilter.swift in Sources */, - 65B3F6021C7370E5000983D0 /* ChartDataBaseFilter.swift in Sources */, + 65B3F6011C7370E5000983D0 /* ChartDataApproximator.swift in Sources */, 65B3F6031C7370E5000983D0 /* ChartDefaultFillFormatter.swift in Sources */, - 65B3F6041C7370E5000983D0 /* ChartDefaultXAxisValueFormatter.swift in Sources */, + 5B72BF951D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift in Sources */, + 65B3F6041C7370E5000983D0 /* ChartDefaultAxisValueFormatter.swift in Sources */, 65B3F6051C7370E5000983D0 /* ChartFillFormatter.swift in Sources */, - 65B3F6061C7370E5000983D0 /* ChartXAxisValueFormatter.swift in Sources */, 65B3F6071C7370E5000983D0 /* ChartHighlight.swift in Sources */, 65B3F6081C7370E5000983D0 /* ChartRange.swift in Sources */, 5B9A0C291C83A7E200ED8ED8 /* AnimatedZoomViewJob.swift in Sources */, 65B3F6091C7370E5000983D0 /* ChartHighlighter.swift in Sources */, + 5BE377EB1D44D6D0006EB34F /* ChartDataEntryBase.swift in Sources */, 5B9A0C261C83A7E200ED8ED8 /* ZoomChartViewJob.swift in Sources */, 65B3F60A1C7370E5000983D0 /* BarChartHighlighter.swift in Sources */, 65B3F60B1C7370E5000983D0 /* CombinedHighlighter.swift in Sources */, @@ -1255,7 +1259,7 @@ 65B3F6211C7370E5000983D0 /* ScatterChartRenderer.swift in Sources */, 65B3F6221C7370E5000983D0 /* ChartAxisRendererBase.swift in Sources */, 65B3F6231C7370E5000983D0 /* ChartXAxisRenderer.swift in Sources */, - 65B3F6241C7370E5000983D0 /* ChartXAxisRendererBarChart.swift in Sources */, + 5B185C011D390C4800B9A953 /* PieChartDataEntry.swift in Sources */, 65B3F6251C7370E5000983D0 /* ChartXAxisRendererHorizontalBarChart.swift in Sources */, 65B3F6261C7370E5000983D0 /* ChartXAxisRendererRadarChart.swift in Sources */, 65B3F6271C7370E5000983D0 /* ChartYAxisRenderer.swift in Sources */, @@ -1275,11 +1279,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 5597263C1BDABC0500E05E59 /* ChartDefaultXAxisValueFormatter.swift in Sources */, + 5597263C1BDABC0500E05E59 /* ChartDefaultAxisValueFormatter.swift in Sources */, 5BCAA74E1C7CAB4900F83F3B /* MoveChartViewJob.swift in Sources */, 5597263D1BDABC0500E05E59 /* ChartFillFormatter.swift in Sources */, 659400BD1BF463C1004F9C27 /* CombinedChartData.swift in Sources */, - 5597263E1BDABC0500E05E59 /* ChartXAxisValueFormatter.swift in Sources */, 659400A91BF463C1004F9C27 /* BarLineScatterCandleBubbleChartDataSet.swift in Sources */, 5597263F1BDABC0500E05E59 /* CombinedHighlighter.swift in Sources */, 559726401BDABC0500E05E59 /* BarChartDataProvider.swift in Sources */, @@ -1334,20 +1337,21 @@ A52C5C521BAC5D1100594CDD /* ChartXAxis.swift in Sources */, 5B4AC1CB1C58A2B90028D1A6 /* ChartFill.swift in Sources */, A52C5C531BAC5D1100594CDD /* ChartYAxis.swift in Sources */, - A52C5C6D1BAC5D1100594CDD /* ChartDataApproximatorFilter.swift in Sources */, + A52C5C6D1BAC5D1100594CDD /* ChartDataApproximator.swift in Sources */, 659400A11BF463C1004F9C27 /* BarChartData.swift in Sources */, - A52C5C6E1BAC5D1100594CDD /* ChartDataBaseFilter.swift in Sources */, 65B3F6331C739F7F000983D0 /* ChartPlatform.swift in Sources */, A52C5C6F1BAC5D1100594CDD /* ChartHighlight.swift in Sources */, 659400AD1BF463C1004F9C27 /* BubbleChartDataEntry.swift in Sources */, A52C5C701BAC5D1100594CDD /* ChartRange.swift in Sources */, A52C5C711BAC5D1100594CDD /* ChartHighlighter.swift in Sources */, + 5B72BF941D490FDD0009B2C7 /* BarLineScatterCandleBubbleChartRenderer.swift in Sources */, 65F06FAE1BE826010074498D /* IPieChartDataSet.swift in Sources */, A52C5C721BAC5D1100594CDD /* BarChartHighlighter.swift in Sources */, A52C5C731BAC5D1100594CDD /* HorizontalBarChartHighlighter.swift in Sources */, 5B4AC1CE1C58F55B0028D1A6 /* LineRadarChartRenderer.swift in Sources */, A52C5C741BAC5D1100594CDD /* ChartAxisRendererBase.swift in Sources */, A52C5C751BAC5D1100594CDD /* BarChartRenderer.swift in Sources */, + 5BE377EA1D44D6D0006EB34F /* ChartDataEntryBase.swift in Sources */, A52C5C761BAC5D1100594CDD /* BubbleChartRenderer.swift in Sources */, A52C5C771BAC5D1100594CDD /* CandleStickChartRenderer.swift in Sources */, 65F06F9F1BE814800074498D /* IScatterChartDataSet.swift in Sources */, @@ -1365,7 +1369,6 @@ A52C5C801BAC5D1200594CDD /* ChartRendererBase.swift in Sources */, A52C5C811BAC5D1200594CDD /* ScatterChartRenderer.swift in Sources */, A52C5C821BAC5D1200594CDD /* ChartXAxisRenderer.swift in Sources */, - A52C5C831BAC5D1200594CDD /* ChartXAxisRendererBarChart.swift in Sources */, 659400CD1BF463C2004F9C27 /* RadarChartDataSet.swift in Sources */, 659400AF1BF463C1004F9C27 /* BubbleChartDataSet.swift in Sources */, 65F06FA21BE815220074498D /* IBubbleChartDataSet.swift in Sources */, @@ -1375,6 +1378,7 @@ A52C5C861BAC5D1200594CDD /* ChartYAxisRenderer.swift in Sources */, 659400AB1BF463C1004F9C27 /* BubbleChartData.swift in Sources */, 65F06F9C1BE813730074498D /* ILineScatterCandleRadarChartDataSet.swift in Sources */, + 5B185C001D390C4800B9A953 /* PieChartDataEntry.swift in Sources */, 65F06FA51BE816050074498D /* ILineChartDataSet.swift in Sources */, 659400A71BF463C1004F9C27 /* BarLineScatterCandleBubbleChartData.swift in Sources */, A52C5C871BAC5D1200594CDD /* ChartYAxisRendererHorizontalBarChart.swift in Sources */, diff --git a/Charts/Classes/Animation/ChartAnimationEasing.swift b/Charts/Classes/Animation/ChartAnimationEasing.swift index 208e72a7dc..cf0b51a8a5 100644 --- a/Charts/Classes/Animation/ChartAnimationEasing.swift +++ b/Charts/Classes/Animation/ChartAnimationEasing.swift @@ -51,7 +51,7 @@ public enum ChartEasingOption: Int case EaseInOutBounce } -public typealias ChartEasingFunctionBlock = ((elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat) +public typealias ChartEasingFunctionBlock = ((elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double) internal func easingFunctionFromOption(easing: ChartEasingOption) -> ChartEasingFunctionBlock { @@ -124,20 +124,20 @@ internal func easingFunctionFromOption(easing: ChartEasingOption) -> ChartEasing internal struct EasingFunctions { - internal static let Linear = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in return CGFloat(elapsed / duration); } + internal static let Linear = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in return Double(elapsed / duration); } - internal static let EaseInQuad = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseInQuad = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) return position * position } - internal static let EaseOutQuad = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseOutQuad = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) return -position * (position - 2.0) } - internal static let EaseInOutQuad = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / (duration / 2.0)) + internal static let EaseInOutQuad = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / (duration / 2.0)) if (position < 1.0) { return 0.5 * position * position @@ -145,19 +145,19 @@ internal struct EasingFunctions return -0.5 * ((--position) * (position - 2.0) - 1.0) } - internal static let EaseInCubic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseInCubic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) return position * position * position } - internal static let EaseOutCubic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseOutCubic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) position-- return (position * position * position + 1.0) } - internal static let EaseInOutCubic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / (duration / 2.0)) + internal static let EaseInOutCubic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / (duration / 2.0)) if (position < 1.0) { return 0.5 * position * position * position @@ -166,19 +166,19 @@ internal struct EasingFunctions return 0.5 * (position * position * position + 2.0) } - internal static let EaseInQuart = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseInQuart = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) return position * position * position * position } - internal static let EaseOutQuart = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseOutQuart = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) position-- return -(position * position * position * position - 1.0) } - internal static let EaseInOutQuart = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / (duration / 2.0)) + internal static let EaseInOutQuart = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / (duration / 2.0)) if (position < 1.0) { return 0.5 * position * position * position * position @@ -187,19 +187,19 @@ internal struct EasingFunctions return -0.5 * (position * position * position * position - 2.0) } - internal static let EaseInQuint = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseInQuint = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) return position * position * position * position * position } - internal static let EaseOutQuint = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseOutQuint = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) position-- return (position * position * position * position * position + 1.0) } - internal static let EaseInOutQuint = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / (duration / 2.0)) + internal static let EaseInOutQuint = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / (duration / 2.0)) if (position < 1.0) { return 0.5 * position * position * position * position * position @@ -211,30 +211,30 @@ internal struct EasingFunctions } } - internal static let EaseInSine = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInSine = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in var position: NSTimeInterval = elapsed / duration - return CGFloat( -cos(position * M_PI_2) + 1.0 ) + return Double( -cos(position * M_PI_2) + 1.0 ) } - internal static let EaseOutSine = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseOutSine = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in var position: NSTimeInterval = elapsed / duration - return CGFloat( sin(position * M_PI_2) ) + return Double( sin(position * M_PI_2) ) } - internal static let EaseInOutSine = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInOutSine = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in var position: NSTimeInterval = elapsed / duration - return CGFloat( -0.5 * (cos(M_PI * position) - 1.0) ) + return Double( -0.5 * (cos(M_PI * position) - 1.0) ) } - internal static let EaseInExpo = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - return (elapsed == 0) ? 0.0 : CGFloat(pow(2.0, 10.0 * (elapsed / duration - 1.0))) + internal static let EaseInExpo = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + return (elapsed == 0) ? 0.0 : Double(pow(2.0, 10.0 * (elapsed / duration - 1.0))) } - internal static let EaseOutExpo = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - return (elapsed == duration) ? 1.0 : (-CGFloat(pow(2.0, -10.0 * elapsed / duration)) + 1.0) + internal static let EaseOutExpo = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + return (elapsed == duration) ? 1.0 : (-Double(pow(2.0, -10.0 * elapsed / duration)) + 1.0) } - internal static let EaseInOutExpo = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInOutExpo = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in if (elapsed == 0) { return 0.0 @@ -247,35 +247,35 @@ internal struct EasingFunctions var position: NSTimeInterval = elapsed / (duration / 2.0) if (position < 1.0) { - return CGFloat( 0.5 * pow(2.0, 10.0 * (position - 1.0)) ) + return Double( 0.5 * pow(2.0, 10.0 * (position - 1.0)) ) } position = position - 1.0 - return CGFloat( 0.5 * (-pow(2.0, -10.0 * position) + 2.0) ) + return Double( 0.5 * (-pow(2.0, -10.0 * position) + 2.0) ) } - internal static let EaseInCirc = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) - return -(CGFloat(sqrt(1.0 - position * position)) - 1.0) + internal static let EaseInCirc = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) + return -(Double(sqrt(1.0 - position * position)) - 1.0) } - internal static let EaseOutCirc = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in - var position = CGFloat(elapsed / duration) + internal static let EaseOutCirc = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in + var position = Double(elapsed / duration) position-- - return CGFloat( sqrt(1 - position * position) ) + return Double( sqrt(1 - position * position) ) } - internal static let EaseInOutCirc = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInOutCirc = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in var position: NSTimeInterval = elapsed / (duration / 2.0) if (position < 1.0) { - return CGFloat( -0.5 * (sqrt(1.0 - position * position) - 1.0) ) + return Double( -0.5 * (sqrt(1.0 - position * position) - 1.0) ) } position -= 2.0 - return CGFloat( 0.5 * (sqrt(1.0 - position * position) + 1.0) ) + return Double( 0.5 * (sqrt(1.0 - position * position) + 1.0) ) } - internal static let EaseInElastic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInElastic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in if (elapsed == 0.0) { return 0.0 @@ -290,10 +290,10 @@ internal struct EasingFunctions var p = duration * 0.3 var s = p / (2.0 * M_PI) * asin(1.0) position -= 1.0 - return CGFloat( -(pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * M_PI) / p)) ) + return Double( -(pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * M_PI) / p)) ) } - internal static let EaseOutElastic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseOutElastic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in if (elapsed == 0.0) { return 0.0 @@ -307,10 +307,10 @@ internal struct EasingFunctions var p = duration * 0.3 var s = p / (2.0 * M_PI) * asin(1.0) - return CGFloat( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * M_PI) / p) + 1.0 ) + return Double( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * M_PI) / p) + 1.0 ) } - internal static let EaseInOutElastic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInOutElastic = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in if (elapsed == 0.0) { return 0.0 @@ -327,66 +327,66 @@ internal struct EasingFunctions if (position < 1.0) { position -= 1.0 - return CGFloat( -0.5 * (pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * M_PI) / p)) ) + return Double( -0.5 * (pow(2.0, 10.0 * position) * sin((position * duration - s) * (2.0 * M_PI) / p)) ) } position -= 1.0 - return CGFloat( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * M_PI) / p) * 0.5 + 1.0 ) + return Double( pow(2.0, -10.0 * position) * sin((position * duration - s) * (2.0 * M_PI) / p) * 0.5 + 1.0 ) } - internal static let EaseInBack = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInBack = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in let s: NSTimeInterval = 1.70158 var position: NSTimeInterval = elapsed / duration - return CGFloat( position * position * ((s + 1.0) * position - s) ) + return Double( position * position * ((s + 1.0) * position - s) ) } - internal static let EaseOutBack = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseOutBack = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in let s: NSTimeInterval = 1.70158 var position: NSTimeInterval = elapsed / duration position -= 1.0 - return CGFloat( position * position * ((s + 1.0) * position + s) + 1.0 ) + return Double( position * position * ((s + 1.0) * position + s) + 1.0 ) } - internal static let EaseInOutBack = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInOutBack = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in var s: NSTimeInterval = 1.70158 var position: NSTimeInterval = elapsed / (duration / 2.0) if (position < 1.0) { s *= 1.525 - return CGFloat( 0.5 * (position * position * ((s + 1.0) * position - s)) ) + return Double( 0.5 * (position * position * ((s + 1.0) * position - s)) ) } s *= 1.525 position -= 2.0 - return CGFloat( 0.5 * (position * position * ((s + 1.0) * position + s) + 2.0) ) + return Double( 0.5 * (position * position * ((s + 1.0) * position + s) + 2.0) ) } - internal static let EaseInBounce = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInBounce = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in return 1.0 - EaseOutBounce(duration - elapsed, duration) } - internal static let EaseOutBounce = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseOutBounce = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in var position: NSTimeInterval = elapsed / duration if (position < (1.0 / 2.75)) { - return CGFloat( 7.5625 * position * position ) + return Double( 7.5625 * position * position ) } else if (position < (2.0 / 2.75)) { position -= (1.5 / 2.75) - return CGFloat( 7.5625 * position * position + 0.75 ) + return Double( 7.5625 * position * position + 0.75 ) } else if (position < (2.5 / 2.75)) { position -= (2.25 / 2.75) - return CGFloat( 7.5625 * position * position + 0.9375 ) + return Double( 7.5625 * position * position + 0.9375 ) } else { position -= (2.625 / 2.75) - return CGFloat( 7.5625 * position * position + 0.984375 ) + return Double( 7.5625 * position * position + 0.984375 ) } } - internal static let EaseInOutBounce = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> CGFloat in + internal static let EaseInOutBounce = { (elapsed: NSTimeInterval, duration: NSTimeInterval) -> Double in if (elapsed < (duration / 2.0)) { return EaseInBounce(elapsed * 2.0, duration) * 0.5 diff --git a/Charts/Classes/Animation/ChartAnimator.swift b/Charts/Classes/Animation/ChartAnimator.swift index 16697a456f..e3e87ffa45 100644 --- a/Charts/Classes/Animation/ChartAnimator.swift +++ b/Charts/Classes/Animation/ChartAnimator.swift @@ -35,10 +35,10 @@ public class ChartAnimator: NSObject public var stopBlock: (() -> Void)? /// the phase that is animated and influences the drawn values on the x-axis - public var phaseX: CGFloat = 1.0 + public var phaseX: Double = 1.0 /// the phase that is animated and influences the drawn values on the y-axis - public var phaseY: CGFloat = 1.0 + public var phaseY: Double = 1.0 private var _startTimeX: NSTimeInterval = 0.0 private var _startTimeY: NSTimeInterval = 0.0 @@ -122,7 +122,7 @@ public class ChartAnimator: NSObject } else { - phaseX = CGFloat(elapsed / duration) + phaseX = Double(elapsed / duration) } } if (_enabledY) @@ -141,7 +141,7 @@ public class ChartAnimator: NSObject } else { - phaseY = CGFloat(elapsed / duration) + phaseY = Double(elapsed / duration) } } } diff --git a/Charts/Classes/Charts/BarChartView.swift b/Charts/Classes/Charts/BarChartView.swift index 9afd35a3f5..6ec08e6e3f 100644 --- a/Charts/Classes/Charts/BarChartView.swift +++ b/Charts/Classes/Charts/BarChartView.swift @@ -17,9 +17,6 @@ import CoreGraphics /// Chart that draws bars. public class BarChartView: BarLineChartViewBase, BarChartDataProvider { - /// flag that enables or disables the highlighting arrow - private var _drawHighlightArrowEnabled = false - /// if set to true, all values are drawn above their bars, instead of below their top private var _drawValueAboveBarEnabled = true @@ -31,30 +28,38 @@ public class BarChartView: BarLineChartViewBase, BarChartDataProvider super.initialize() renderer = BarChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) - _xAxisRenderer = ChartXAxisRendererBarChart(viewPortHandler: _viewPortHandler, xAxis: _xAxis, transformer: _leftAxisTransformer, chart: self) self.highlighter = BarChartHighlighter(chart: self) - - _xAxis._axisMinimum = -0.5 } internal override func calcMinMax() { - super.calcMinMax() - - guard let data = _data else { return } - - let barData = data as! BarChartData + guard let data = self.data as? BarChartData + else { return } - // increase deltax by 1 because the bars have a width of 1 - _xAxis.axisRange += 0.5 + if self.autoScaleMinMaxEnabled + { + data.calcMinMax() + } - // extend xDelta to make space for multiple datasets (if ther are one) - _xAxis.axisRange *= Double(data.dataSetCount) + if fitBars + { + _xAxis.calculate( + min: data.xMin - data.barWidth / 2.0, + max: data.xMax - data.barWidth / 2.0) + } + else + { + _xAxis.calculate(min: data.xMin, max: data.xMax) + } - let groupSpace = barData.groupSpace - _xAxis.axisRange += Double(barData.xValCount) * Double(groupSpace) - _xAxis._axisMaximum = _xAxis.axisRange - _xAxis._axisMinimum + // calculate axis range (min / max) according to provided data + _leftAxis.calculate( + min: data.getYMin(.Left), + max: data.getYMax(.Left)) + _rightAxis.calculate( + min: data.getYMin(.Right), + max: data.getYMax(.Right)) } /// - returns: the Highlight object (contains x-index and DataSet index) of the selected value at the given touch point inside the BarChart. @@ -73,18 +78,17 @@ public class BarChartView: BarLineChartViewBase, BarChartDataProvider public func getBarBounds(e: BarChartDataEntry) -> CGRect { guard let - set = _data?.getDataSetForEntry(e) as? IBarChartDataSet + data = _data as? BarChartData, + set = data.getDataSetForEntry(e) as? IBarChartDataSet else { return CGRectNull } - let barspace = set.barSpace - let y = CGFloat(e.value) - let x = CGFloat(e.xIndex) + let y = e.y + let x = e.x - let barWidth: CGFloat = 0.5 + let barWidth = data.barWidth - let spaceHalf = barspace / 2.0 - let left = x - barWidth + spaceHalf - let right = x + barWidth - spaceHalf + let left = x - barWidth / 2.0 + let right = x + barWidth / 2.0 let top = y >= 0.0 ? y : 0.0 let bottom = y <= 0.0 ? y : 0.0 @@ -95,41 +99,28 @@ public class BarChartView: BarLineChartViewBase, BarChartDataProvider return bounds } - public override var lowestVisibleXIndex: Int + /// Groups all BarDataSet objects this data object holds together by modifying the x-position of their entries. + /// Leaves space as specified by the parameters. + /// Calls `notifyDataSetChanged()` afterwards. + /// + /// - parameter fromX: the starting point on the x-axis where the grouping should begin + /// - parameter groupSpace: the space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f + /// - parameter barSpace: the space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f + public func groupBars(fromX fromX: Double, groupSpace: Double, barSpace: Double) { - let step = CGFloat(_data?.dataSetCount ?? 0) - let div = (step <= 1.0) ? 1.0 : step + (_data as! BarChartData).groupSpace - - var pt = CGPoint(x: _viewPortHandler.contentLeft, y: _viewPortHandler.contentBottom) - getTransformer(ChartYAxis.AxisDependency.Left).pixelToValue(&pt) - - return Int((pt.x <= CGFloat(chartXMin)) ? 0.0 : (pt.x / div) + 1.0) - } - - public override var highestVisibleXIndex: Int - { - let step = CGFloat(_data?.dataSetCount ?? 0) - let div = (step <= 1.0) ? 1.0 : step + (_data as! BarChartData).groupSpace - - var pt = CGPoint(x: _viewPortHandler.contentRight, y: _viewPortHandler.contentBottom) - getTransformer(ChartYAxis.AxisDependency.Left).pixelToValue(&pt) + guard let barData = self.barData + else + { + print("You need to set data for the chart before grouping bars.", terminator: "\n") + return + } - return Int((pt.x >= CGFloat(chartXMax)) ? CGFloat(chartXMax) / div : (pt.x / div)) + barData.groupBars(fromX: fromX, groupSpace: groupSpace, barSpace: barSpace) + notifyDataSetChanged() } // MARK: Accessors - /// flag that enables or disables the highlighting arrow - public var drawHighlightArrowEnabled: Bool - { - get { return _drawHighlightArrowEnabled; } - set - { - _drawHighlightArrowEnabled = newValue - setNeedsDisplay() - } - } - /// if set to true, all values are drawn above their bars, instead of below their top public var drawValueAboveBarEnabled: Bool { @@ -152,13 +143,14 @@ public class BarChartView: BarLineChartViewBase, BarChartDataProvider } } + /// Adds half of the bar width to each side of the x-axis range in order to allow the bars of the barchart to be fully displayed. + /// **default**: false + public var fitBars = false + // MARK: - BarChartDataProbider public var barData: BarChartData? { return _data as? BarChartData } - /// - returns: true if drawing the highlighting arrow is enabled, false if not - public var isDrawHighlightArrowEnabled: Bool { return drawHighlightArrowEnabled } - /// - returns: true if drawing values above bars is enabled, false if not public var isDrawValueAboveBarEnabled: Bool { return drawValueAboveBarEnabled } diff --git a/Charts/Classes/Charts/BarLineChartViewBase.swift b/Charts/Classes/Charts/BarLineChartViewBase.swift index fbb1e2db73..bbd471cc08 100644 --- a/Charts/Classes/Charts/BarLineChartViewBase.swift +++ b/Charts/Classes/Charts/BarLineChartViewBase.swift @@ -23,12 +23,12 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar { /// the maximum number of entries to which values will be drawn /// (entry numbers greater than this value will cause value-labels to disappear) - internal var _maxVisibleValueCount = 100 + internal var _maxVisibleCount = 100 /// flag that indicates if auto scaling on the y axis is enabled private var _autoScaleMinMaxEnabled = false - private var _autoScaleLastLowestVisibleXIndex: Int! - private var _autoScaleLastHighestVisibleXIndex: Int! + private var _autoScaleLastLowestVisibleX: Double? + private var _autoScaleLastHighestVisibleX: Double? private var _pinchZoomEnabled = false private var _doubleTapToZoomEnabled = true @@ -170,28 +170,21 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar let optionalContext = NSUIGraphicsGetCurrentContext() guard let context = optionalContext else { return } - - calcModulus() - - if (_xAxisRenderer !== nil) - { - _xAxisRenderer!.calcXBounds(chart: self, xAxisModulus: _xAxis.axisLabelModulus) - } - if (renderer !== nil) - { - renderer!.calcXBounds(chart: self, xAxisModulus: _xAxis.axisLabelModulus) - } // execute all drawing commands drawGridBackground(context: context) - if (_leftAxis.isEnabled) + if _leftAxis.isEnabled + { + _leftYAxisRenderer?.computeAxis(min: _leftAxis._axisMinimum, max: _leftAxis._axisMaximum, inverted: _leftAxis.isInverted) + } + if _rightAxis.isEnabled { - _leftYAxisRenderer?.computeAxis(yMin: _leftAxis._axisMinimum, yMax: _leftAxis._axisMaximum) + _rightYAxisRenderer?.computeAxis(min: _rightAxis._axisMinimum, max: _rightAxis._axisMaximum, inverted: _rightAxis.isInverted) } - if (_rightAxis.isEnabled) + if _xAxis.isEnabled { - _rightYAxisRenderer?.computeAxis(yMin: _rightAxis._axisMinimum, yMax: _rightAxis._axisMaximum) + _xAxisRenderer?.computeAxis(min: _xAxis._axisMinimum, max: _xAxis._axisMaximum, inverted: false) } _xAxisRenderer?.renderAxisLine(context: context) @@ -200,17 +193,17 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar if (_autoScaleMinMaxEnabled) { - let lowestVisibleXIndex = self.lowestVisibleXIndex, - highestVisibleXIndex = self.highestVisibleXIndex + let lowestVisibleX = self.lowestVisibleX, + highestVisibleX = self.highestVisibleX - if (_autoScaleLastLowestVisibleXIndex == nil || _autoScaleLastLowestVisibleXIndex != lowestVisibleXIndex || - _autoScaleLastHighestVisibleXIndex == nil || _autoScaleLastHighestVisibleXIndex != highestVisibleXIndex) + if (_autoScaleLastLowestVisibleX == nil || _autoScaleLastLowestVisibleX != lowestVisibleX || + _autoScaleLastHighestVisibleX == nil || _autoScaleLastHighestVisibleX != highestVisibleX) { calcMinMax() calculateOffsets() - _autoScaleLastLowestVisibleXIndex = lowestVisibleXIndex - _autoScaleLastHighestVisibleXIndex = highestVisibleXIndex + _autoScaleLastLowestVisibleX = lowestVisibleX + _autoScaleLastHighestVisibleX = highestVisibleX } } @@ -272,7 +265,6 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar renderer!.drawValues(context: context) _legendRenderer.renderLegend(context: context) - // drawLegend() drawMarkers(context: context) @@ -293,17 +285,19 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar public override func notifyDataSetChanged() { - calcMinMax() + renderer?.initBuffers() - _leftAxis?._defaultValueFormatter = _defaultValueFormatter - _rightAxis?._defaultValueFormatter = _defaultValueFormatter + calcMinMax() - _leftYAxisRenderer?.computeAxis(yMin: _leftAxis._axisMinimum, yMax: _leftAxis._axisMaximum) - _rightYAxisRenderer?.computeAxis(yMin: _rightAxis._axisMinimum, yMax: _rightAxis._axisMaximum) + _leftYAxisRenderer?.computeAxis(min: _leftAxis._axisMinimum, max: _leftAxis._axisMaximum, inverted: _leftAxis.isInverted) + _rightYAxisRenderer?.computeAxis(min: _rightAxis._axisMinimum, max: _rightAxis._axisMaximum, inverted: _rightAxis.isInverted) if let data = _data { - _xAxisRenderer?.computeAxis(xValAverageLength: data.xValAverageLength, xValues: data.xVals) + _xAxisRenderer?.computeAxis( + min: _xAxis._axisMinimum, + max: _xAxis._axisMaximum, + inverted: false) if (_legend !== nil) { @@ -318,14 +312,13 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar internal override func calcMinMax() { - if (_autoScaleMinMaxEnabled) + if _autoScaleMinMaxEnabled { - _data?.calcMinMax(start: lowestVisibleXIndex, end: highestVisibleXIndex) + _data?.calcMinMax() } // calculate / set x-axis range - _xAxis._axisMaximum = Double((_data?.xVals.count ?? 0) - 1) - _xAxis.axisRange = .abs(_xAxis._axisMaximum - _xAxis._axisMinimum); + _xAxis.calculate(min: _data?.xMin ?? 0.0, max: _data?.xMax ?? 0.0) // calculate axis range (min / max) according to provided data _leftAxis.calculate(min: _data?.getYMin(.Left) ?? 0.0, max: _data?.getYMax(.Left) ?? 0.0) @@ -457,85 +450,20 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar prepareOffsetMatrix() prepareValuePxMatrix() } - - - /// calculates the modulus for x-labels and grid - internal func calcModulus() - { - if (_xAxis === nil || !_xAxis.isEnabled) - { - return - } - - if (!_xAxis.isAxisModulusCustom) - { - _xAxis.axisLabelModulus = Int(ceil((CGFloat(_data?.xValCount ?? 0) * _xAxis.labelRotatedWidth) / (_viewPortHandler.contentWidth * _viewPortHandler.touchMatrix.a))) - } - - if (_xAxis.axisLabelModulus < 1) - { - _xAxis.axisLabelModulus = 1 - } - } public override func getMarkerPosition(entry e: ChartDataEntry, highlight: ChartHighlight) -> CGPoint { guard let data = _data else { return CGPointZero } - - let dataSetIndex = highlight.dataSetIndex - var xPos = CGFloat(e.xIndex) - var yPos = CGFloat(e.value) - if (self.isKindOfClass(BarChartView)) - { - let bd = _data as! BarChartData - let space = bd.groupSpace - let setCount = data.dataSetCount - let i = e.xIndex - - if self is HorizontalBarChartView - { - // calculate the x-position, depending on datasetcount - let y = CGFloat(i + i * (setCount - 1) + dataSetIndex) + space * CGFloat(i) + space / 2.0 - - yPos = y - - if let entry = e as? BarChartDataEntry - { - if entry.values != nil && highlight.range !== nil - { - xPos = CGFloat(highlight.range!.to) - } - else - { - xPos = CGFloat(e.value) - } - } - } - else - { - let x = CGFloat(i + i * (setCount - 1) + dataSetIndex) + space * CGFloat(i) + space / 2.0 - - xPos = x - - if let entry = e as? BarChartDataEntry - { - if entry.values != nil && highlight.range !== nil - { - yPos = CGFloat(highlight.range!.to) - } - else - { - yPos = CGFloat(e.value) - } - } - } - } + let dataSetIndex = highlight.dataSetIndex + let xPos = e.x + let yPos = e.y * _animator.phaseY // position of the marker depends on selected value index and value - var pt = CGPoint(x: xPos, y: yPos * _animator.phaseY) + var pt = CGPoint(x: CGFloat(xPos), y: CGFloat(yPos * _animator.phaseY)) - getTransformer(data.getDataSetByIndex(dataSetIndex)!.axisDependency).pointValueToPixel(&pt) + getTransformer(data.getDataSetByIndex(dataSetIndex)!.axisDependency) + .pointValueToPixel(&pt) return pt } @@ -610,8 +538,8 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar } else { - self.lastHighlighted = h self.highlightValue(highlight: h, callDelegate: true) + self.lastHighlighted = h } } } @@ -639,7 +567,7 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar location.y = -(self.bounds.size.height - location.y - _viewPortHandler.offsetBottom) } - self.zoom(isScaleXEnabled ? 1.4 : 1.0, scaleY: isScaleYEnabled ? 1.4 : 1.0, x: location.x, y: location.y) + self.zoom(scaleX: isScaleXEnabled ? 1.4 : 1.0, scaleY: isScaleYEnabled ? 1.4 : 1.0, x: location.x, y: location.y) } } } @@ -1056,7 +984,7 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// - parameter scaleY: if < 1 --> zoom out, if > 1 --> zoom in /// - parameter x: /// - parameter y: - public func zoom(scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) + public func zoom(scaleX scaleX: CGFloat, scaleY: CGFloat, x: CGFloat, y: CGFloat) { let matrix = _viewPortHandler.zoom(scaleX: scaleX, scaleY: scaleY, x: x, y: y) _viewPortHandler.refresh(newMatrix: matrix, chart: self, invalidate: false) @@ -1071,17 +999,25 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// /// - parameter scaleX: if < 1 --> zoom out, if > 1 --> zoom in /// - parameter scaleY: if < 1 --> zoom out, if > 1 --> zoom in - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: - public func zoom( - scaleX: CGFloat, + public func zoomAndCenter( + scaleX scaleX: CGFloat, scaleY: CGFloat, - xIndex: CGFloat, + xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency) { - let job = ZoomChartViewJob(viewPortHandler: viewPortHandler, scaleX: scaleX, scaleY: scaleY, xIndex: xIndex, yValue: yValue, transformer: getTransformer(axis), axis: axis, view: self) + let job = ZoomChartViewJob( + viewPortHandler: viewPortHandler, + scaleX: scaleX, + scaleY: scaleY, + xValue: xValue, + yValue: yValue, + transformer: getTransformer(axis), + axis: axis, + view: self) addViewportJob(job) } @@ -1089,7 +1025,7 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// /// - parameter scaleX: /// - parameter scaleY: - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds @@ -1097,13 +1033,13 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar public func zoomAndCenterViewAnimated( scaleX scaleX: CGFloat, scaleY: CGFloat, - xIndex: CGFloat, + xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval, easing: ChartEasingFunctionBlock?) { - let origin = getValueByTouchPoint( + let origin = valueForTouchPoint( pt: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), axis: axis) @@ -1112,12 +1048,12 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar transformer: getTransformer(axis), view: self, yAxis: getAxis(axis), - xValCount: _xAxis.values.count, + xAxisRange: _xAxis.axisRange, scaleX: scaleX, scaleY: scaleY, xOrigin: viewPortHandler.scaleX, yOrigin: viewPortHandler.scaleY, - zoomCenterX: xIndex, + zoomCenterX: CGFloat(xValue), zoomCenterY: CGFloat(yValue), zoomOriginX: origin.x, zoomOriginY: origin.y, @@ -1131,7 +1067,7 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// /// - parameter scaleX: /// - parameter scaleY: - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds @@ -1139,20 +1075,20 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar public func zoomAndCenterViewAnimated( scaleX scaleX: CGFloat, scaleY: CGFloat, - xIndex: CGFloat, + xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval, easingOption: ChartEasingOption) { - zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) + zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) } /// Zooms by the specified scale factor to the specified values on the specified axis. /// /// - parameter scaleX: /// - parameter scaleY: - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds @@ -1160,12 +1096,12 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar public func zoomAndCenterViewAnimated( scaleX scaleX: CGFloat, scaleY: CGFloat, - xIndex: CGFloat, + xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval) { - zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) + zoomAndCenterViewAnimated(scaleX: scaleX, scaleY: scaleY, xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) } /// Resets all zooming and dragging and makes the chart fit exactly it's bounds. @@ -1185,49 +1121,56 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar _viewPortHandler.setMinimumScaleY(scaleY) } - /// Sets the size of the area (range on the x-axis) that should be maximum visible at once (no further zomming out allowed). + public var visibleXRange: Double + { + return abs(highestVisibleX - lowestVisibleX) + } + + /// Sets the size of the area (range on the x-axis) that should be maximum visible at once (no further zooming out allowed). /// If this is e.g. set to 10, no more than 10 values on the x-axis can be viewed at once without scrolling. - public func setVisibleXRangeMaximum(maxXRange: CGFloat) + public func setVisibleXRangeMaximum(maxXRange: Double) { - let xScale = CGFloat(_xAxis.axisRange) / maxXRange - _viewPortHandler.setMinimumScaleX(xScale) + let xScale = _xAxis.axisRange / maxXRange + _viewPortHandler.setMinimumScaleX(CGFloat(xScale)) } /// Sets the size of the area (range on the x-axis) that should be minimum visible at once (no further zooming in allowed). /// If this is e.g. set to 10, no less than 10 values on the x-axis can be viewed at once without scrolling. - public func setVisibleXRangeMinimum(minXRange: CGFloat) + public func setVisibleXRangeMinimum(minXRange: Double) { - let xScale = CGFloat(_xAxis.axisRange) / minXRange - _viewPortHandler.setMaximumScaleX(xScale) + let xScale = _xAxis.axisRange / minXRange + _viewPortHandler.setMaximumScaleX(CGFloat(xScale)) } /// Limits the maximum and minimum value count that can be visible by pinching and zooming. /// e.g. minRange=10, maxRange=100 no less than 10 values and no more that 100 values can be viewed /// at once without scrolling - public func setVisibleXRange(minXRange minXRange: CGFloat, maxXRange: CGFloat) + public func setVisibleXRange(minXRange minXRange: Double, maxXRange: Double) { - let maxScale = CGFloat(_xAxis.axisRange) / minXRange - let minScale = CGFloat(_xAxis.axisRange) / maxXRange - _viewPortHandler.setMinMaxScaleX(minScaleX: minScale, maxScaleX: maxScale) + let maxScale = _xAxis.axisRange / minXRange + let minScale = _xAxis.axisRange / maxXRange + _viewPortHandler.setMinMaxScaleX( + minScaleX: CGFloat(minScale), + maxScaleX: CGFloat(maxScale)) } /// Sets the size of the area (range on the y-axis) that should be maximum visible at once. /// /// - parameter yRange: /// - parameter axis: - the axis for which this limit should apply - public func setVisibleYRangeMaximum(maxYRange: CGFloat, axis: ChartYAxis.AxisDependency) + public func setVisibleYRangeMaximum(maxYRange: Double, axis: ChartYAxis.AxisDependency) { let yScale = getDeltaY(axis) / maxYRange - _viewPortHandler.setMinimumScaleY(yScale) + _viewPortHandler.setMinimumScaleY(CGFloat(yScale)) } /// Moves the left side of the current viewport to the specified x-index. /// This also refreshes the chart by calling setNeedsDisplay(). - public func moveViewToX(xIndex: CGFloat) + public func moveViewToX(xValue: Double) { let job = MoveChartViewJob( viewPortHandler: viewPortHandler, - xIndex: xIndex, + xValue: xValue, yValue: 0.0, transformer: getTransformer(.Left), view: self) @@ -1242,12 +1185,12 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// - parameter axis: - which axis should be used as a reference for the y-axis public func moveViewToY(yValue: Double, axis: ChartYAxis.AxisDependency) { - let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY + let valsInView = getDeltaY(axis) / Double(_viewPortHandler.scaleY) let job = MoveChartViewJob( viewPortHandler: viewPortHandler, - xIndex: 0, - yValue: yValue + Double(valsInView) / 2.0, + xValue: 0.0, + yValue: yValue + valsInView / 2.0, transformer: getTransformer(axis), view: self) @@ -1257,17 +1200,17 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// This will move the left side of the current viewport to the specified x-index on the x-axis, and center the viewport to the specified y-value on the y-axis. /// This also refreshes the chart by calling setNeedsDisplay(). /// - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: - which axis should be used as a reference for the y-axis - public func moveViewTo(xIndex xIndex: CGFloat, yValue: Double, axis: ChartYAxis.AxisDependency) + public func moveViewTo(xValue xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency) { - let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY + let valsInView = getDeltaY(axis) / Double(_viewPortHandler.scaleY) let job = MoveChartViewJob( viewPortHandler: viewPortHandler, - xIndex: xIndex, - yValue: yValue + Double(valsInView) / 2.0, + xValue: xValue, + yValue: yValue + valsInView / 2.0, transformer: getTransformer(axis), view: self) @@ -1277,28 +1220,28 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. /// This also refreshes the chart by calling setNeedsDisplay(). /// - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: public func moveViewToAnimated( - xIndex xIndex: CGFloat, + xValue xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval, easing: ChartEasingFunctionBlock?) { - let bounds = getValueByTouchPoint( + let bounds = valueForTouchPoint( pt: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), axis: axis) - let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY + let valsInView = getDeltaY(axis) / Double(_viewPortHandler.scaleY) let job = AnimatedMoveChartViewJob( viewPortHandler: viewPortHandler, - xIndex: xIndex, - yValue: yValue + Double(valsInView) / 2.0, + xValue: xValue, + yValue: yValue + valsInView / 2.0, transformer: getTransformer(axis), view: self, xOrigin: bounds.x, @@ -1312,56 +1255,56 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. /// This also refreshes the chart by calling setNeedsDisplay(). /// - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: public func moveViewToAnimated( - xIndex xIndex: CGFloat, + xValue xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval, easingOption: ChartEasingOption) { - moveViewToAnimated(xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) + moveViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) } /// This will move the left side of the current viewport to the specified x-position and center the viewport to the specified y-position animated. /// This also refreshes the chart by calling setNeedsDisplay(). /// - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: public func moveViewToAnimated( - xIndex xIndex: CGFloat, + xValue xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval) { - moveViewToAnimated(xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) + moveViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) } /// This will move the center of the current viewport to the specified x-index and y-value. /// This also refreshes the chart by calling setNeedsDisplay(). /// - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: - which axis should be used as a reference for the y-axis public func centerViewTo( - xIndex xIndex: CGFloat, + xValue xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency) { - let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY - let xsInView = CGFloat(xAxis.values.count) / _viewPortHandler.scaleX + let valsInView = getDeltaY(axis) / Double(_viewPortHandler.scaleY) + let xsInView = xAxis.axisRange / Double(_viewPortHandler.scaleX) let job = MoveChartViewJob( viewPortHandler: viewPortHandler, - xIndex: xIndex - xsInView / 2.0, - yValue: yValue + Double(valsInView) / 2.0, + xValue: xValue - xsInView / 2.0, + yValue: yValue + valsInView / 2.0, transformer: getTransformer(axis), view: self) @@ -1370,29 +1313,29 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// This will move the center of the current viewport to the specified x-value and y-value animated. /// - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: public func centerViewToAnimated( - xIndex xIndex: CGFloat, + xValue xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval, easing: ChartEasingFunctionBlock?) { - let bounds = getValueByTouchPoint( + let bounds = valueForTouchPoint( pt: CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop), axis: axis) - let valsInView = getDeltaY(axis) / _viewPortHandler.scaleY - let xsInView = CGFloat(xAxis.values.count) / _viewPortHandler.scaleX + let valsInView = getDeltaY(axis) / Double(_viewPortHandler.scaleY) + let xsInView = xAxis.axisRange / Double(_viewPortHandler.scaleX) let job = AnimatedMoveChartViewJob( viewPortHandler: viewPortHandler, - xIndex: xIndex - xsInView / 2.0, - yValue: yValue + Double(valsInView) / 2.0, + xValue: xValue - xsInView / 2.0, + yValue: yValue + valsInView / 2.0, transformer: getTransformer(axis), view: self, xOrigin: bounds.x, @@ -1405,35 +1348,35 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// This will move the center of the current viewport to the specified x-value and y-value animated. /// - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: public func centerViewToAnimated( - xIndex xIndex: CGFloat, + xValue xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval, easingOption: ChartEasingOption) { - centerViewToAnimated(xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) + centerViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easing: easingFunctionFromOption(easingOption)) } /// This will move the center of the current viewport to the specified x-value and y-value animated. /// - /// - parameter xIndex: + /// - parameter xValue: /// - parameter yValue: /// - parameter axis: which axis should be used as a reference for the y-axis /// - parameter duration: the duration of the animation in seconds /// - parameter easing: public func centerViewToAnimated( - xIndex xIndex: CGFloat, + xValue xValue: Double, yValue: Double, axis: ChartYAxis.AxisDependency, duration: NSTimeInterval) { - centerViewToAnimated(xIndex: xIndex, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) + centerViewToAnimated(xValue: xValue, yValue: yValue, axis: axis, duration: duration, easingOption: .EaseInOutSine) } /// Sets custom offsets for the current `ChartViewPort` (the offsets on the sides of the actual chart window). Setting this will prevent the chart from automatically calculating it's offsets. Use `resetViewPortOffsets()` to undo this. @@ -1466,22 +1409,22 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar // MARK: - Accessors /// - returns: the delta-y value (y-value range) of the specified axis. - public func getDeltaY(axis: ChartYAxis.AxisDependency) -> CGFloat + public func getDeltaY(axis: ChartYAxis.AxisDependency) -> Double { if (axis == .Left) { - return CGFloat(leftAxis.axisRange) + return leftAxis.axisRange } else { - return CGFloat(rightAxis.axisRange) + return rightAxis.axisRange } } /// - returns: the position (in pixels) the provided Entry has inside the chart view public func getPosition(e: ChartDataEntry, axis: ChartYAxis.AxisDependency) -> CGPoint { - var vals = CGPoint(x: CGFloat(e.xIndex), y: CGFloat(e.value)) + var vals = CGPoint(x: CGFloat(e.x), y: CGFloat(e.y)) getTransformer(axis).pointValueToPixel(&vals) @@ -1634,31 +1577,23 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar /// (encapsulated in a `CGPoint`). This method transforms pixel coordinates to /// coordinates / values in the chart. This is the opposite method to /// `getPixelsForValues(...)`. - public func getValueByTouchPoint(pt pt: CGPoint, axis: ChartYAxis.AxisDependency) -> CGPoint + public func valueForTouchPoint(pt pt: CGPoint, axis: ChartYAxis.AxisDependency) -> CGPoint { - var pt = pt - - getTransformer(axis).pixelToValue(&pt) - - return pt + return getTransformer(axis).valueForTouchPoint(pt) } /// Transforms the given chart values into pixels. This is the opposite - /// method to `getValueByTouchPoint(...)`. + /// method to `valueForTouchPoint(...)`. public func getPixelForValue(x: Double, y: Double, axis: ChartYAxis.AxisDependency) -> CGPoint { - var pt = CGPoint(x: CGFloat(x), y: CGFloat(y)) - - getTransformer(axis).pointValueToPixel(&pt) - - return pt + return getTransformer(axis).pixelForValue(x: x, y: y) } /// - returns: the y-value at the given touch position (must not necessarily be /// a value contained in one of the datasets) public func getYValueByTouchPoint(pt pt: CGPoint, axis: ChartYAxis.AxisDependency) -> CGFloat { - return getValueByTouchPoint(pt: pt, axis: axis).y + return valueForTouchPoint(pt: pt, axis: axis).y } /// - returns: the Entry object displayed at the touched position of the chart @@ -1912,17 +1847,16 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar } } - /// the number of maximum visible drawn values on the chart - /// only active when `setDrawValues()` is enabled - public var maxVisibleValueCount: Int + /// the number of maximum visible drawn values on the chart only active when `drawValuesEnabled` is enabled + public override var maxVisibleCount: Int { get { - return _maxVisibleValueCount + return _maxVisibleCount } set { - _maxVisibleValueCount = newValue + _maxVisibleCount = newValue } } @@ -1932,15 +1866,19 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar } /// - returns: the lowest x-index (value on the x-axis) that is still visible on he chart. - public var lowestVisibleXIndex: Int + public var lowestVisibleX: Double { - var pt = CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom) + var pt = CGPoint( + x: viewPortHandler.contentLeft, + y: viewPortHandler.contentBottom) + getTransformer(.Left).pixelToValue(&pt) - return (pt.x <= 0.0) ? 0 : Int(ceil(pt.x)) + + return max(xAxis._axisMinimum, Double(pt.x)) } /// - returns: the highest x-index (value on the x-axis) that is still visible on the chart. - public var highestVisibleXIndex: Int + public var highestVisibleX: Double { var pt = CGPoint( x: viewPortHandler.contentRight, @@ -1948,10 +1886,6 @@ public class BarLineChartViewBase: ChartViewBase, BarLineScatterCandleBubbleChar getTransformer(.Left).pixelToValue(&pt) - guard let - data = _data - else { return Int(round(pt.x)) } - - return min(data.xValCount - 1, Int(floor(pt.x))) + return min(xAxis._axisMaximum, Double(pt.x)) } } \ No newline at end of file diff --git a/Charts/Classes/Charts/BubbleChartView.swift b/Charts/Classes/Charts/BubbleChartView.swift index 01dff622b1..868c43c35f 100644 --- a/Charts/Classes/Charts/BubbleChartView.swift +++ b/Charts/Classes/Charts/BubbleChartView.swift @@ -21,42 +21,6 @@ public class BubbleChartView: BarLineChartViewBase, BubbleChartDataProvider renderer = BubbleChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) } - public override func calcMinMax() - { - super.calcMinMax() - guard let data = _data else { return } - - if _xAxis.axisRange == 0.0 && data.yValCount > 0 - { - _xAxis.axisRange = 1.0 - } - - _xAxis._axisMinimum = -0.5 - _xAxis._axisMaximum = Double(data.xVals.count) - 0.5 - - if renderer as? BubbleChartRenderer !== nil, - let sets = data.dataSets as? [IBubbleChartDataSet] - { - for set in sets { - - let xmin = set.xMin - let xmax = set.xMax - - if (xmin < _xAxis._axisMinimum) - { - _xAxis._axisMinimum = xmin - } - - if (xmax > _xAxis._axisMaximum) - { - _xAxis._axisMaximum = xmax - } - } - } - - _xAxis.axisRange = abs(_xAxis._axisMaximum - _xAxis._axisMinimum) - } - // MARK: - BubbleChartDataProbider public var bubbleData: BubbleChartData? { return _data as? BubbleChartData } diff --git a/Charts/Classes/Charts/CandleStickChartView.swift b/Charts/Classes/Charts/CandleStickChartView.swift index 81588fcac4..b49150ec9c 100644 --- a/Charts/Classes/Charts/CandleStickChartView.swift +++ b/Charts/Classes/Charts/CandleStickChartView.swift @@ -24,14 +24,6 @@ public class CandleStickChartView: BarLineChartViewBase, CandleChartDataProvider renderer = CandleStickChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) _xAxis._axisMinimum = -0.5 } - - internal override func calcMinMax() - { - super.calcMinMax() - - _xAxis._axisMaximum += 0.5 - _xAxis.axisRange = abs(_xAxis._axisMaximum - _xAxis._axisMinimum) - } // MARK: - CandleChartDataProvider diff --git a/Charts/Classes/Charts/ChartViewBase.swift b/Charts/Classes/Charts/ChartViewBase.swift index 4a589b458e..759bd9210a 100755 --- a/Charts/Classes/Charts/ChartViewBase.swift +++ b/Charts/Classes/Charts/ChartViewBase.swift @@ -178,6 +178,8 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate { _animator = ChartAnimator() _animator.delegate = self + + maxHighlightDistance = 70.0 _viewPortHandler = ChartViewPortHandler() _viewPortHandler.setChartDimens(width: bounds.size.width, height: bounds.size.height) @@ -235,7 +237,7 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate { guard let data = _data else { return true } - if (data.yValCount <= 0) + if data.entryCount <= 0 { return true } @@ -270,7 +272,7 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate // check if a custom formatter is set or not var reference = Double(0.0) - if let data = _data where data.xValCount >= 2 + if let data = _data where data.entryCount >= 2 { reference = fabs(max - min) } @@ -450,14 +452,14 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate /// Highlights the value at the given x-index in the given DataSet. /// Provide -1 as the x-index to undo all highlighting. - public func highlightValue(xIndex xIndex: Int, dataSetIndex: Int) + public func highlightValue(x x: Double, dataSetIndex: Int) { - highlightValue(xIndex: xIndex, dataSetIndex: dataSetIndex, callDelegate: true) + highlightValue(x: x, dataSetIndex: dataSetIndex, callDelegate: true) } /// Highlights the value at the given x-index in the given DataSet. - /// Provide -1 as the x-index to undo all highlighting. - public func highlightValue(xIndex xIndex: Int, dataSetIndex: Int, callDelegate: Bool) + /// Provide -1 as the dataSetIndex to undo all highlighting. + public func highlightValue(x x: Double, dataSetIndex: Int, callDelegate: Bool) { guard let data = _data else { @@ -465,13 +467,13 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate return } - if (xIndex < 0 || dataSetIndex < 0 || xIndex >= data.xValCount || dataSetIndex >= data.dataSetCount) + if dataSetIndex < 0 || dataSetIndex >= data.dataSetCount { highlightValue(highlight: nil, callDelegate: callDelegate) } else { - highlightValue(highlight: ChartHighlight(xIndex: xIndex, dataSetIndex: dataSetIndex), callDelegate: callDelegate) + highlightValue(highlight: ChartHighlight(x: x, dataSetIndex: dataSetIndex), callDelegate: callDelegate) } } @@ -499,7 +501,7 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate if self is BarLineChartViewBase && (self as! BarLineChartViewBase).isHighlightFullBarEnabled { - h = ChartHighlight(xIndex: h!.xIndex, value: Double.NaN, dataIndex: -1, dataSetIndex: -1, stackIndex: -1) + h = ChartHighlight(x: h!.x, y: Double.NaN, dataIndex: -1, dataSetIndex: -1, stackIndex: -1) } _indicesToHighlight = [h!] @@ -540,38 +542,38 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate for i in 0 ..< _indicesToHighlight.count { let highlight = _indicesToHighlight[i] - let xIndex = highlight.xIndex - - let deltaX = _xAxis?.axisRange ?? (Double(_data?.xValCount ?? 0) - 1) - if xIndex <= Int(deltaX) && xIndex <= Int(CGFloat(deltaX) * _animator.phaseX) + + guard let + set = data?.getDataSetByIndex(highlight.dataSetIndex), + e = _data?.getEntryForHighlight(highlight) + else { continue } + + let entryIndex = set.entryIndex(entry: e) + if entryIndex > Int(Double(set.entryCount) * _animator.phaseX) { - let e = _data?.getEntryForHighlight(highlight) - if (e === nil || e!.xIndex != highlight.xIndex) - { - continue - } - - let pos = getMarkerPosition(entry: e!, highlight: highlight) + continue + } - // check bounds - if (!_viewPortHandler.isInBounds(x: pos.x, y: pos.y)) - { - continue - } + let pos = getMarkerPosition(entry: e, highlight: highlight) - // callbacks to update the content - marker!.refreshContent(entry: e!, highlight: highlight) + // check bounds + if !_viewPortHandler.isInBounds(x: pos.x, y: pos.y) + { + continue + } - let markerSize = marker!.size - if (pos.y - markerSize.height <= 0.0) - { - let y = markerSize.height - pos.y - marker!.draw(context: context, point: CGPoint(x: pos.x, y: pos.y + y)) - } - else - { - marker!.draw(context: context, point: pos) - } + // callbacks to update the content + marker!.refreshContent(entry: e, highlight: highlight) + + let markerSize = marker!.size + if pos.y - markerSize.height <= 0.0 + { + let y = markerSize.height - pos.y + marker!.draw(context: context, point: CGPoint(x: pos.x, y: pos.y + y)) + } + else + { + marker!.draw(context: context, point: pos) } } } @@ -717,18 +719,13 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate return _xAxis._axisMinimum } - public var xValCount: Int + public var xRange: Double { - return _data?.xValCount ?? 0 + return _xAxis.axisRange } - /// - returns: the total number of (y) values the chart holds (across all DataSets) - public var valueCount: Int - { - return _data?.yValCount ?? 0 - } - - /// *Note: (Equivalent of getCenter() in MPAndroidChart, as center is already a standard in iOS that returns the center point relative to superview, and MPAndroidChart returns relative to self)* + /// * + /// - note: (Equivalent of getCenter() in MPAndroidChart, as center is already a standard in iOS that returns the center point relative to superview, and MPAndroidChart returns relative to self)* /// - returns: the center point of the chart (the whole View) in pixels. public var midPoint: CGPoint { @@ -765,19 +762,8 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate return _viewPortHandler.contentRect } - /// - returns: the x-value at the given index - public func getXValue(index: Int) -> String! - { - guard let data = _data where data.xValCount > index else - { - return nil - } - - return data.xVals[index] - } - /// Get all Entry objects at the given index across all DataSets. - public func getEntriesAtIndex(xIndex: Int) -> [ChartDataEntry] + public func getEntriesAtIndex(xValue: Double) -> [ChartDataEntry] { var vals = [ChartDataEntry]() @@ -786,7 +772,7 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate for i in 0 ..< data.dataSetCount { let set = data.getDataSetByIndex(i) - let e = set.entryForXIndex(xIndex) + let e = set.entryForXPos(xValue) if (e !== nil) { vals.append(e!) @@ -966,6 +952,16 @@ public class ChartViewBase: NSUIView, ChartDataProvider, ChartAnimatorDelegate } } + /// The maximum distance in screen pixels away from an entry causing it to highlight. + /// **default**: 70.0 + public var maxHighlightDistance: CGFloat = 70.0 + + /// the number of maximum visible drawn values on the chart only active when `drawValuesEnabled` is enabled + public var maxVisibleCount: Int + { + return Int(INT_MAX) + } + // MARK: - ChartAnimatorDelegate public func chartAnimatorUpdated(chartAnimator: ChartAnimator) diff --git a/Charts/Classes/Charts/CombinedChartView.swift b/Charts/Classes/Charts/CombinedChartView.swift index 63f8ea5354..801184a5fe 100644 --- a/Charts/Classes/Charts/CombinedChartView.swift +++ b/Charts/Classes/Charts/CombinedChartView.swift @@ -47,44 +47,6 @@ public class CombinedChartView: BarLineChartViewBase, LineChartDataProvider, Bar renderer = CombinedChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler) } - override func calcMinMax() - { - super.calcMinMax() - guard let data = _data else { return } - - if (self.barData !== nil || self.candleData !== nil || self.bubbleData !== nil) - { - _xAxis._axisMinimum = -0.5 - _xAxis._axisMaximum = Double(data.xVals.count) - 0.5 - - if (self.bubbleData !== nil) - { - for set in self.bubbleData?.dataSets as! [IBubbleChartDataSet] - { - let xmin = set.xMin - let xmax = set.xMax - - if (xmin < chartXMin) - { - _xAxis._axisMinimum = xmin - } - - if (xmax > chartXMax) - { - _xAxis._axisMaximum = xmax - } - } - } - } - - _xAxis.axisRange = abs(_xAxis._axisMaximum - _xAxis._axisMinimum) - - if _xAxis.axisRange == 0.0 && self.lineData?.yValCount > 0 - { - _xAxis.axisRange = 1.0 - } - } - public override var data: ChartData? { get @@ -95,6 +57,7 @@ public class CombinedChartView: BarLineChartViewBase, LineChartDataProvider, Bar { super.data = newValue (renderer as! CombinedChartRenderer?)!.createRenderers() + renderer?.initBuffers() } } @@ -186,13 +149,6 @@ public class CombinedChartView: BarLineChartViewBase, LineChartDataProvider, Bar // MARK: - Accessors - /// flag that enables or disables the highlighting arrow - public var drawHighlightArrowEnabled: Bool - { - get { return (renderer as! CombinedChartRenderer!).drawHighlightArrowEnabled } - set { (renderer as! CombinedChartRenderer!).drawHighlightArrowEnabled = newValue } - } - /// if set to true, all values are drawn above their bars, instead of below their top public var drawValueAboveBarEnabled: Bool { @@ -207,9 +163,6 @@ public class CombinedChartView: BarLineChartViewBase, LineChartDataProvider, Bar set { (renderer as! CombinedChartRenderer!).drawBarShadowEnabled = newValue } } - /// - returns: true if drawing the highlighting arrow is enabled, false if not - public var isDrawHighlightArrowEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawHighlightArrowEnabled; } - /// - returns: true if drawing values above bars is enabled, false if not public var isDrawValueAboveBarEnabled: Bool { return (renderer as! CombinedChartRenderer!).drawValueAboveBarEnabled; } diff --git a/Charts/Classes/Charts/HorizontalBarChartView.swift b/Charts/Classes/Charts/HorizontalBarChartView.swift index 56c868b8b2..0d66ccaba3 100644 --- a/Charts/Classes/Charts/HorizontalBarChartView.swift +++ b/Charts/Classes/Charts/HorizontalBarChartView.swift @@ -99,37 +99,21 @@ public class HorizontalBarChartView: BarChartView _rightAxisTransformer.prepareMatrixValuePx(chartXMin: _rightAxis._axisMinimum, deltaX: CGFloat(_rightAxis.axisRange), deltaY: CGFloat(_xAxis.axisRange), chartYMin: _xAxis._axisMinimum) _leftAxisTransformer.prepareMatrixValuePx(chartXMin: _leftAxis._axisMinimum, deltaX: CGFloat(_leftAxis.axisRange), deltaY: CGFloat(_xAxis.axisRange), chartYMin: _xAxis._axisMinimum) } - - internal override func calcModulus() - { - if let data = _data - { - _xAxis.axisLabelModulus = Int(ceil((CGFloat(data.xValCount) * _xAxis.labelRotatedHeight) / (_viewPortHandler.contentHeight * viewPortHandler.touchMatrix.d))) - } - else - { - _xAxis.axisLabelModulus = 1 - } - - if (_xAxis.axisLabelModulus < 1) - { - _xAxis.axisLabelModulus = 1 - } - } public override func getBarBounds(e: BarChartDataEntry) -> CGRect { guard let - set = _data?.getDataSetForEntry(e) as? IBarChartDataSet + data = _data as? BarChartData, + set = data.getDataSetForEntry(e) as? IBarChartDataSet else { return CGRectNull } - let barspace = set.barSpace - let y = CGFloat(e.value) - let x = CGFloat(e.xIndex) + let y = e.y + let x = e.x + + let barWidth = data.barWidth - let spaceHalf = barspace / 2.0 - let top = x - 0.5 + spaceHalf - let bottom = x + 0.5 - spaceHalf + let top = x - 0.5 + barWidth / 2.0 + let bottom = x + 0.5 - barWidth / 2.0 let left = y >= 0.0 ? y : 0.0 let right = y <= 0.0 ? y : 0.0 @@ -142,7 +126,7 @@ public class HorizontalBarChartView: BarChartView public override func getPosition(e: ChartDataEntry, axis: ChartYAxis.AxisDependency) -> CGPoint { - var vals = CGPoint(x: CGFloat(e.value), y: CGFloat(e.xIndex)) + var vals = CGPoint(x: CGFloat(e.y), y: CGFloat(e.x)) getTransformer(axis).pointValueToPixel(&vals) @@ -160,25 +144,28 @@ public class HorizontalBarChartView: BarChartView return self.highlighter?.getHighlight(x: pt.y, y: pt.x) } - public override var lowestVisibleXIndex: Int + /// - returns: the lowest x-index (value on the x-axis) that is still visible on he chart. + public override var lowestVisibleX: Double { - let step = CGFloat(_data?.dataSetCount ?? 0) - let div = (step <= 1.0) ? 1.0 : step + (_data as! BarChartData).groupSpace + var pt = CGPoint( + x: viewPortHandler.contentLeft, + y: viewPortHandler.contentBottom) - var pt = CGPoint(x: _viewPortHandler.contentLeft, y: _viewPortHandler.contentBottom) - getTransformer(ChartYAxis.AxisDependency.Left).pixelToValue(&pt) + getTransformer(.Left).pixelToValue(&pt) - return Int(((pt.y <= 0.0) ? 0.0 : pt.y / div) + 1.0) + return max(xAxis._axisMinimum, Double(pt.y)) + // FIXME: Update in Android } - public override var highestVisibleXIndex: Int + /// - returns: the highest x-index (value on the x-axis) that is still visible on the chart. + public override var highestVisibleX: Double { - let step = CGFloat(_data?.dataSetCount ?? 0) - let div = (step <= 1.0) ? 1.0 : step + (_data as! BarChartData).groupSpace + var pt = CGPoint( + x: viewPortHandler.contentLeft, + y: viewPortHandler.contentTop) - var pt = CGPoint(x: _viewPortHandler.contentLeft, y: _viewPortHandler.contentTop) - getTransformer(ChartYAxis.AxisDependency.Left).pixelToValue(&pt) + getTransformer(.Left).pixelToValue(&pt) - return Int((pt.y >= CGFloat(chartXMax)) ? CGFloat(chartXMax) / div : (pt.y / div)) + return min(xAxis._axisMaximum, Double(pt.y)) } } diff --git a/Charts/Classes/Charts/LineChartView.swift b/Charts/Classes/Charts/LineChartView.swift index 93094a98be..8ca4b18269 100644 --- a/Charts/Classes/Charts/LineChartView.swift +++ b/Charts/Classes/Charts/LineChartView.swift @@ -24,17 +24,6 @@ public class LineChartView: BarLineChartViewBase, LineChartDataProvider renderer = LineChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) } - internal override func calcMinMax() - { - super.calcMinMax() - guard let data = _data else { return } - - if _xAxis.axisRange == 0.0 && data.yValCount > 0 - { - _xAxis.axisRange = 1.0 - } - } - // MARK: - LineChartDataProvider public var lineData: LineChartData? { return _data as? LineChartData } diff --git a/Charts/Classes/Charts/PieChartView.swift b/Charts/Classes/Charts/PieChartView.swift index d1d3feabb0..ac3ff451d8 100755 --- a/Charts/Classes/Charts/PieChartView.swift +++ b/Charts/Classes/Charts/PieChartView.swift @@ -156,14 +156,15 @@ public class PieChartView: PieRadarChartViewBase let rotationAngle = self.rotationAngle - let i = e.xIndex + guard let entryIndex = data?.dataSets[0].entryIndex(x: highlight.x, rounding: .Closest) + else { return CGPoint(x: 0.0, y: 0.0) } // offset needed to center the drawn text in the slice - let offset = drawAngles[i] / 2.0 + let offset = drawAngles[entryIndex] / 2.0 // calculate the text position - let x: CGFloat = (r * cos(((rotationAngle + absoluteAngles[i] - offset) * _animator.phaseY) * ChartUtils.Math.FDEG2RAD) + center.x) - let y: CGFloat = (r * sin(((rotationAngle + absoluteAngles[i] - offset) * _animator.phaseY) * ChartUtils.Math.FDEG2RAD) + center.y) + let x: CGFloat = (r * cos(((rotationAngle + absoluteAngles[entryIndex] - offset) * CGFloat(_animator.phaseY)) * ChartUtils.Math.FDEG2RAD) + center.x) + let y: CGFloat = (r * sin(((rotationAngle + absoluteAngles[entryIndex] - offset) * CGFloat(_animator.phaseY)) * ChartUtils.Math.FDEG2RAD) + center.y) return CGPoint(x: x, y: y) } @@ -176,8 +177,10 @@ public class PieChartView: PieRadarChartViewBase guard let data = _data else { return } - _drawAngles.reserveCapacity(data.yValCount) - _absoluteAngles.reserveCapacity(data.yValCount) + let entryCount = data.entryCount + + _drawAngles.reserveCapacity(entryCount) + _absoluteAngles.reserveCapacity(entryCount) let yValueSum = (_data as! PieChartData).yValueSum @@ -194,7 +197,7 @@ public class PieChartView: PieRadarChartViewBase { guard let e = set.entryForIndex(j) else { continue } - _drawAngles.append(calcAngle(abs(e.value), yValueSum: yValueSum)) + _drawAngles.append(calcAngle(abs(e.y), yValueSum: yValueSum)) if (cnt == 0) { @@ -211,7 +214,7 @@ public class PieChartView: PieRadarChartViewBase } /// checks if the given index in the given DataSet is set for highlighting or not - public func needsHighlight(xIndex xIndex: Int, dataSetIndex: Int) -> Bool + public func needsHighlight(xValue xValue: Double, dataSetIndex: Int) -> Bool { // no highlight if (!valuesToHighlight() || dataSetIndex < 0) @@ -222,7 +225,7 @@ public class PieChartView: PieRadarChartViewBase for i in 0 ..< _indicesToHighlight.count { // check if the xvalue for the given dataset needs highlight - if (_indicesToHighlight[i].xIndex == xIndex + if (_indicesToHighlight[i].x == xValue && _indicesToHighlight[i].dataSetIndex == dataSetIndex) { return true @@ -266,13 +269,13 @@ public class PieChartView: PieRadarChartViewBase } /// - returns: the index of the DataSet this x-index belongs to. - public func dataSetIndexForIndex(xIndex: Int) -> Int + public func dataSetIndexForIndex(xValue: Double) -> Int { var dataSets = _data?.dataSets ?? [] for i in 0 ..< dataSets.count { - if (dataSets[i].entryForXIndex(xIndex) !== nil) + if (dataSets[i].entryForXPos(xValue) !== nil) { return i } @@ -298,7 +301,7 @@ public class PieChartView: PieRadarChartViewBase /// The color for the hole that is drawn in the center of the PieChart (if enabled). /// - /// *Note: Use holeTransparent with holeColor = nil to make the hole transparent.* + /// - note: Use holeTransparent with holeColor = nil to make the hole transparent.* public var holeColor: NSUIColor? { get diff --git a/Charts/Classes/Charts/PieRadarChartViewBase.swift b/Charts/Classes/Charts/PieRadarChartViewBase.swift index f0c13e3a92..9d4f9c523d 100755 --- a/Charts/Classes/Charts/PieRadarChartViewBase.swift +++ b/Charts/Classes/Charts/PieRadarChartViewBase.swift @@ -73,7 +73,15 @@ public class PieRadarChartViewBase: ChartViewBase internal override func calcMinMax() { - _xAxis.axisRange = Double((_data?.xVals.count ?? 0) - 1) + /*_xAxis.axisRange = Double((_data?.xVals.count ?? 0) - 1)*/ + } + + public override var maxVisibleCount: Int + { + get + { + return data?.entryCount ?? 0 + } } public override func notifyDataSetChanged() @@ -385,7 +393,7 @@ public class PieRadarChartViewBase: ChartViewBase /// The SelectionDetail objects give information about the value at the selected index and the DataSet it belongs to. /// - returns: an array of SelectionDetail objects for the given x-index. - public func getSelectionDetailsAtIndex(xIndex: Int) -> [ChartSelectionDetail] + public func getSelectionDetailsAtIndex(xValue: Double) -> [ChartSelectionDetail] { var vals = [ChartSelectionDetail]() @@ -401,13 +409,13 @@ public class PieRadarChartViewBase: ChartViewBase } // extract all y-values from all DataSets at the given x-index - let yVal = dataSet.yValForXIndex(xIndex) + let yVal = dataSet.yValueForXValue(xValue) if (yVal.isNaN) { continue } - vals.append(ChartSelectionDetail(value: yVal, dataSetIndex: i, dataSet: dataSet)) + vals.append(ChartSelectionDetail(xValue: 0.0, yValue: yVal, dataSetIndex: i, dataSet: dataSet)) } return vals @@ -463,7 +471,7 @@ public class PieRadarChartViewBase: ChartViewBase _spinAnimator = ChartAnimator() _spinAnimator.updateBlock = { - self.rotationAngle = (toAngle - fromAngle) * self._spinAnimator.phaseX + fromAngle + self.rotationAngle = (toAngle - fromAngle) * CGFloat(self._spinAnimator.phaseX) + fromAngle } _spinAnimator.stopBlock = { self._spinAnimator = nil; } @@ -877,7 +885,7 @@ public class PieRadarChartViewBase: ChartViewBase if (self.isKindOfClass(PieChartView)) { - angle /= _animator.phaseY + angle /= CGFloat(_animator.phaseY) } let index = indexForAngle(angle) @@ -890,14 +898,17 @@ public class PieRadarChartViewBase: ChartViewBase } else { - let valsAtIndex = getSelectionDetailsAtIndex(index) + let valsAtIndex = getSelectionDetailsAtIndex(Double(index)) var dataSetIndex = 0 // get the dataset that is closest to the selection (PieChart only has one DataSet) if (self.isKindOfClass(RadarChartView)) { - dataSetIndex = ChartUtils.closestDataSetIndexByValue(valsAtIndex: valsAtIndex, value: Double(distance / (self as! RadarChartView).factor), axis: nil) ?? -1 + dataSetIndex = ChartUtils.closestDataSetIndexByValue( + valsAtIndex: valsAtIndex, + value: Double(distance / (self as! RadarChartView).factor), + axis: nil) ?? -1 } if (dataSetIndex < 0) @@ -907,7 +918,7 @@ public class PieRadarChartViewBase: ChartViewBase } else { - let h = ChartHighlight(xIndex: index, dataSetIndex: dataSetIndex) + let h = ChartHighlight(x: Double(index), dataSetIndex: dataSetIndex) if (_lastHighlight !== nil && h == _lastHighlight) { diff --git a/Charts/Classes/Charts/RadarChartView.swift b/Charts/Classes/Charts/RadarChartView.swift index 4bb61876d3..3930232e4a 100644 --- a/Charts/Classes/Charts/RadarChartView.swift +++ b/Charts/Classes/Charts/RadarChartView.swift @@ -61,7 +61,6 @@ public class RadarChartView: PieRadarChartViewBase super.initialize() _yAxis = ChartYAxis(position: .Left) - _xAxis.spaceBetweenLabels = 0 renderer = RadarChartRenderer(chart: self, animator: _animator, viewPortHandler: _viewPortHandler) @@ -72,24 +71,22 @@ public class RadarChartView: PieRadarChartViewBase internal override func calcMinMax() { super.calcMinMax() - guard let data = _data else { return } - // calculate / set x-axis range - _xAxis._axisMaximum = Double(data.xVals.count) - 1.0 - _xAxis.axisRange = Double(abs(_xAxis._axisMaximum - _xAxis._axisMinimum)) + guard let data = _data else { return } _yAxis.calculate(min: data.getYMin(.Left), max: data.getYMax(.Left)) + _xAxis.calculate(min: 0.0, max: Double(data.maxEntryCountSet?.entryCount ?? 0)) } public override func getMarkerPosition(entry entry: ChartDataEntry, highlight: ChartHighlight) -> CGPoint { - let angle = self.sliceAngle * CGFloat(entry.xIndex) + self.rotationAngle - let val = CGFloat(entry.value) * self.factor + let angle = Double(self.sliceAngle) * entry.x * _animator.phaseX + Double(self.rotationAngle) + let val = entry.y * Double(self.factor) * _animator.phaseX let c = self.centerOffsets - let p = CGPoint(x: c.x + val * cos(angle * ChartUtils.Math.FDEG2RAD), - y: c.y + val * sin(angle * ChartUtils.Math.FDEG2RAD)) - + let p = CGPoint(x: c.x + CGFloat(val * cos(angle * ChartUtils.Math.DEG2RAD)), + y: c.y + CGFloat(val * sin(angle * ChartUtils.Math.DEG2RAD))) + return p } @@ -97,10 +94,8 @@ public class RadarChartView: PieRadarChartViewBase { calcMinMax() - _yAxis?._defaultValueFormatter = _defaultValueFormatter - - _yAxisRenderer?.computeAxis(yMin: _yAxis._axisMinimum, yMax: _yAxis._axisMaximum) - _xAxisRenderer?.computeAxis(xValAverageLength: data?.xValAverageLength ?? 0, xValues: data?.xVals ?? []) + _yAxisRenderer?.computeAxis(min: _yAxis._axisMinimum, max: _yAxis._axisMaximum, inverted: _yAxis.isInverted) + _xAxisRenderer?.computeAxis(min: _xAxis._axisMinimum, max: _xAxis._axisMaximum, inverted: false) if let data = _data, legend = _legend where !legend.isLegendCustom { @@ -124,9 +119,14 @@ public class RadarChartView: PieRadarChartViewBase let optionalContext = NSUIGraphicsGetCurrentContext() guard let context = optionalContext else { return } + if _xAxis.isEnabled + { + _xAxisRenderer.computeAxis(min: _xAxis._axisMinimum, max: _xAxis._axisMaximum, inverted: false) + } + _xAxisRenderer?.renderAxisLabels(context: context) - - if (drawWeb) + + if drawWeb { renderer!.drawExtras(context: context) } @@ -135,7 +135,7 @@ public class RadarChartView: PieRadarChartViewBase renderer!.drawData(context: context) - if (valuesToHighlight()) + if valuesToHighlight() { renderer!.drawHighlighted(context: context, indices: _indicesToHighlight) } @@ -162,7 +162,7 @@ public class RadarChartView: PieRadarChartViewBase /// - returns: the angle that each slice in the radar chart occupies. public var sliceAngle: CGFloat { - return 360.0 / CGFloat(_data?.xValCount ?? 0) + return 360.0 / CGFloat(_data?.maxEntryCountSet?.entryCount ?? 0) } public override func indexForAngle(angle: CGFloat) -> Int @@ -172,7 +172,7 @@ public class RadarChartView: PieRadarChartViewBase let sliceAngle = self.sliceAngle - for i in 0 ..< (_data?.xValCount ?? 0) + for i in 0 ..< (_data?.entryCount ?? 0) { if (sliceAngle * CGFloat(i + 1) - sliceAngle / 2.0 > a) { @@ -220,11 +220,11 @@ public class RadarChartView: PieRadarChartViewBase } /// - returns: the maximum value this chart can display on it's y-axis. - public override var chartYMax: Double { return _yAxis._axisMaximum; } + public override var chartYMax: Double { return _yAxis._axisMaximum } /// - returns: the minimum value this chart can display on it's y-axis. - public override var chartYMin: Double { return _yAxis._axisMinimum; } + public override var chartYMin: Double { return _yAxis._axisMinimum } /// - returns: the range of y-values this chart can display. - public var yRange: Double { return _yAxis.axisRange} + public var yRange: Double { return _yAxis.axisRange } } \ No newline at end of file diff --git a/Charts/Classes/Charts/ScatterChartView.swift b/Charts/Classes/Charts/ScatterChartView.swift index 1ae7527e9c..15d03b33a1 100644 --- a/Charts/Classes/Charts/ScatterChartView.swift +++ b/Charts/Classes/Charts/ScatterChartView.swift @@ -22,21 +22,6 @@ public class ScatterChartView: BarLineChartViewBase, ScatterChartDataProvider super.initialize() renderer = ScatterChartRenderer(dataProvider: self, animator: _animator, viewPortHandler: _viewPortHandler) - _xAxis._axisMinimum = -0.5 - } - - public override func calcMinMax() - { - super.calcMinMax() - guard let data = _data else { return } - - if _xAxis.axisRange == 0.0 && data.yValCount > 0 - { - _xAxis.axisRange = 1.0 - } - - _xAxis._axisMaximum += 0.5 - _xAxis.axisRange = abs(_xAxis._axisMaximum - _xAxis._axisMinimum) } // MARK: - ScatterChartDataProbider diff --git a/Charts/Classes/Components/ChartAxisBase.swift b/Charts/Classes/Components/ChartAxisBase.swift index 4069d8c929..310f158564 100644 --- a/Charts/Classes/Components/ChartAxisBase.swift +++ b/Charts/Classes/Components/ChartAxisBase.swift @@ -18,6 +18,14 @@ import CoreGraphics /// Base class for all axes public class ChartAxisBase: ChartComponentBase { + public override init() + { + super.init() + } + + /// Custom formatter that is used instead of the auto-formatter if set + private var _axisValueFormatter: ChartAxisValueFormatter? + public var labelFont = NSUIFont.systemFontOfSize(10.0) public var labelTextColor = NSUIColor.blackColor() @@ -38,6 +46,18 @@ public class ChartAxisBase: ChartComponentBase /// flag that indicates of the labels of this axis should be drawn or not public var drawLabelsEnabled = true + private var _centerAxisLabelsEnabled = false + public var centerAxisLabelsEnabled: Bool + { + get { return _centerAxisLabelsEnabled && entryCount > 1 } + set { _centerAxisLabelsEnabled = newValue } + } + + public var isCenterAxisLabelsEnabled: Bool + { + get { return centerAxisLabelsEnabled } + } + /// array of limitlines that can be set for the axis private var _limitLines = [ChartLimitLine]() @@ -48,15 +68,110 @@ public class ChartAxisBase: ChartComponentBase /// the flag can be used to turn off the antialias for grid lines public var gridAntialiasEnabled = true - - public override init() + + /// the actual array of entries + public var entries = [Double]() + + /// axis label entries only used for centered labels + public var centeredEntries = [Double]() + + /// the number of entries the legend contains + public var entryCount: Int { return entries.count; } + + /// the number of y-label entries the y-labels should have + /// + /// **default**: 6 + private var _labelCount = Int(6) + + /// the number of decimal digits to use (for the default formatter + public var decimals: Int = 0 + + /// When true, axis labels are controlled by the `granularity` property. + /// When false, axis values could possibly be repeated. + /// This could happen if two adjacent axis values are rounded to same value. + /// If using granularity this could be avoided by having fewer axis values visible. + public var granularityEnabled = false + + private var _granularity = Double(1.0) + + /// The minimum interval between axis values. + /// This can be used to avoid label duplicating when zooming in. + /// + /// **default**: 1.0 + public var granularity: Double { - super.init() + get + { + return _granularity + } + set + { + _granularity = newValue + + // set this to true if it was disabled, as it makes no sense to set this property with granularity disabled + granularityEnabled = true + } } + /// The minimum interval between axis values. + public var isGranularityEnabled: Bool + { + get + { + return granularityEnabled + } + } + + /// if true, the set number of y-labels will be forced + public var forceLabelsEnabled = false + public func getLongestLabel() -> String { - fatalError("getLongestLabel() cannot be called on ChartAxisBase") + var longest = "" + + for i in 0 ..< entries.count + { + let text = getFormattedLabel(i) + + if longest.characters.count < text.characters.count + { + longest = text + } + } + + return longest + } + + /// - returns: the formatted label at the specified index. This will either use the auto-formatter or the custom formatter (if one is set). + public func getFormattedLabel(index: Int) -> String + { + if (index < 0 || index >= entries.count) + { + return "" + } + + return valueFormatter?.stringForValue(entries[index], axis: self) ?? "" + } + + /// Sets the formatter to be used for formatting the axis labels. + /// If no formatter is set, the chart will automatically determine a reasonable formatting (concerning decimals) for all the values that are drawn inside the chart. + /// Use `nil` to use the formatter calculated by the chart. + public var valueFormatter: ChartAxisValueFormatter? + { + get + { + if _axisValueFormatter == nil || + (_axisValueFormatter is ChartDefaultAxisValueFormatter && (_axisValueFormatter as? ChartDefaultAxisValueFormatter)?.decimals != decimals) + { + _axisValueFormatter = ChartDefaultAxisValueFormatter(decimals: decimals) + } + + return _axisValueFormatter + } + set + { + _axisValueFormatter = newValue ?? ChartDefaultAxisValueFormatter(decimals: decimals) + } } public var isDrawGridLinesEnabled: Bool { return drawGridLinesEnabled; } @@ -89,6 +204,43 @@ public class ChartAxisBase: ChartComponentBase /// the total range of values this axis covers public var axisRange = Double(0) + /// the number of label entries the y-axis should have + /// max = 25, + /// min = 2, + /// default = 6, + /// be aware that this number is not fixed and can only be approximated + public var labelCount: Int + { + get + { + return _labelCount + } + set + { + _labelCount = newValue + + if (_labelCount > 25) + { + _labelCount = 25 + } + if (_labelCount < 2) + { + _labelCount = 2 + } + + forceLabelsEnabled = false + } + } + + public func setLabelCount(count: Int, force: Bool) + { + self.labelCount = count + forceLabelsEnabled = force + } + + /// - returns: true if focing the y-label count is enabled. Default: false + public var isForceLabelsEnabled: Bool { return forceLabelsEnabled } + /// Adds a new ChartLimitLine to this axis. public func addLimitLine(line: ChartLimitLine) { @@ -151,6 +303,7 @@ public class ChartAxisBase: ChartComponentBase { _customAxisMin = true _axisMinimum = newValue + axisRange = abs(_axisMaximum - newValue) } } @@ -167,6 +320,33 @@ public class ChartAxisBase: ChartComponentBase { _customAxisMax = true _axisMaximum = newValue + axisRange = abs(_axisMaximum - newValue) + } + } + + /// Calculates the minimum, maximum and range values of the YAxis with the given minimum and maximum values from the chart data. + /// - parameter dataMin: the y-min value according to chart data + /// - parameter dataMax: the y-max value according to chart + public func calculate(min dataMin: Double, max dataMax: Double) + { + // if custom, use value as is, else use data value + var min = _customAxisMin ? _axisMinimum : dataMin + var max = _customAxisMax ? _axisMaximum : dataMax + + // temporary range (before calculations) + let range = abs(max - min) + + // in case all values are equal + if range == 0.0 + { + max = max + 1.0 + min = min - 1.0 } + + _axisMinimum = min + _axisMaximum = max + + // actual range + axisRange = abs(max - min) } } \ No newline at end of file diff --git a/Charts/Classes/Components/ChartXAxis.swift b/Charts/Classes/Components/ChartXAxis.swift index 37882ab337..d8a625cbbf 100644 --- a/Charts/Classes/Components/ChartXAxis.swift +++ b/Charts/Classes/Components/ChartXAxis.swift @@ -27,68 +27,31 @@ public class ChartXAxis: ChartAxisBase case BottomInside } - public var values = [String?]() - - /// width of the x-axis labels in pixels - this is automatically calculated by the computeAxis() methods in the renderers + /// width of the x-axis labels in pixels - this is automatically calculated by the `computeSize()` methods in the renderers public var labelWidth = CGFloat(1.0) - /// height of the x-axis labels in pixels - this is automatically calculated by the computeAxis() methods in the renderers + /// height of the x-axis labels in pixels - this is automatically calculated by the `computeSize()` methods in the renderers public var labelHeight = CGFloat(1.0) - /// width of the (rotated) x-axis labels in pixels - this is automatically calculated by the computeAxis() methods in the renderers + /// width of the (rotated) x-axis labels in pixels - this is automatically calculated by the `computeSize()` methods in the renderers public var labelRotatedWidth = CGFloat(1.0) - /// height of the (rotated) x-axis labels in pixels - this is automatically calculated by the computeAxis() methods in the renderers + /// height of the (rotated) x-axis labels in pixels - this is automatically calculated by the `computeSize()` methods in the renderers public var labelRotatedHeight = CGFloat(1.0) /// This is the angle for drawing the X axis labels (in degrees) public var labelRotationAngle = CGFloat(0.0) - - /// the space that should be left out (in characters) between the x-axis labels - /// This only applies if the number of labels that will be skipped in between drawn axis labels is not custom set. - /// - /// **default**: 4 - public var spaceBetweenLabels = Int(4) - - /// the modulus that indicates if a value at a specified index in an array(list) for the x-axis-labels is drawn or not. Draw when `(index % modulus) == 0`. - public var axisLabelModulus = Int(1) - - /// Is axisLabelModulus a custom value or auto calculated? If false, then it's auto, if true, then custom. - /// - /// **default**: false (automatic modulus) - private var _isAxisModulusCustom = false - - /// the modulus that indicates if a value at a specified index in an array(list) for the y-axis-labels is drawn or not. Draw when `(index % modulus) == 0`. - /// Used only for Horizontal BarChart - public var yAxisLabelModulus = Int(1) /// if set to true, the chart will avoid that the first and last label entry in the chart "clip" off the edge of the chart public var avoidFirstLastClippingEnabled = false - /// Custom formatter for adjusting x-value strings - private var _xAxisValueFormatter: ChartXAxisValueFormatter = ChartDefaultXAxisValueFormatter() - - /// Custom XValueFormatter for the data object that allows custom-formatting of all x-values before rendering them. - /// Provide null to reset back to the default formatting. - public var valueFormatter: ChartXAxisValueFormatter? - { - get - { - return _xAxisValueFormatter - } - set - { - _xAxisValueFormatter = newValue ?? ChartDefaultXAxisValueFormatter() - } - } - /// the position of the x-labels relative to the chart public var labelPosition = LabelPosition.Top /// if set to true, word wrapping the labels will be enabled. /// word wrapping is done using `(value width * labelRotatedWidth)` /// - /// *Note: currently supports all charts except pie/radar/horizontal-bar* + /// - note: currently supports all charts except pie/radar/horizontal-bar* public var wordWrapEnabled = false /// - returns: true if word wrapping the labels is enabled @@ -106,61 +69,9 @@ public class ChartXAxis: ChartAxisBase self.yOffset = 4.0; } - - public override func getLongestLabel() -> String - { - var longest = "" - - for i in 0 ..< values.count - { - let text = values[i] - - if (text != nil && longest.characters.count < (text!).characters.count) - { - longest = text! - } - } - - return longest - } public var isAvoidFirstLastClippingEnabled: Bool { return avoidFirstLastClippingEnabled } - - /// Sets the number of labels that should be skipped on the axis before the next label is drawn. - /// This will disable the feature that automatically calculates an adequate space between the axis labels and set the number of labels to be skipped to the fixed number provided by this method. - /// Call `resetLabelsToSkip(...)` to re-enable automatic calculation. - public func setLabelsToSkip(count: Int) - { - _isAxisModulusCustom = true - - if (count < 0) - { - axisLabelModulus = 1 - } - else - { - axisLabelModulus = count + 1 - } - } - - /// Calling this will disable a custom number of labels to be skipped (set by `setLabelsToSkip(...)`) while drawing the x-axis. Instead, the number of values to skip will again be calculated automatically. - public func resetLabelsToSkip() - { - _isAxisModulusCustom = false - } - - /// - returns: true if a custom axis-modulus has been set that determines the number of labels to skip when drawing. - public var isAxisModulusCustom: Bool - { - return _isAxisModulusCustom - } - - public var valuesObjc: [NSObject] - { - get { return ChartUtils.bridgedObjCGetStringArray(swift: values); } - set { self.values = ChartUtils.bridgedObjCGetStringArray(objc: newValue); } - } } diff --git a/Charts/Classes/Components/ChartYAxis.swift b/Charts/Classes/Components/ChartYAxis.swift index 08ee073c09..41f458b326 100644 --- a/Charts/Classes/Components/ChartYAxis.swift +++ b/Charts/Classes/Components/ChartYAxis.swift @@ -40,18 +40,9 @@ public class ChartYAxis: ChartAxisBase case Right } - public var entries = [Double]() - public var entryCount: Int { return entries.count; } - - /// the number of y-label entries the y-labels should have, default 6 - private var _labelCount = Int(6) - /// indicates if the top y-label entry is drawn or not public var drawTopYLabelEntryEnabled = true - /// if true, the y-labels show only the minimum and maximum value - public var showOnlyMinMaxEnabled = false - /// flag that indicates if the axis is inverted or not public var inverted = false @@ -75,9 +66,6 @@ public class ChartYAxis: ChartAxisBase } } } - - /// if true, the set number of y-labels will be forced - public var forceLabelsEnabled = false /// flag that indicates if the zero-line should be drawn regardless of other grid lines public var drawZeroLineEnabled = false @@ -95,12 +83,6 @@ public class ChartYAxis: ChartAxisBase /// I.e. [2, 3] will paint [-- -- ] /// [1, 3, 4, 2] will paint [- ---- - ---- ] public var zeroLineDashLengths: [CGFloat]? - - /// the formatter used to customly format the y-labels - public var valueFormatter: NSNumberFormatter? - - /// the formatter used to customly format the y-labels - internal var _defaultValueFormatter = NSNumberFormatter() /// axis space from the largest value to the top in percent of the total axis range public var spaceTop = CGFloat(0.1) @@ -125,41 +107,10 @@ public class ChartYAxis: ChartAxisBase /// **default**: CGFloat.infinity public var maxWidth = CGFloat(CGFloat.infinity) - /// When true, axis labels are controlled by the `granularity` property. - /// When false, axis values could possibly be repeated. - /// This could happen if two adjacent axis values are rounded to same value. - /// If using granularity this could be avoided by having fewer axis values visible. - public var granularityEnabled = false - - private var _granularity = Double(1.0) - - /// The minimum interval between axis values. - /// This can be used to avoid label duplicating when zooming in. - /// - /// **default**: 1.0 - public var granularity: Double - { - get - { - return _granularity - } - set - { - _granularity = newValue - - // set this to true if it was disabled, as it makes no sense to set this property with granularity disabled - granularityEnabled = true - } - } - public override init() { super.init() - _defaultValueFormatter.minimumIntegerDigits = 1 - _defaultValueFormatter.maximumFractionDigits = 1 - _defaultValueFormatter.minimumFractionDigits = 1 - _defaultValueFormatter.usesGroupingSeparator = true self.yOffset = 0.0 } @@ -169,10 +120,6 @@ public class ChartYAxis: ChartAxisBase _axisDependency = position - _defaultValueFormatter.minimumIntegerDigits = 1 - _defaultValueFormatter.maximumFractionDigits = 1 - _defaultValueFormatter.minimumFractionDigits = 1 - _defaultValueFormatter.usesGroupingSeparator = true self.yOffset = 0.0 } @@ -181,39 +128,6 @@ public class ChartYAxis: ChartAxisBase return _axisDependency } - public func setLabelCount(count: Int, force: Bool) - { - _labelCount = count - - if (_labelCount > 25) - { - _labelCount = 25 - } - if (_labelCount < 2) - { - _labelCount = 2 - } - - forceLabelsEnabled = force - } - - /// the number of label entries the y-axis should have - /// max = 25, - /// min = 2, - /// default = 6, - /// be aware that this number is not fixed and can only be approximated - public var labelCount: Int - { - get - { - return _labelCount - } - set - { - setLabelCount(newValue, force: false); - } - } - public func requiredSize() -> CGSize { let label = getLongestLabel() as NSString @@ -228,34 +142,6 @@ public class ChartYAxis: ChartAxisBase { return requiredSize().height } - - public override func getLongestLabel() -> String - { - var longest = "" - - for i in 0 ..< entries.count - { - let text = getFormattedLabel(i) - - if (longest.characters.count < text.characters.count) - { - longest = text - } - } - - return longest - } - - /// - returns: the formatted y-label at the specified index. This will either use the auto-formatter or the custom formatter (if one is set). - public func getFormattedLabel(index: Int) -> String - { - if (index < 0 || index >= entries.count) - { - return "" - } - - return (valueFormatter ?? _defaultValueFormatter).stringFromNumber(entries[index])! - } /// - returns: true if this axis needs horizontal offset, false if no offset is needed. public var needsOffset: Bool @@ -272,52 +158,44 @@ public class ChartYAxis: ChartAxisBase public var isInverted: Bool { return inverted; } - /// This is deprecated now, use `axisMinValue` - @available(*, deprecated=1.0, message="Use axisMinValue instead.") - public var isStartAtZeroEnabled: Bool { return startAtZeroEnabled } - - /// - returns: true if focing the y-label count is enabled. Default: false - public var isForceLabelsEnabled: Bool { return forceLabelsEnabled } - - public var isShowOnlyMinMaxEnabled: Bool { return showOnlyMinMaxEnabled; } - - public var isDrawTopYLabelEntryEnabled: Bool { return drawTopYLabelEntryEnabled; } - - /// Calculates the minimum, maximum and range values of the YAxis with the given minimum and maximum values from the chart data. - /// - parameter dataMin: the y-min value according to chart data - /// - parameter dataMax: the y-max value according to chart - public func calculate(min dataMin: Double, max dataMax: Double) + public override func calculate(min dataMin: Double, max dataMax: Double) { // if custom, use value as is, else use data value var min = _customAxisMin ? _axisMinimum : dataMin var max = _customAxisMax ? _axisMaximum : dataMax - + // temporary range (before calculations) let range = abs(max - min) - + // in case all values are equal if range == 0.0 { max = max + 1.0 min = min - 1.0 } - + // bottom-space only effects non-custom min if !_customAxisMin { - let bottomSpace = range * Double(spaceBottom) - _axisMinimum = min - bottomSpace + let bottomSpace = range / 100.0 * Double(spaceBottom) + _axisMinimum = (min - bottomSpace) } - + // top-space only effects non-custom max if !_customAxisMax { - let topSpace = range * Double(spaceTop) - _axisMaximum = max + topSpace + let topSpace = range / 100.0 * Double(spaceTop) + _axisMaximum = (max + topSpace) } - + // calc actual range axisRange = abs(_axisMaximum - _axisMinimum) } + /// This is deprecated now, use `axisMinValue` + @available(*, deprecated=1.0, message="Use axisMinValue instead.") + public var isStartAtZeroEnabled: Bool { return startAtZeroEnabled } + + public var isDrawTopYLabelEntryEnabled: Bool { return drawTopYLabelEntryEnabled; } + } diff --git a/Charts/Classes/Data/Implementations/ChartBaseDataSet.swift b/Charts/Classes/Data/Implementations/ChartBaseDataSet.swift index d6800dd98d..7884ed8a48 100644 --- a/Charts/Classes/Data/Implementations/ChartBaseDataSet.swift +++ b/Charts/Classes/Data/Implementations/ChartBaseDataSet.swift @@ -43,10 +43,10 @@ public class ChartBaseDataSet: NSObject, IChartDataSet /// Use this method to tell the data set that the underlying data has changed public func notifyDataSetChanged() { - calcMinMax(start: 0, end: entryCount - 1) + calcMinMax() } - public func calcMinMax(start start: Int, end: Int) + public func calcMinMax() { fatalError("calcMinMax is not implemented in ChartBaseDataSet") } @@ -61,19 +61,29 @@ public class ChartBaseDataSet: NSObject, IChartDataSet fatalError("yMax is not implemented in ChartBaseDataSet") } + public var xMin: Double + { + fatalError("xMin is not implemented in ChartBaseDataSet") + } + + public var xMax: Double + { + fatalError("xMax is not implemented in ChartBaseDataSet") + } + public var entryCount: Int { fatalError("entryCount is not implemented in ChartBaseDataSet") } - public func yValForXIndex(x: Int) -> Double + public func yValueForXValue(x: Double) -> Double { - fatalError("yValForXIndex is not implemented in ChartBaseDataSet") + fatalError("yValueForXValue is not implemented in ChartBaseDataSet") } - public func yValsForXIndex(x: Int) -> [Double] + public func yValuesForXValue(x: Double) -> [Double] { - fatalError("yValsForXIndex is not implemented in ChartBaseDataSet") + fatalError("yValuesForXValue is not implemented in ChartBaseDataSet") } public func entryForIndex(i: Int) -> ChartDataEntry? @@ -81,29 +91,29 @@ public class ChartBaseDataSet: NSObject, IChartDataSet fatalError("entryForIndex is not implemented in ChartBaseDataSet") } - public func entryForXIndex(x: Int, rounding: ChartDataSetRounding) -> ChartDataEntry? + public func entryForXPos(x: Double, rounding: ChartDataSetRounding) -> ChartDataEntry? { - fatalError("entryForXIndex is not implemented in ChartBaseDataSet") + fatalError("entryForXPos(x, rounding) is not implemented in ChartBaseDataSet") } - public func entryForXIndex(x: Int) -> ChartDataEntry? + public func entryForXPos(x: Double) -> ChartDataEntry? { - fatalError("entryForXIndex is not implemented in ChartBaseDataSet") + fatalError("entryForXPos(x) is not implemented in ChartBaseDataSet") } - public func entriesForXIndex(x: Int) -> [ChartDataEntry] + public func entriesForXPos(x: Double) -> [ChartDataEntry] { - fatalError("entriesForXIndex is not implemented in ChartBaseDataSet") + fatalError("entriesForXPos is not implemented in ChartBaseDataSet") } - public func entryIndex(xIndex x: Int, rounding: ChartDataSetRounding) -> Int + public func entryIndex(x x: Double, rounding: ChartDataSetRounding) -> Int { - fatalError("entryIndex is not implemented in ChartBaseDataSet") + fatalError("entryIndex(x, rounding) is not implemented in ChartBaseDataSet") } public func entryIndex(entry e: ChartDataEntry) -> Int { - fatalError("entryIndex is not implemented in ChartBaseDataSet") + fatalError("entryIndex(entry) is not implemented in ChartBaseDataSet") } public func addEntry(e: ChartDataEntry) -> Bool @@ -121,29 +131,44 @@ public class ChartBaseDataSet: NSObject, IChartDataSet fatalError("removeEntry is not implemented in ChartBaseDataSet") } - public func removeEntry(xIndex xIndex: Int) -> Bool + public func removeEntry(index index: Int) -> Bool { - if let entry = entryForXIndex(xIndex) + if let entry = entryForIndex(index) { return removeEntry(entry) } return false } - public func removeFirst() -> Bool + public func removeEntry(x x: Double) -> Bool { - if let entry = entryForIndex(0) + if let entry = entryForXPos(x) { return removeEntry(entry) } return false } + public func removeFirst() -> Bool + { + if entryCount > 0 + { + if let entry = entryForIndex(0) + { + return removeEntry(entry) + } + } + return false + } + public func removeLast() -> Bool { - if let entry = entryForIndex(entryCount - 1) + if entryCount > 0 { - return removeEntry(entry) + if let entry = entryForIndex(entryCount - 1) + { + return removeEntry(entry) + } } return false } diff --git a/Charts/Classes/Data/Implementations/Standard/BarChartData.swift b/Charts/Classes/Data/Implementations/Standard/BarChartData.swift index 42bffea7c4..991c3e6467 100644 --- a/Charts/Classes/Data/Implementations/Standard/BarChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/BarChartData.swift @@ -21,38 +21,88 @@ public class BarChartData: BarLineScatterCandleBubbleChartData super.init() } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) - { - super.init(xVals: xVals, dataSets: dataSets) - } - - private var _groupSpace = CGFloat(0.8) + /// The width of the bars on the x-axis, in values (not pixels) + /// + /// **default**: 1.0 + public var barWidth = Double(1.0) - /// The spacing is relative to a full bar width - public var groupSpace: CGFloat + /// Groups all BarDataSet objects this data object holds together by modifying the x-position of their entries. + /// Leaves space as specified by the parameters. + /// Do not forget to call notifyDataSetChanged() on your BarChart object after calling this method. + /// + /// - parameter the starting point on the x-axis where the grouping should begin + /// - parameter groupSpace: The space between groups of bars in values (not pixels) e.g. 0.8f for bar width 1f + /// - parameter barSpace: The space between individual bars in values (not pixels) e.g. 0.1f for bar width 1f + public func groupBars(fromX fromX: Double, groupSpace: Double, barSpace: Double) { - get + let setCount = _dataSets.count + if setCount <= 1 { - if (_dataSets.count <= 1) - { - return 0.0 - } - return _groupSpace + print("BarData needs to hold at least 2 BarDataSets to allow grouping.", terminator: "\n") + return } - set + + let max = maxEntryCountSet + let maxEntryCount = max?.entryCount ?? 0 + + let groupSpaceWidthHalf = groupSpace / 2.0 + let barSpaceHalf = barSpace / 2.0 + let barWidthHalf = self.barWidth / 2.0 + + var fromX = fromX + + let interval = groupWidth(groupSpace: groupSpace, barSpace: barSpace) + + for i in 0.stride(to: maxEntryCount, by: 1) { - _groupSpace = newValue + let start = fromX + fromX += groupSpaceWidthHalf + + for set in _dataSets as! [IBarChartDataSet] + { + fromX += barSpaceHalf + fromX += barWidthHalf + + if i < set.entryCount + { + if let entry = set.entryForIndex(i) + { + entry.x = fromX + } + } + + fromX += barWidthHalf + fromX += barSpaceHalf + } + + fromX += groupSpaceWidthHalf + let end = fromX + let innerInterval = end - start + let diff = interval - innerInterval + + // correct rounding errors + if diff > 0 || diff < 0 + { + fromX += diff + } + } + + notifyDataChanged() } - /// - returns: true if this BarData object contains grouped DataSets (more than 1 DataSet). - public var isGrouped: Bool + /// In case of grouped bars, this method returns the space an individual group of bar needs on the x-axis. + /// + /// - parameter groupSpace: + /// - parameter barSpace: + public func groupWidth(groupSpace groupSpace: Double, barSpace: Double) -> Double { - return _dataSets.count > 1 ? true : false + return Double(_dataSets.count) * (self.barWidth + barSpace) + groupSpace } + } diff --git a/Charts/Classes/Data/Implementations/Standard/BarChartDataEntry.swift b/Charts/Classes/Data/Implementations/Standard/BarChartDataEntry.swift index 7546d4f8ae..6380bd74c0 100644 --- a/Charts/Classes/Data/Implementations/Standard/BarChartDataEntry.swift +++ b/Charts/Classes/Data/Implementations/Standard/BarChartDataEntry.swift @@ -16,7 +16,7 @@ import Foundation public class BarChartDataEntry: ChartDataEntry { /// the values the stacked barchart holds - private var _values: [Double]? + private var _yVals: [Double]? /// the sum of all negative values this entry (if stacked) contains private var _negativeSum: Double = 0.0 @@ -30,45 +30,45 @@ public class BarChartDataEntry: ChartDataEntry } /// Constructor for stacked bar entries. - public init(values: [Double], xIndex: Int) + public init(x: Double, yValues: [Double]) { - super.init(value: BarChartDataEntry.calcSum(values), xIndex: xIndex) - self.values = values + super.init(x: x, y: BarChartDataEntry.calcSum(yValues)) + self._yVals = yValues calcPosNegSum() } /// Constructor for normal bars (not stacked). - public override init(value: Double, xIndex: Int) + public override init(x: Double, y: Double) { - super.init(value: value, xIndex: xIndex) + super.init(x: x, y: y) } /// Constructor for stacked bar entries. - public init(values: [Double], xIndex: Int, label: String) + public init(x: Double, yValues: [Double], label: String) { - super.init(value: BarChartDataEntry.calcSum(values), xIndex: xIndex, data: label) - self.values = values + super.init(x: x, y: BarChartDataEntry.calcSum(yValues), data: label) + self.yValues = yValues } /// Constructor for normal bars (not stacked). - public override init(value: Double, xIndex: Int, data: AnyObject?) + public override init(x: Double, y: Double, data: AnyObject?) { - super.init(value: value, xIndex: xIndex, data: data) + super.init(x: x, y: y, data: data) } public func getBelowSum(stackIndex :Int) -> Double { - if (values == nil) + if (_yVals == nil) { return 0 } var remainder: Double = 0.0 - var index = values!.count - 1 + var index = _yVals!.count - 1 while (index > stackIndex && index >= 0) { - remainder += values![index] + remainder += _yVals![index] index -= 1 } @@ -89,7 +89,7 @@ public class BarChartDataEntry: ChartDataEntry public func calcPosNegSum() { - if _values == nil + if _yVals == nil { _positiveSum = 0.0 _negativeSum = 0.0 @@ -99,7 +99,7 @@ public class BarChartDataEntry: ChartDataEntry var sumNeg: Double = 0.0 var sumPos: Double = 0.0 - for f in _values! + for f in _yVals! { if f < 0.0 { @@ -118,16 +118,16 @@ public class BarChartDataEntry: ChartDataEntry // MARK: Accessors /// the values the stacked barchart holds - public var isStacked: Bool { return _values != nil } + public var isStacked: Bool { return _yVals != nil } /// the values the stacked barchart holds - public var values: [Double]? + public var yValues: [Double]? { - get { return self._values } + get { return self._yVals } set { - self.value = BarChartDataEntry.calcSum(newValue) - self._values = newValue + self.y = BarChartDataEntry.calcSum(newValue) + self._yVals = newValue calcPosNegSum() } } @@ -137,8 +137,8 @@ public class BarChartDataEntry: ChartDataEntry public override func copyWithZone(zone: NSZone) -> AnyObject { let copy = super.copyWithZone(zone) as! BarChartDataEntry - copy._values = _values - copy.value = value + copy._yVals = _yVals + copy.y = y copy._negativeSum = _negativeSum return copy } diff --git a/Charts/Classes/Data/Implementations/Standard/BarChartDataSet.swift b/Charts/Classes/Data/Implementations/Standard/BarChartDataSet.swift index 9461ab2ca4..74ea324494 100644 --- a/Charts/Classes/Data/Implementations/Standard/BarChartDataSet.swift +++ b/Charts/Classes/Data/Implementations/Standard/BarChartDataSet.swift @@ -21,8 +21,8 @@ public class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartD { self.highlightColor = NSUIColor.blackColor() - self.calcStackSize(yVals as! [BarChartDataEntry]) - self.calcEntryCountIncludingStacks(yVals as! [BarChartDataEntry]) + self.calcStackSize(values as! [BarChartDataEntry]) + self.calcEntryCountIncludingStacks(values as! [BarChartDataEntry]) } public required init() @@ -31,9 +31,9 @@ public class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartD initialize() } - public override init(yVals: [ChartDataEntry]?, label: String?) + public override init(values: [ChartDataEntry]?, label: String?) { - super.init(yVals: yVals, label: label) + super.init(values: values, label: label) initialize() } @@ -54,7 +54,7 @@ public class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartD for i in 0 ..< yVals.count { - let vals = yVals[i].values + let vals = yVals[i].yValues if (vals == nil) { @@ -72,7 +72,7 @@ public class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartD { for i in 0 ..< yVals.count { - if let vals = yVals[i].values + if let vals = yVals[i].yValues { if vals.count > _stackSize { @@ -82,71 +82,58 @@ public class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartD } } - public override func calcMinMax(start start : Int, end: Int) + public override func calcMinMax() { - let yValCount = _yVals.count - - if yValCount == 0 + if _values.count == 0 { return } - var endValue : Int - - if end == 0 || end >= yValCount - { - endValue = yValCount - 1 - } - else - { - endValue = end - } - - _lastStart = start - _lastEnd = endValue - - _yMin = DBL_MAX _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - for i in start.stride(through: endValue, by: 1) + for e in _values as! [BarChartDataEntry] { - if let e = _yVals[i] as? BarChartDataEntry + if !e.y.isNaN { - if !e.value.isNaN + if e.yValues == nil { - if e.values == nil + if e.y < _yMin { - if e.value < _yMin - { - _yMin = e.value - } - - if e.value > _yMax - { - _yMax = e.value - } + _yMin = e.y } - else + + if e.y > _yMax { - if -e.negativeSum < _yMin - { - _yMin = -e.negativeSum - } - - if e.positiveSum > _yMax - { - _yMax = e.positiveSum - } + _yMax = e.y } } + else + { + if -e.negativeSum < _yMin + { + _yMin = -e.negativeSum + } + + if e.positiveSum > _yMax + { + _yMax = e.positiveSum + } + } + + if e.x < _xMin + { + _xMin = e.x + } + + if e.x > _xMax + { + _xMax = e.x + } } } - - if (_yMin == DBL_MAX) - { - _yMin = 0.0 - _yMax = 0.0 - } } /// - returns: the maximum number of bars that can be stacked upon another in this DataSet. @@ -172,9 +159,6 @@ public class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartD // MARK: - Styling functions and accessors - /// space indicator between the bars in percentage of the whole width of one value (0.15 == 15% of bar width) - public var barSpace: CGFloat = 0.15 - /// the color used for drawing the bar-shadows. The bar shadows is a surface behind the bar that indicates the maximum value public var barShadowColor = NSUIColor(red: 215.0/255.0, green: 215.0/255.0, blue: 215.0/255.0, alpha: 1.0) @@ -195,7 +179,7 @@ public class BarChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBarChartD copy._stackSize = _stackSize copy._entryCountStacks = _entryCountStacks copy.stackLabels = stackLabels - copy.barSpace = barSpace + copy.barShadowColor = barShadowColor copy.highlightAlpha = highlightAlpha return copy diff --git a/Charts/Classes/Data/Implementations/Standard/BarLineScatterCandleBubbleChartData.swift b/Charts/Classes/Data/Implementations/Standard/BarLineScatterCandleBubbleChartData.swift index 0a55d17966..34b05d3b15 100644 --- a/Charts/Classes/Data/Implementations/Standard/BarLineScatterCandleBubbleChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/BarLineScatterCandleBubbleChartData.swift @@ -20,13 +20,8 @@ public class BarLineScatterCandleBubbleChartData: ChartData super.init() } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) - } - - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) - { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } } diff --git a/Charts/Classes/Data/Implementations/Standard/BubbleChartData.swift b/Charts/Classes/Data/Implementations/Standard/BubbleChartData.swift index c681c382c7..2b62414ab3 100644 --- a/Charts/Classes/Data/Implementations/Standard/BubbleChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/BubbleChartData.swift @@ -19,20 +19,15 @@ public class BubbleChartData: BarLineScatterCandleBubbleChartData super.init() } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) - } - - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) - { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } /// Sets the width of the circle that surrounds the bubble when highlighted for all DataSet objects this data object contains public func setHighlightCircleWidth(width: CGFloat) { - for set in _dataSets as! [IBubbleChartDataSet]! + for set in (_dataSets as? [IBubbleChartDataSet])! { set.highlightCircleWidth = width } diff --git a/Charts/Classes/Data/Implementations/Standard/BubbleChartDataEntry.swift b/Charts/Classes/Data/Implementations/Standard/BubbleChartDataEntry.swift index 88c29b1099..08c97f74ab 100644 --- a/Charts/Classes/Data/Implementations/Standard/BubbleChartDataEntry.swift +++ b/Charts/Classes/Data/Implementations/Standard/BubbleChartDataEntry.swift @@ -22,23 +22,23 @@ public class BubbleChartDataEntry: ChartDataEntry super.init() } - /// - parameter xIndex: The index on the x-axis. - /// - parameter val: The value on the y-axis. + /// - parameter x: The index on the x-axis. + /// - parameter y: The value on the y-axis. /// - parameter size: The size of the bubble. - public init(xIndex: Int, value: Double, size: CGFloat) + public init(x: Double, y: Double, size: CGFloat) { - super.init(value: value, xIndex: xIndex) + super.init(x: x, y: y) self.size = size } - /// - parameter xIndex: The index on the x-axis. - /// - parameter val: The value on the y-axis. + /// - parameter x: The index on the x-axis. + /// - parameter y: The value on the y-axis. /// - parameter size: The size of the bubble. /// - parameter data: Spot for additional data this Entry represents. - public init(xIndex: Int, value: Double, size: CGFloat, data: AnyObject?) + public init(x: Double, y: Double, size: CGFloat, data: AnyObject?) { - super.init(value: value, xIndex: xIndex, data: data) + super.init(x: x, y: y, data: data) self.size = size } diff --git a/Charts/Classes/Data/Implementations/Standard/BubbleChartDataSet.swift b/Charts/Classes/Data/Implementations/Standard/BubbleChartDataSet.swift index 85fea17ce5..e98645e533 100644 --- a/Charts/Classes/Data/Implementations/Standard/BubbleChartDataSet.swift +++ b/Charts/Classes/Data/Implementations/Standard/BubbleChartDataSet.swift @@ -17,110 +17,39 @@ public class BubbleChartDataSet: BarLineScatterCandleBubbleChartDataSet, IBubble { // MARK: - Data functions and accessors - internal var _xMax = Double(0.0) - internal var _xMin = Double(0.0) internal var _maxSize = CGFloat(0.0) - public var xMin: Double { return _xMin } - public var xMax: Double { return _xMax } public var maxSize: CGFloat { return _maxSize } public var normalizeSizeEnabled: Bool = true public var isNormalizeSizeEnabled: Bool { return normalizeSizeEnabled } - public override func calcMinMax(start start: Int, end: Int) + public override func calcMinMax() { - let yValCount = self.entryCount - - if yValCount == 0 + if self.entryCount == 0 { return } - - let entries = yVals as! [BubbleChartDataEntry] // need chart width to guess this properly - var endValue : Int - - if end == 0 || end >= yValCount - { - endValue = yValCount - 1 - } - else - { - endValue = end - } - - _lastStart = start - _lastEnd = end + _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - _yMin = yMin(entries[start]) - _yMax = yMax(entries[start]) - - for i in start.stride(through: endValue, by: 1) + for e in values as! [BubbleChartDataEntry] { - let entry = entries[i] - - let ymin = yMin(entry) - let ymax = yMax(entry) - - if (ymin < _yMin) - { - _yMin = ymin - } - - if (ymax > _yMax) - { - _yMax = ymax - } - - let xmin = xMin(entry) - let xmax = xMax(entry) - - if (xmin < _xMin) - { - _xMin = xmin - } + calcMinMax(entry: e) - if (xmax > _xMax) - { - _xMax = xmax - } - - let size = largestSize(entry) + let size = e.size - if (size > _maxSize) + if size > _maxSize { _maxSize = size } } } - private func yMin(entry: BubbleChartDataEntry) -> Double - { - return entry.value - } - - private func yMax(entry: BubbleChartDataEntry) -> Double - { - return entry.value - } - - private func xMin(entry: BubbleChartDataEntry) -> Double - { - return Double(entry.xIndex) - } - - private func xMax(entry: BubbleChartDataEntry) -> Double - { - return Double(entry.xIndex) - } - - private func largestSize(entry: BubbleChartDataEntry) -> CGFloat - { - return entry.size - } - // MARK: - Styling functions and accessors /// Sets/gets the width of the circle that surrounds the bubble when highlighted diff --git a/Charts/Classes/Data/Implementations/Standard/CandleChartData.swift b/Charts/Classes/Data/Implementations/Standard/CandleChartData.swift index 349539d622..fce9951f76 100644 --- a/Charts/Classes/Data/Implementations/Standard/CandleChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/CandleChartData.swift @@ -20,13 +20,8 @@ public class CandleChartData: BarLineScatterCandleBubbleChartData super.init() } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) - } - - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) - { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } } diff --git a/Charts/Classes/Data/Implementations/Standard/CandleChartDataEntry.swift b/Charts/Classes/Data/Implementations/Standard/CandleChartDataEntry.swift index e42673ca36..f8b94aacc7 100644 --- a/Charts/Classes/Data/Implementations/Standard/CandleChartDataEntry.swift +++ b/Charts/Classes/Data/Implementations/Standard/CandleChartDataEntry.swift @@ -32,9 +32,9 @@ public class CandleChartDataEntry: ChartDataEntry super.init() } - public init(xIndex: Int, shadowH: Double, shadowL: Double, open: Double, close: Double) + public init(x: Double, shadowH: Double, shadowL: Double, open: Double, close: Double) { - super.init(value: (shadowH + shadowL) / 2.0, xIndex: xIndex) + super.init(x: x, y: (shadowH + shadowL) / 2.0) self.high = shadowH self.low = shadowL @@ -42,9 +42,9 @@ public class CandleChartDataEntry: ChartDataEntry self.close = close } - public init(xIndex: Int, shadowH: Double, shadowL: Double, open: Double, close: Double, data: AnyObject?) + public init(x: Double, shadowH: Double, shadowL: Double, open: Double, close: Double, data: AnyObject?) { - super.init(value: (shadowH + shadowL) / 2.0, xIndex: xIndex, data: data) + super.init(x: x, y: (shadowH + shadowL) / 2.0, data: data) self.high = shadowH self.low = shadowL @@ -65,15 +65,15 @@ public class CandleChartDataEntry: ChartDataEntry } /// the center value of the candle. (Middle value between high and low) - public override var value: Double + public override var y: Double { get { - return super.value + return super.y } set { - super.value = (high + low) / 2.0 + super.y = (high + low) / 2.0 } } diff --git a/Charts/Classes/Data/Implementations/Standard/CandleChartDataSet.swift b/Charts/Classes/Data/Implementations/Standard/CandleChartDataSet.swift index a58d97f997..c32d7a7908 100644 --- a/Charts/Classes/Data/Implementations/Standard/CandleChartDataSet.swift +++ b/Charts/Classes/Data/Implementations/Standard/CandleChartDataSet.swift @@ -23,14 +23,14 @@ public class CandleChartDataSet: LineScatterCandleRadarChartDataSet, ICandleChar super.init() } - public override init(yVals: [ChartDataEntry]?, label: String?) + public override init(values: [ChartDataEntry]?, label: String?) { - super.init(yVals: yVals, label: label) + super.init(values: values, label: label) } // MARK: - Data functions and accessors - public override func calcMinMax(start start: Int, end: Int) + public override func calcMinMax() { let yValCount = self.entryCount @@ -39,29 +39,13 @@ public class CandleChartDataSet: LineScatterCandleRadarChartDataSet, ICandleChar return } - var entries = yVals as! [CandleChartDataEntry] - - var endValue : Int - - if end == 0 || end >= yValCount - { - endValue = yValCount - 1 - } - else - { - endValue = end - } - - _lastStart = start - _lastEnd = end - - _yMin = DBL_MAX _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - for i in start.stride(through: endValue, by: 1) + for e in _values as! [CandleChartDataEntry] { - let e = entries[i] - if (e.low < _yMin) { _yMin = e.low @@ -71,6 +55,16 @@ public class CandleChartDataSet: LineScatterCandleRadarChartDataSet, ICandleChar { _yMax = e.high } + + if (e.x < _xMin) + { + _xMin = e.x + } + + if (e.x > _xMax) + { + _xMax = e.x + } } } diff --git a/Charts/Classes/Data/Implementations/Standard/ChartData.swift b/Charts/Classes/Data/Implementations/Standard/ChartData.swift index 58da7a2c1f..a94fd81513 100644 --- a/Charts/Classes/Data/Implementations/Standard/ChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/ChartData.swift @@ -14,257 +14,220 @@ import Foundation - public class ChartData: NSObject { - internal var _yMax = Double(0.0) - internal var _yMin = Double(0.0) - internal var _leftAxisMax = Double(0.0) - internal var _leftAxisMin = Double(0.0) - internal var _rightAxisMax = Double(0.0) - internal var _rightAxisMin = Double(0.0) - private var _yValCount = Int(0) - - /// the last start value used for calcMinMax - internal var _lastStart: Int = 0 - - /// the last end value used for calcMinMax - internal var _lastEnd: Int = 0 + internal var _yMax: Double = -DBL_MAX + internal var _yMin: Double = DBL_MAX + internal var _xMax: Double = -DBL_MAX + internal var _xMin: Double = DBL_MAX + internal var _leftAxisMax: Double = -DBL_MAX + internal var _leftAxisMin: Double = DBL_MAX + internal var _rightAxisMax: Double = -DBL_MAX + internal var _rightAxisMin: Double = DBL_MAX - /// the average length (in characters) across all x-value strings - private var _xValAverageLength = Double(0.0) - - internal var _xVals: [String?]! - internal var _dataSets: [IChartDataSet]! + internal var _dataSets = [IChartDataSet]() public override init() { super.init() - _xVals = [String?]() _dataSets = [IChartDataSet]() } - public init(xVals: [String?]?, dataSets: [IChartDataSet]?) - { - super.init() - - _xVals = xVals == nil ? [String?]() : xVals - _dataSets = dataSets == nil ? [IChartDataSet]() : dataSets - - self.initialize(_dataSets) - } - - public init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) + public init(dataSets: [IChartDataSet]?) { super.init() - _xVals = xVals == nil ? [String?]() : ChartUtils.bridgedObjCGetStringArray(objc: xVals!) - _dataSets = dataSets == nil ? [IChartDataSet]() : dataSets + _dataSets = dataSets ?? [IChartDataSet]() self.initialize(_dataSets) } - public convenience init(xVals: [String?]?) - { - self.init(xVals: xVals, dataSets: [IChartDataSet]()) - } - - public convenience init(xVals: [NSObject]?) + public convenience init(dataSet: IChartDataSet?) { - self.init(xVals: xVals, dataSets: [IChartDataSet]()) + self.init(dataSets: dataSet === nil ? nil : [dataSet!]) } - public convenience init(xVals: [String?]?, dataSet: IChartDataSet?) + internal func initialize(dataSets: [IChartDataSet]) { - self.init(xVals: xVals, dataSets: dataSet === nil ? nil : [dataSet!]) + calcMinMax() } - public convenience init(xVals: [NSObject]?, dataSet: IChartDataSet?) + /// Call this method to let the ChartData know that the underlying data has changed. + /// Calling this performs all necessary recalculations needed when the contained data has changed. + public func notifyDataChanged() { - self.init(xVals: xVals, dataSets: dataSet === nil ? nil : [dataSet!]) + initialize(_dataSets) } - internal func initialize(dataSets: [IChartDataSet]) + /// calc minimum and maximum y value over all datasets + public func calcMinMax() { - checkIsLegal(dataSets) + _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - calcMinMax(start: _lastStart, end: _lastEnd) - calcYValueCount() - - calcXValAverageLength() - } - - // calculates the average length (in characters) across all x-value strings - internal func calcXValAverageLength() - { - if (_xVals.count == 0) + for set in _dataSets { - _xValAverageLength = 1 - return + calcMinMax(dataSet: set) } - var sum = 1 + _leftAxisMax = -DBL_MAX + _leftAxisMin = DBL_MAX + _rightAxisMax = -DBL_MAX + _rightAxisMin = DBL_MAX - for i in 0 ..< _xVals.count + // left axis + let firstLeft = getFirstLeft() + + if firstLeft !== nil { - sum += _xVals[i] == nil ? 0 : (_xVals[i]!).characters.count + _leftAxisMax = firstLeft!.yMax + _leftAxisMin = firstLeft!.yMin + + for dataSet in _dataSets + { + if dataSet.axisDependency == .Left + { + if dataSet.yMin < _leftAxisMin + { + _leftAxisMin = dataSet.yMin + } + + if dataSet.yMax > _leftAxisMax + { + _leftAxisMax = dataSet.yMax + } + } + } } - _xValAverageLength = Double(sum) / Double(_xVals.count) + // right axis + let firstRight = getFirstRight() + + if firstRight !== nil + { + _rightAxisMax = firstRight!.yMax + _rightAxisMin = firstRight!.yMin + + for dataSet in _dataSets + { + if dataSet.axisDependency == .Right + { + if dataSet.yMin < _rightAxisMin + { + _rightAxisMin = dataSet.yMin + } + + if dataSet.yMax > _rightAxisMax + { + _rightAxisMax = dataSet.yMax + } + } + } + } } - // Checks if the combination of x-values array and DataSet array is legal or not. - // :param: dataSets - internal func checkIsLegal(dataSets: [IChartDataSet]!) + public func calcMinMax(entry e: ChartDataEntry, axis: ChartYAxis.AxisDependency) { - if (dataSets == nil) + if _yMax < e.y { - return + _yMax = e.y } - if self is ScatterChartData - { // In scatter chart it makes sense to have more than one y-value value for an x-index - return + if _yMin > e.y + { + _yMin = e.y } - for i in 0 ..< dataSets.count + if _xMax < e.x { - if (dataSets[i].entryCount > _xVals.count) - { - print("One or more of the DataSet Entry arrays are longer than the x-values array of this Data object.", terminator: "\n") - return - } + _xMax = e.x } - } - - /// Call this method to let the ChartData know that the underlying data has changed. - /// Calling this performs all necessary recalculations needed when the contained data has changed. - public func notifyDataChanged() - { - initialize(_dataSets) - } - - /// calc minimum and maximum y value over all datasets - internal func calcMinMax(start start: Int, end: Int) - { - if (_dataSets == nil || _dataSets.count < 1) + if _xMin > e.x { - _yMax = 0.0 - _yMin = 0.0 + _xMin = e.x } - else + + if axis == .Left { - _lastStart = start - _lastEnd = end - - _yMin = DBL_MAX - _yMax = -DBL_MAX - - for i in 0 ..< _dataSets.count + if _leftAxisMax < e.y { - _dataSets[i].calcMinMax(start: start, end: end) - - if (_dataSets[i].yMin < _yMin) - { - _yMin = _dataSets[i].yMin - } - - if (_dataSets[i].yMax > _yMax) - { - _yMax = _dataSets[i].yMax - } + _leftAxisMax = e.y } - if (_yMin == DBL_MAX) + if _leftAxisMin > e.y { - _yMin = 0.0 - _yMax = 0.0 + _leftAxisMin = e.y } - - // left axis - let firstLeft = getFirstLeft() - - if (firstLeft !== nil) + } + else + { + if _rightAxisMax < e.y { - _leftAxisMax = firstLeft!.yMax - _leftAxisMin = firstLeft!.yMin - - for dataSet in _dataSets - { - if (dataSet.axisDependency == .Left) - { - if (dataSet.yMin < _leftAxisMin) - { - _leftAxisMin = dataSet.yMin - } - - if (dataSet.yMax > _leftAxisMax) - { - _leftAxisMax = dataSet.yMax - } - } - } + _rightAxisMax = e.y } - - // right axis - let firstRight = getFirstRight() - - if (firstRight !== nil) + + if _rightAxisMin > e.y { - _rightAxisMax = firstRight!.yMax - _rightAxisMin = firstRight!.yMin - - for dataSet in _dataSets - { - if (dataSet.axisDependency == .Right) - { - if (dataSet.yMin < _rightAxisMin) - { - _rightAxisMin = dataSet.yMin - } - - if (dataSet.yMax > _rightAxisMax) - { - _rightAxisMax = dataSet.yMax - } - } - } + _rightAxisMin = e.y } - - // in case there is only one axis, adjust the second axis - handleEmptyAxis(firstLeft, firstRight: firstRight) } } - /// Calculates the total number of y-values across all ChartDataSets the ChartData represents. - internal func calcYValueCount() + public func calcMinMax(dataSet d: IChartDataSet) { - _yValCount = 0 + if _yMax < d.yMax + { + _yMax = d.yMax + } - if (_dataSets == nil) + if _yMin > d.yMin { - return + _yMin = d.yMin } - var count = 0 + if _xMax < d.xMax + { + _xMax = d.xMax + } - for i in 0 ..< _dataSets.count + if _xMin > d.xMin { - count += _dataSets[i].entryCount + _xMin = d.xMin } - _yValCount = count + if d.axisDependency == .Left + { + if _leftAxisMax < d.yMax + { + _leftAxisMax = d.yMax + } + + if _leftAxisMin > d.yMin + { + _leftAxisMin = d.yMin + } + } + else + { + if _rightAxisMax < d.yMax + { + _rightAxisMax = d.yMax + } + + if _rightAxisMin > d.yMin + { + _rightAxisMin = d.yMin + } + } } /// - returns: the number of LineDataSets this object contains public var dataSetCount: Int { - if (_dataSets == nil) - { - return 0 - } return _dataSets.count } @@ -274,6 +237,7 @@ public class ChartData: NSObject return _yMin } + @nonobjc public func getYMin() -> Double { return _yMin @@ -281,13 +245,27 @@ public class ChartData: NSObject public func getYMin(axis: ChartYAxis.AxisDependency) -> Double { - if (axis == .Left) + if axis == .Left { - return _leftAxisMin + if _leftAxisMin == DBL_MAX + { + return _rightAxisMin + } + else + { + return _leftAxisMin + } } else { - return _rightAxisMin + if _rightAxisMin == DBL_MAX + { + return _leftAxisMin + } + else + { + return _rightAxisMin + } } } @@ -297,6 +275,7 @@ public class ChartData: NSObject return _yMax } + @nonobjc public func getYMax() -> Double { return _yMax @@ -304,54 +283,42 @@ public class ChartData: NSObject public func getYMax(axis: ChartYAxis.AxisDependency) -> Double { - if (axis == .Left) + if axis == .Left { - return _leftAxisMax + if _leftAxisMax == -DBL_MAX + { + return _rightAxisMax + } + else + { + return _leftAxisMax + } } else { - return _rightAxisMax - } - } - - /// - returns: the average length (in characters) across all values in the x-vals array - public var xValAverageLength: Double - { - return _xValAverageLength - } - - /// - returns: the total number of y-values across all DataSet objects the this object represents. - public var yValCount: Int - { - return _yValCount - } - - /// - returns: the x-values the chart represents - public var xVals: [String?] - { - get - { - return _xVals - } - set - { - _xVals = newValue + if _rightAxisMax == -DBL_MAX + { + return _leftAxisMax + } + else + { + return _rightAxisMax + } } } - ///Adds a new x-value to the chart data. - public func addXValue(xVal: String?) + /// - returns: the minimum x-value the data object contains. + public var xMin: Double { - _xVals.append(xVal) + return _xMin } - - /// Removes the x-value at the specified index. - public func removeXValue(index: Int) + /// - returns: the maximum x-value the data object contains. + public var xMax: Double { - _xVals.removeAtIndex(index) + return _xMax } - /// - returns: the array of ChartDataSets this object holds. + /// - returns: all DataSet objects this ChartData object holds. public var dataSets: [IChartDataSet] { get @@ -403,12 +370,6 @@ public class ChartData: NSObject return -1 } - /// - returns: the total number of x-values this ChartData object represents (the size of the x-values array) - public var xValCount: Int - { - return _xVals.count - } - /// - returns: the labels of all DataSets as a string array. internal func dataSetLabels() -> [String] { @@ -440,11 +401,11 @@ public class ChartData: NSObject else { // The value of the highlighted entry could be NaN - if we are not interested in highlighting a specific value. - - let entries = _dataSets[highlight.dataSetIndex].entriesForXIndex(highlight.xIndex) + // FIXME: Fix on android + let entries = _dataSets[highlight.dataSetIndex].entriesForXPos(highlight.x) for e in entries { - if e.value == highlight.value || isnan(highlight.value) + if e.y == highlight.y || isnan(highlight.y) { return e } @@ -475,7 +436,7 @@ public class ChartData: NSObject public func getDataSetByIndex(index: Int) -> IChartDataSet! { - if (_dataSets == nil || index < 0 || index >= _dataSets.count) + if index < 0 || index >= _dataSets.count { return nil } @@ -485,82 +446,9 @@ public class ChartData: NSObject public func addDataSet(d: IChartDataSet!) { - if (_dataSets == nil) - { - return - } - - _yValCount += d.entryCount - - if (_dataSets.count == 0) - { - _yMax = d.yMax - _yMin = d.yMin - - if (d.axisDependency == .Left) - { - _leftAxisMax = d.yMax - _leftAxisMin = d.yMin - } - else - { - _rightAxisMax = d.yMax - _rightAxisMin = d.yMin - } - } - else - { - if (_yMax < d.yMax) - { - _yMax = d.yMax - } - if (_yMin > d.yMin) - { - _yMin = d.yMin - } - - if (d.axisDependency == .Left) - { - if (_leftAxisMax < d.yMax) - { - _leftAxisMax = d.yMax - } - if (_leftAxisMin > d.yMin) - { - _leftAxisMin = d.yMin - } - } - else - { - if (_rightAxisMax < d.yMax) - { - _rightAxisMax = d.yMax - } - if (_rightAxisMin > d.yMin) - { - _rightAxisMin = d.yMin - } - } - } + calcMinMax(dataSet: d) _dataSets.append(d) - - handleEmptyAxis(getFirstLeft(), firstRight: getFirstRight()) - } - - public func handleEmptyAxis(firstLeft: IChartDataSet?, firstRight: IChartDataSet?) - { - // in case there is only one axis, adjust the second axis - if (firstLeft === nil) - { - _leftAxisMax = _rightAxisMax - _leftAxisMin = _rightAxisMin - } - else if (firstRight === nil) - { - _rightAxisMax = _leftAxisMax - _rightAxisMin = _leftAxisMin - } } /// Removes the given DataSet from this data object. @@ -569,7 +457,7 @@ public class ChartData: NSObject /// - returns: true if a DataSet was removed, false if no DataSet could be removed. public func removeDataSet(dataSet: IChartDataSet!) -> Bool { - if (_dataSets == nil || dataSet === nil) + if dataSet === nil { return false } @@ -591,15 +479,14 @@ public class ChartData: NSObject /// - returns: true if a DataSet was removed, false if no DataSet could be removed. public func removeDataSetByIndex(index: Int) -> Bool { - if (_dataSets == nil || index >= _dataSets.count || index < 0) + if index >= _dataSets.count || index < 0 { return false } - let d = _dataSets.removeAtIndex(index) - _yValCount -= d.entryCount + _dataSets.removeAtIndex(index) - calcMinMax(start: _lastStart, end: _lastEnd) + calcMinMax() return true } @@ -607,79 +494,25 @@ public class ChartData: NSObject /// Adds an Entry to the DataSet at the specified index. Entries are added to the end of the list. public func addEntry(e: ChartDataEntry, dataSetIndex: Int) { - if _dataSets != nil && _dataSets.count > dataSetIndex && dataSetIndex >= 0 + if _dataSets.count > dataSetIndex && dataSetIndex >= 0 { - let val = e.value let set = _dataSets[dataSetIndex] if !set.addEntry(e) { return } - if (_yValCount == 0) - { - _yMin = val - _yMax = val - - if (set.axisDependency == .Left) - { - _leftAxisMax = e.value - _leftAxisMin = e.value - } - else - { - _rightAxisMax = e.value - _rightAxisMin = e.value - } - } - else - { - if (_yMax < val) - { - _yMax = val - } - if (_yMin > val) - { - _yMin = val - } - - if (set.axisDependency == .Left) - { - if (_leftAxisMax < e.value) - { - _leftAxisMax = e.value - } - if (_leftAxisMin > e.value) - { - _leftAxisMin = e.value - } - } - else - { - if (_rightAxisMax < e.value) - { - _rightAxisMax = e.value - } - if (_rightAxisMin > e.value) - { - _rightAxisMin = e.value - } - } - } - - _yValCount += 1 - - handleEmptyAxis(getFirstLeft(), firstRight: getFirstRight()) + calcMinMax(entry: e, axis: set.axisDependency) } else { - print("ChartData.addEntry() - dataSetIndex out of range.", terminator: "\n") + print("ChartData.addEntry() - Cannot add Entry because dataSetIndex too high or too low.", terminator: "\n") } } /// Removes the given Entry object from the DataSet at the specified index. - public func removeEntry(entry: ChartDataEntry!, dataSetIndex: Int) -> Bool + public func removeEntry(entry: ChartDataEntry, dataSetIndex: Int) -> Bool { - // entry null, outofbounds - if (entry === nil || dataSetIndex >= _dataSets.count) + // entry outofbounds + if dataSetIndex >= _dataSets.count { return false } @@ -689,32 +522,28 @@ public class ChartData: NSObject if (removed) { - _yValCount -= 1 - - calcMinMax(start: _lastStart, end: _lastEnd) + calcMinMax() } return removed } - /// Removes the Entry object at the given xIndex from the ChartDataSet at the + /// Removes the Entry object closest to the given xIndex from the ChartDataSet at the /// specified index. /// - returns: true if an entry was removed, false if no Entry was found that meets the specified requirements. - public func removeEntryByXIndex(xIndex: Int, dataSetIndex: Int) -> Bool + public func removeEntry(xValue xValue: Double, dataSetIndex: Int) -> Bool { - if (dataSetIndex >= _dataSets.count) + if dataSetIndex >= _dataSets.count { return false } - let entry = _dataSets[dataSetIndex].entryForXIndex(xIndex) - - if (entry?.xIndex != xIndex) + if let entry = _dataSets[dataSetIndex].entryForXPos(xValue) { - return false + return removeEntry(entry, dataSetIndex: dataSetIndex) } - return removeEntry(entry, dataSetIndex: dataSetIndex) + return false } /// - returns: the DataSet that contains the provided Entry, or null, if no DataSet contains this entry. @@ -729,7 +558,7 @@ public class ChartData: NSObject { let set = _dataSets[i] - if (e === set.entryForXIndex(e.xIndex)) + if (e === set.entryForXPos(e.x)) { return set } @@ -737,8 +566,8 @@ public class ChartData: NSObject return nil } - - /// - returns: the index of the provided DataSet inside the DataSets array of this data object. -1 if the DataSet was not found. + + /// - returns: the index of the provided DataSet in the DataSet array of this data object, or -1 if it does not exist. public func indexOfDataSet(dataSet: IChartDataSet) -> Int { for i in 0 ..< _dataSets.count @@ -783,11 +612,6 @@ public class ChartData: NSObject /// - returns: all colors used across all DataSet objects this object represents. public func getColors() -> [NSUIColor]? { - if (_dataSets == nil) - { - return nil - } - var clrcnt = 0 for i in 0 ..< _dataSets.count @@ -810,19 +634,6 @@ public class ChartData: NSObject return colors } - /// Generates an x-values array filled with numbers in range specified by the parameters. Can be used for convenience. - public func generateXVals(from: Int, to: Int) -> [String] - { - var xvals = [String]() - - for i in from ..< to - { - xvals.append(String(i)) - } - - return xvals - } - /// Sets a custom ValueFormatter for all DataSets this data object contains. public func setValueFormatter(formatter: NSNumberFormatter!) { @@ -895,13 +706,13 @@ public class ChartData: NSObject notifyDataChanged() } - /// Checks if this data object contains the specified Entry. + /// Checks if this data object contains the specified DataSet. /// - returns: true if so, false if not. - public func contains(entry entry: ChartDataEntry) -> Bool + public func contains(dataSet dataSet: IChartDataSet) -> Bool { for set in dataSets { - if set.contains(entry) + if set === dataSet { return true } @@ -910,33 +721,37 @@ public class ChartData: NSObject return false } - /// Checks if this data object contains the specified DataSet. - /// - returns: true if so, false if not. - public func contains(dataSet dataSet: IChartDataSet) -> Bool + /// - returns: The total entry count across all DataSet objects this data object contains. + public var entryCount: Int { - for set in dataSets + var count = 0 + + for set in _dataSets { - if set === dataSet - { - return true - } + count += set.entryCount } - return false + return count } - - /// MARK: - ObjC compatibility - - /// - returns: the average length (in characters) across all values in the x-vals array - public var xValsObjc: [NSObject] + + /// - returns: the DataSet object with the maximum number of entries or null if there are no DataSets. + public var maxEntryCountSet: IChartDataSet? { - get + if _dataSets.count == 0 { - return ChartUtils.bridgedObjCGetStringArray(swift: _xVals); + return nil } - set + + var max = _dataSets[0] + + for set in _dataSets { - _xVals = ChartUtils.bridgedObjCGetStringArray(objc: newValue) + if set.entryCount > max.entryCount + { + max = set + } } + + return max } } diff --git a/Charts/Classes/Data/Implementations/Standard/ChartDataEntry.swift b/Charts/Classes/Data/Implementations/Standard/ChartDataEntry.swift index 59fb5a8b32..96b8d68e6c 100644 --- a/Charts/Classes/Data/Implementations/Standard/ChartDataEntry.swift +++ b/Charts/Classes/Data/Implementations/Standard/ChartDataEntry.swift @@ -14,36 +14,37 @@ import Foundation -public class ChartDataEntry: NSObject +public class ChartDataEntry: ChartDataEntryBase { - /// the actual value (y axis) - public var value = Double(0.0) + /// the x value + public var x = Double(0.0) - /// the index on the x-axis - public var xIndex = Int(0) - - /// optional spot for additional data this Entry represents - public var data: AnyObject? - - public override required init() + public required init() { super.init() } - public init(value: Double, xIndex: Int) + /// An Entry represents one single entry in the chart. + /// - parameter x: the x value + /// - parameter y: the y value (the actual value of the entry) + public init(x: Double, y: Double) { - super.init() + super.init(y: y) - self.value = value - self.xIndex = xIndex + self.x = x } - public init(value: Double, xIndex: Int, data: AnyObject?) + /// An Entry represents one single entry in the chart. + /// - parameter x: the x value + /// - parameter y: the y value (the actual value of the entry) + /// - parameter data: Space for additional data this Entry represents. + + public init(x: Double, y: Double, data: AnyObject?) { - super.init() + super.init(y: y) - self.value = value - self.xIndex = xIndex + self.x = x + self.data = data } @@ -51,27 +52,12 @@ public class ChartDataEntry: NSObject public override func isEqual(object: AnyObject?) -> Bool { - if (object === nil) - { - return false - } - - if (!object!.isKindOfClass(self.dynamicType)) - { - return false - } - - if (object!.data !== data && !object!.data.isEqual(self.data)) - { - return false - } - - if (object!.xIndex != xIndex) + if !super.isEqual(object) { return false } - if (fabs(object!.value - value) > 0.00001) + if fabs(object!.x - x) > DBL_EPSILON { return false } @@ -83,7 +69,7 @@ public class ChartDataEntry: NSObject public override var description: String { - return "ChartDataEntry, xIndex: \(xIndex), value \(value)" + return "ChartDataEntry, x: \(x), y \(y)" } // MARK: NSCopying @@ -92,8 +78,8 @@ public class ChartDataEntry: NSObject { let copy = self.dynamicType.init() - copy.value = value - copy.xIndex = xIndex + copy.x = x + copy.y = y copy.data = data return copy @@ -102,27 +88,27 @@ public class ChartDataEntry: NSObject public func ==(lhs: ChartDataEntry, rhs: ChartDataEntry) -> Bool { - if (lhs === rhs) + if lhs === rhs { return true } - if (!lhs.isKindOfClass(rhs.dynamicType)) + if !lhs.isKindOfClass(rhs.dynamicType) { return false } - if (lhs.data !== rhs.data && !lhs.data!.isEqual(rhs.data)) + if lhs.data !== rhs.data && !lhs.data!.isEqual(rhs.data) { return false } - if (lhs.xIndex != rhs.xIndex) + if fabs(lhs.x - rhs.x) > DBL_EPSILON { return false } - if (fabs(lhs.value - rhs.value) > 0.00001) + if fabs(lhs.y - rhs.y) > DBL_EPSILON { return false } diff --git a/Charts/Classes/Data/Implementations/Standard/ChartDataEntryBase.swift b/Charts/Classes/Data/Implementations/Standard/ChartDataEntryBase.swift new file mode 100644 index 0000000000..c8cc6f35ca --- /dev/null +++ b/Charts/Classes/Data/Implementations/Standard/ChartDataEntryBase.swift @@ -0,0 +1,106 @@ +// +// ChartDataEntryBase.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/Charts +// + +import Foundation + +public class ChartDataEntryBase: NSObject +{ + /// the y value + public var y = Double(0.0) + + /// optional spot for additional data this Entry represents + public var data: AnyObject? + + public override required init() + { + super.init() + } + + /// An Entry represents one single entry in the chart. + /// - parameter y: the y value (the actual value of the entry) + public init(y: Double) + { + super.init() + + self.y = y + } + + /// - parameter y: the y value (the actual value of the entry) + /// - parameter data: Space for additional data this Entry represents. + + public init(y: Double, data: AnyObject?) + { + super.init() + + self.y = y + self.data = data + } + + // MARK: NSObject + + public override func isEqual(object: AnyObject?) -> Bool + { + if object === nil + { + return false + } + + if !object!.isKindOfClass(self.dynamicType) + { + return false + } + + if object!.data !== data && !object!.data.isEqual(self.data) + { + return false + } + + // FIXME: On Android too, those should be epsilons + if fabs(object!.y - y) > DBL_EPSILON + { + return false + } + + return true + } + + // MARK: NSObject + + public override var description: String + { + return "ChartDataEntryBase, y \(y)" + } +} + +public func ==(lhs: ChartDataEntryBase, rhs: ChartDataEntryBase) -> Bool +{ + if lhs === rhs + { + return true + } + + if !lhs.isKindOfClass(rhs.dynamicType) + { + return false + } + + if lhs.data !== rhs.data && !lhs.data!.isEqual(rhs.data) + { + return false + } + + if fabs(lhs.y - rhs.y) > DBL_EPSILON + { + return false + } + + return true +} \ No newline at end of file diff --git a/Charts/Classes/Data/Implementations/Standard/ChartDataSet.swift b/Charts/Classes/Data/Implementations/Standard/ChartDataSet.swift index 34e2aa3f1d..1a35691ec7 100644 --- a/Charts/Classes/Data/Implementations/Standard/ChartDataSet.swift +++ b/Charts/Classes/Data/Implementations/Standard/ChartDataSet.swift @@ -14,6 +14,7 @@ import Foundation +/// Determines how to round DataSet index values for `ChartDataSet.entryIndex(x, rounding)` when an exact x-pos is not found. @objc public enum ChartDataSetRounding: Int { @@ -22,58 +23,67 @@ public enum ChartDataSetRounding: Int case Closest = 2 } +/// The DataSet class represents one group or type of entries (Entry) in the Chart that belong together. +/// It is designed to logically separate different groups of values inside the Chart (e.g. the values for a specific line in the LineChart, or the values of a specific group of bars in the BarChart). public class ChartDataSet: ChartBaseDataSet { public required init() { super.init() - _yVals = [ChartDataEntry]() + _values = [ChartDataEntry]() } public override init(label: String?) { super.init(label: label) - _yVals = [ChartDataEntry]() + _values = [ChartDataEntry]() } - public init(yVals: [ChartDataEntry]?, label: String?) + public init(values: [ChartDataEntry]?, label: String?) { super.init(label: label) - _yVals = yVals == nil ? [ChartDataEntry]() : yVals + _values = values == nil ? [ChartDataEntry]() : values - self.calcMinMax(start: _lastStart, end: _lastEnd) + self.calcMinMax() } - public convenience init(yVals: [ChartDataEntry]?) + public convenience init(values: [ChartDataEntry]?) { - self.init(yVals: yVals, label: "DataSet") + self.init(values: values, label: "DataSet") } // MARK: - Data functions and accessors - internal var _yVals: [ChartDataEntry]! - internal var _yMax = Double(0.0) - internal var _yMin = Double(0.0) + /// the entries that this dataset represents / holds together + internal var _values: [ChartDataEntry]! - /// the last start value used for calcMinMax - internal var _lastStart: Int = 0 + /// maximum y-value in the value array + internal var _yMax: Double = -DBL_MAX - /// the last end value used for calcMinMax - internal var _lastEnd: Int = 0 + /// minimum y-value in the value array + internal var _yMin: Double = DBL_MAX - /// the array of y-values that this DataSet represents. - public var yVals: [ChartDataEntry] + /// maximum x-value in the value array + internal var _xMax: Double = -DBL_MAX + + /// minimum x-value in the value array + internal var _xMin: Double = DBL_MAX + + /// * + /// - note: Calls `notifyDataSetChanged()` after setting a new value. + /// - returns: The array of y-values that this DataSet represents. + public var values: [ChartDataEntry] { get { - return _yVals + return _values } set { - _yVals = newValue + _values = newValue notifyDataSetChanged() } } @@ -81,56 +91,47 @@ public class ChartDataSet: ChartBaseDataSet /// Use this method to tell the data set that the underlying data has changed public override func notifyDataSetChanged() { - calcMinMax(start: _lastStart, end: _lastEnd) + calcMinMax() } - public override func calcMinMax(start start: Int, end: Int) + public override func calcMinMax() { - let yValCount = _yVals.count - - if yValCount == 0 + if _values.count == 0 { return } - var endValue : Int + _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - if end == 0 || end >= yValCount + for e in _values { - endValue = yValCount - 1 + calcMinMax(entry: e) } - else + } + + /// Updates the min and max x and y value of this DataSet based on the given Entry. + /// + /// - parameter e: + internal func calcMinMax(entry e: ChartDataEntry) + { + if e.y < _yMin { - endValue = end + _yMin = e.y } - - _lastStart = start - _lastEnd = endValue - - _yMin = DBL_MAX - _yMax = -DBL_MAX - - for i in start.stride(through: endValue, by: 1) + if e.y > _yMax { - let e = _yVals[i] - - if (!e.value.isNaN) - { - if (e.value < _yMin) - { - _yMin = e.value - } - if (e.value > _yMax) - { - _yMax = e.value - } - } + _yMax = e.y } - - if (_yMin == DBL_MAX) + if e.x < _xMin + { + _xMin = e.x + } + if e.x > _xMax { - _yMin = 0.0 - _yMax = 0.0 + _xMax = e.x } } @@ -140,87 +141,95 @@ public class ChartDataSet: ChartBaseDataSet /// - returns: the maximum y-value this DataSet holds public override var yMax: Double { return _yMax } + /// - returns: the minimum x-value this DataSet holds + public override var xMin: Double { return _xMin } + + /// - returns: the maximum x-value this DataSet holds + public override var xMax: Double { return _xMax } + /// - returns: the number of y-values this DataSet represents - public override var entryCount: Int { return _yVals?.count ?? 0 } + public override var entryCount: Int { return _values?.count ?? 0 } /// - returns: the value of the Entry object at the given xIndex. Returns NaN if no value is at the given x-index. - public override func yValForXIndex(x: Int) -> Double + public override func yValueForXValue(x: Double) -> Double { - let e = self.entryForXIndex(x) + let e = self.entryForXPos(x) - if (e !== nil && e!.xIndex == x) { return e!.value } + if (e !== nil && e!.x == x) { return e!.y } else { return Double.NaN } } /// - returns: all of the y values of the Entry objects at the given xIndex. Returns NaN if no value is at the given x-index. - public override func yValsForXIndex(x: Int) -> [Double] + public override func yValuesForXValue(x: Double) -> [Double] { - let entries = self.entriesForXIndex(x) + let entries = self.entriesForXPos(x) var yVals = [Double]() for e in entries { - yVals.append(e.value) + yVals.append(e.y) } return yVals } - /// - returns: the entry object found at the given index (not x-index!) + /// - returns: the entry object found at the given index (not x-value!) /// - throws: out of bounds /// if `i` is out of bounds, it may throw an out-of-bounds exception public override func entryForIndex(i: Int) -> ChartDataEntry? { - return _yVals[i] + return _values[i] } - /// - returns: the first Entry object found at the given xIndex with binary search. - /// If the no Entry at the specifed x-index is found, this method returns the Entry at the closest x-index. - /// nil if no Entry object at that index. - public override func entryForXIndex(x: Int, rounding: ChartDataSetRounding) -> ChartDataEntry? + /// - returns: the first Entry object found at the given x-pos with binary search. + /// If the no Entry at the specifed x-pos is found, this method returns the Entry at the closest x-pox. + /// nil if no Entry object at that x-pos. + /// - parameter x: the x-pos + /// - parameter rounding: determine whether to round up/down/closest if there is no Entry matching the provided x-pos + public override func entryForXPos(x: Double, rounding: ChartDataSetRounding) -> ChartDataEntry? { - let index = self.entryIndex(xIndex: x, rounding: rounding) + let index = self.entryIndex(x: x, rounding: rounding) if (index > -1) { - return _yVals[index] + return _values[index] } return nil } - /// - returns: the first Entry object found at the given xIndex with binary search. - /// If the no Entry at the specifed x-index is found, this method returns the Entry at the closest x-index. - /// nil if no Entry object at that index. - public override func entryForXIndex(x: Int) -> ChartDataEntry? + /// - returns: the first Entry object found at the given x-pos with binary search. + /// If the no Entry at the specifed x-pos is found, this method returns the Entry at the closest x-pos. + /// nil if no Entry object at that x-pos. + public override func entryForXPos(x: Double) -> ChartDataEntry? { - return entryForXIndex(x, rounding: .Closest) + return entryForXPos(x, rounding: .Closest) } /// - returns: all Entry objects found at the given xIndex with binary search. /// An empty array if no Entry object at that index. - public override func entriesForXIndex(x: Int) -> [ChartDataEntry] + public override func entriesForXPos(x: Double) -> [ChartDataEntry] { var entries = [ChartDataEntry]() var low = 0 - var high = _yVals.count - 1 + var high = _values.count - 1 while (low <= high) { var m = (high + low) / 2 - var entry = _yVals[m] + var entry = _values[m] - if (x == entry.xIndex) + if (x == entry.x) { - while (m > 0 && _yVals[m - 1].xIndex == x) + while (m > 0 && _values[m - 1].x == x) { m -= 1 } - high = _yVals.count + high = _values.count while (m < high) { - entry = _yVals[m] - if (entry.xIndex == x) + entry = _values[m] + if (entry.x == x) { entries.append(entry) } @@ -236,7 +245,7 @@ public class ChartDataSet: ChartBaseDataSet } else { - if (x > _yVals[m].xIndex) + if (x > entry.x) { low = m + 1 } @@ -254,20 +263,20 @@ public class ChartDataSet: ChartBaseDataSet /// /// - parameter x: x-index of the entry to search for /// - parameter rounding: x-index of the entry to search for - public override func entryIndex(xIndex x: Int, rounding: ChartDataSetRounding) -> Int + public override func entryIndex(x x: Double, rounding: ChartDataSetRounding) -> Int { var low = 0 - var high = _yVals.count - 1 + var high = _values.count - 1 var closest = -1 while (low <= high) { var m = (high + low) / 2 - let entry = _yVals[m] + let entry = _values[m] - if (x == entry.xIndex) + if (x == entry.x) { - while (m > 0 && _yVals[m - 1].xIndex == x) + while (m > 0 && _values[m - 1].x == x) { m -= 1 } @@ -275,7 +284,7 @@ public class ChartDataSet: ChartBaseDataSet return m } - if (x > entry.xIndex) + if (x > entry.x) { low = m + 1 } @@ -291,15 +300,15 @@ public class ChartDataSet: ChartBaseDataSet { if rounding == .Up { - let closestXIndex = _yVals[closest].xIndex - if closestXIndex < x && closest < _yVals.count - 1 + let closestXIndex = _values[closest].x + if closestXIndex < x && closest < _values.count - 1 { closest = closest + 1 } } else if rounding == .Down { - let closestXIndex = _yVals[closest].xIndex + let closestXIndex = _values[closest].x if closestXIndex > x && closest > 0 { closest = closest - 1 @@ -315,9 +324,9 @@ public class ChartDataSet: ChartBaseDataSet /// - parameter e: the entry to search for public override func entryIndex(entry e: ChartDataEntry) -> Int { - for i in 0 ..< _yVals.count + for i in 0 ..< _values.count { - if _yVals[i] === e + if _values[i] === e { return i } @@ -333,31 +342,14 @@ public class ChartDataSet: ChartBaseDataSet /// - returns: true public override func addEntry(e: ChartDataEntry) -> Bool { - let val = e.value - - if (_yVals == nil) + if (_values == nil) { - _yVals = [ChartDataEntry]() + _values = [ChartDataEntry]() } - if (_yVals.count == 0) - { - _yMax = val - _yMin = val - } - else - { - if (_yMax < val) - { - _yMax = val - } - if (_yMin > val) - { - _yMin = val - } - } + calcMinMax(entry: e) - _yVals.append(e) + _values.append(e) return true } @@ -369,43 +361,26 @@ public class ChartDataSet: ChartBaseDataSet /// - returns: true public override func addEntryOrdered(e: ChartDataEntry) -> Bool { - let val = e.value - - if (_yVals == nil) + if (_values == nil) { - _yVals = [ChartDataEntry]() + _values = [ChartDataEntry]() } - if (_yVals.count == 0) - { - _yMax = val - _yMin = val - } - else - { - if (_yMax < val) - { - _yMax = val - } - if (_yMin > val) - { - _yMin = val - } - } + calcMinMax(entry: e) - if _yVals.last?.xIndex > e.xIndex + if _values.last?.x > e.x { - var closestIndex = entryIndex(xIndex: e.xIndex, rounding: .Closest) - if _yVals[closestIndex].xIndex < e.xIndex + var closestIndex = entryIndex(x: e.x, rounding: .Up) + while _values[closestIndex].x < e.x { closestIndex += 1 } - _yVals.insert(e, atIndex: closestIndex) - - return true + _values.insert(e, atIndex: closestIndex) + } + else + { + _values.append(e) } - - _yVals.append(e) return true } @@ -418,11 +393,11 @@ public class ChartDataSet: ChartBaseDataSet { var removed = false - for i in 0 ..< _yVals.count + for i in 0 ..< _values.count { - if (_yVals[i] === entry) + if (_values[i] === entry) { - _yVals.removeAtIndex(i) + _values.removeAtIndex(i) removed = true break } @@ -430,7 +405,7 @@ public class ChartDataSet: ChartBaseDataSet if (removed) { - calcMinMax(start: _lastStart, end: _lastEnd) + calcMinMax() } return removed @@ -441,13 +416,13 @@ public class ChartDataSet: ChartBaseDataSet /// - returns: true if successful, false if not. public override func removeFirst() -> Bool { - let entry: ChartDataEntry? = _yVals.isEmpty ? nil : _yVals.removeFirst() + let entry: ChartDataEntry? = _values.isEmpty ? nil : _values.removeFirst() let removed = entry != nil if (removed) { - calcMinMax(start: _lastStart, end: _lastEnd) + calcMinMax() } return removed; @@ -458,13 +433,13 @@ public class ChartDataSet: ChartBaseDataSet /// - returns: true if successful, false if not. public override func removeLast() -> Bool { - let entry: ChartDataEntry? = _yVals.isEmpty ? nil : _yVals.removeLast() + let entry: ChartDataEntry? = _values.isEmpty ? nil : _values.removeLast() let removed = entry != nil if (removed) { - calcMinMax(start: _lastStart, end: _lastEnd) + calcMinMax() } return removed; @@ -474,7 +449,7 @@ public class ChartDataSet: ChartBaseDataSet /// - returns: true if contains the entry, false if not. public override func contains(e: ChartDataEntry) -> Bool { - for entry in _yVals + for entry in _values { if (entry.isEqual(e)) { @@ -488,16 +463,11 @@ public class ChartDataSet: ChartBaseDataSet /// Removes all values from this DataSet and recalculates min and max value. public override func clear() { - _yVals.removeAll(keepCapacity: true) - _lastStart = 0 - _lastEnd = 0 + _values.removeAll(keepCapacity: true) notifyDataSetChanged() } // MARK: - Data functions and accessors - - /// - returns: the number of entries this DataSet holds. - public var valueCount: Int { return _yVals.count } // MARK: - NSCopying @@ -505,11 +475,9 @@ public class ChartDataSet: ChartBaseDataSet { let copy = super.copyWithZone(zone) as! ChartDataSet - copy._yVals = _yVals + copy._values = _values copy._yMax = _yMax copy._yMin = _yMin - copy._lastStart = _lastStart - copy._lastEnd = _lastEnd return copy } diff --git a/Charts/Classes/Data/Implementations/Standard/CombinedChartData.swift b/Charts/Classes/Data/Implementations/Standard/CombinedChartData.swift index de809172ce..187df7599d 100644 --- a/Charts/Classes/Data/Implementations/Standard/CombinedChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/CombinedChartData.swift @@ -26,14 +26,9 @@ public class CombinedChartData: BarLineScatterCandleBubbleChartData super.init() } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) - } - - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) - { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } public var lineData: LineChartData! @@ -50,12 +45,7 @@ public class CombinedChartData: BarLineScatterCandleBubbleChartData _dataSets.append(dataSet) } - checkIsLegal(newValue.dataSets) - - calcMinMax(start: _lastStart, end: _lastEnd) - calcYValueCount() - - calcXValAverageLength() + calcMinMax() } } @@ -73,12 +63,7 @@ public class CombinedChartData: BarLineScatterCandleBubbleChartData _dataSets.append(dataSet) } - checkIsLegal(newValue.dataSets) - - calcMinMax(start: _lastStart, end: _lastEnd) - calcYValueCount() - - calcXValAverageLength() + calcMinMax() } } @@ -96,12 +81,7 @@ public class CombinedChartData: BarLineScatterCandleBubbleChartData _dataSets.append(dataSet) } - checkIsLegal(newValue.dataSets) - - calcMinMax(start: _lastStart, end: _lastEnd) - calcYValueCount() - - calcXValAverageLength() + calcMinMax() } } @@ -119,12 +99,7 @@ public class CombinedChartData: BarLineScatterCandleBubbleChartData _dataSets.append(dataSet) } - checkIsLegal(newValue.dataSets) - - calcMinMax(start: _lastStart, end: _lastEnd) - calcYValueCount() - - calcXValAverageLength() + calcMinMax() } } @@ -142,12 +117,7 @@ public class CombinedChartData: BarLineScatterCandleBubbleChartData _dataSets.append(dataSet) } - checkIsLegal(newValue.dataSets) - - calcMinMax(start: _lastStart, end: _lastEnd) - calcYValueCount() - - calcXValAverageLength() + calcMinMax() } } @@ -229,11 +199,11 @@ public class CombinedChartData: BarLineScatterCandleBubbleChartData else { // The value of the highlighted entry could be NaN - if we are not interested in highlighting a specific value. - - let entries = data.getDataSetByIndex(highlight.dataSetIndex).entriesForXIndex(highlight.xIndex) + // FIXME: Implement on Android + let entries = data.getDataSetByIndex(highlight.dataSetIndex).entriesForXPos(highlight.x) for e in entries { - if e.value == highlight.value || isnan(highlight.value) + if e.y == highlight.y || isnan(highlight.y) { return e } diff --git a/Charts/Classes/Data/Implementations/Standard/LineChartData.swift b/Charts/Classes/Data/Implementations/Standard/LineChartData.swift index 4e6ff973f9..47bf871e70 100644 --- a/Charts/Classes/Data/Implementations/Standard/LineChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/LineChartData.swift @@ -21,13 +21,8 @@ public class LineChartData: ChartData super.init() } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) - } - - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) - { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } } diff --git a/Charts/Classes/Data/Implementations/Standard/LineChartDataSet.swift b/Charts/Classes/Data/Implementations/Standard/LineChartDataSet.swift index 5bdcf884eb..260002b5f1 100644 --- a/Charts/Classes/Data/Implementations/Standard/LineChartDataSet.swift +++ b/Charts/Classes/Data/Implementations/Standard/LineChartDataSet.swift @@ -38,9 +38,9 @@ public class LineChartDataSet: LineRadarChartDataSet, ILineChartDataSet initialize() } - public override init(yVals: [ChartDataEntry]?, label: String?) + public override init(values: [ChartDataEntry]?, label: String?) { - super.init(yVals: yVals, label: label) + super.init(values: values, label: label) initialize() } diff --git a/Charts/Classes/Data/Implementations/Standard/PieChartData.swift b/Charts/Classes/Data/Implementations/Standard/PieChartData.swift index 45ed900d7f..28fcb0715b 100644 --- a/Charts/Classes/Data/Implementations/Standard/PieChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/PieChartData.swift @@ -20,14 +20,9 @@ public class PieChartData: ChartData super.init() } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) - } - - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) - { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } var dataSet: IPieChartDataSet? @@ -83,12 +78,7 @@ public class PieChartData: ChartData } public override func addDataSet(d: IChartDataSet!) - { - if (_dataSets == nil) - { - return - } - + { super.addDataSet(d) } @@ -98,7 +88,7 @@ public class PieChartData: ChartData /// - returns: true if a DataSet was removed, false if no DataSet could be removed. public override func removeDataSetByIndex(index: Int) -> Bool { - if (_dataSets == nil || index >= _dataSets.count || index < 0) + if index >= _dataSets.count || index < 0 { return false } @@ -115,7 +105,7 @@ public class PieChartData: ChartData for i in 0.. AnyObject + { + let copy = super.copyWithZone(zone) as! PieChartDataEntry + copy.label = label + return copy + } +} \ No newline at end of file diff --git a/Charts/Classes/Data/Implementations/Standard/PieChartDataSet.swift b/Charts/Classes/Data/Implementations/Standard/PieChartDataSet.swift index 7c563cd6b1..8004743b16 100644 --- a/Charts/Classes/Data/Implementations/Standard/PieChartDataSet.swift +++ b/Charts/Classes/Data/Implementations/Standard/PieChartDataSet.swift @@ -36,9 +36,9 @@ public class PieChartDataSet: ChartDataSet, IPieChartDataSet initialize() } - public override init(yVals: [ChartDataEntry]?, label: String?) + public override init(values: [ChartDataEntry]?, label: String?) { - super.init(yVals: yVals, label: label) + super.init(values: values, label: label) initialize() } diff --git a/Charts/Classes/Data/Implementations/Standard/RadarChartData.swift b/Charts/Classes/Data/Implementations/Standard/RadarChartData.swift index 73f9ca8f6f..7509147c37 100644 --- a/Charts/Classes/Data/Implementations/Standard/RadarChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/RadarChartData.swift @@ -22,18 +22,22 @@ public class RadarChartData: ChartData public var highlightLineDashPhase = CGFloat(0.0) public var highlightLineDashLengths: [CGFloat]? - public override init() + /// Sets labels that should be drawn around the RadarChart at the end of each web line. + public var labels = [String]() + + /// Sets the labels that should be drawn around the RadarChart at the end of each web line. + public func setLabels(labels: String...) { - super.init() + self.labels = labels } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init() { - super.init(xVals: xVals, dataSets: dataSets) + super.init() } - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } } diff --git a/Charts/Classes/Data/Implementations/Standard/RadarChartDataSet.swift b/Charts/Classes/Data/Implementations/Standard/RadarChartDataSet.swift index ebbde701b1..a1a3cfc9df 100644 --- a/Charts/Classes/Data/Implementations/Standard/RadarChartDataSet.swift +++ b/Charts/Classes/Data/Implementations/Standard/RadarChartDataSet.swift @@ -29,9 +29,9 @@ public class RadarChartDataSet: LineRadarChartDataSet, IRadarChartDataSet initialize() } - public override init(yVals: [ChartDataEntry]?, label: String?) + public override init(values: [ChartDataEntry]?, label: String?) { - super.init(yVals: yVals, label: label) + super.init(values: values, label: label) initialize() } diff --git a/Charts/Classes/Data/Implementations/Standard/ScatterChartData.swift b/Charts/Classes/Data/Implementations/Standard/ScatterChartData.swift index f31b13bb6e..2e78ed5c42 100644 --- a/Charts/Classes/Data/Implementations/Standard/ScatterChartData.swift +++ b/Charts/Classes/Data/Implementations/Standard/ScatterChartData.swift @@ -21,14 +21,9 @@ public class ScatterChartData: BarLineScatterCandleBubbleChartData super.init() } - public override init(xVals: [String?]?, dataSets: [IChartDataSet]?) + public override init(dataSets: [IChartDataSet]?) { - super.init(xVals: xVals, dataSets: dataSets) - } - - public override init(xVals: [NSObject]?, dataSets: [IChartDataSet]?) - { - super.init(xVals: xVals, dataSets: dataSets) + super.init(dataSets: dataSets) } /// - returns: the maximum shape-size across all DataSets. diff --git a/Charts/Classes/Data/Interfaces/IBarChartDataSet.swift b/Charts/Classes/Data/Interfaces/IBarChartDataSet.swift index edf3ca427d..ba54ffbc58 100644 --- a/Charts/Classes/Data/Interfaces/IBarChartDataSet.swift +++ b/Charts/Classes/Data/Interfaces/IBarChartDataSet.swift @@ -21,9 +21,6 @@ public protocol IBarChartDataSet: IBarLineScatterCandleBubbleChartDataSet // MARK: - Styling functions and accessors - /// space indicator between the bars in percentage of the whole width of one value (0.15 == 15% of bar width) - var barSpace: CGFloat { get set } - /// - returns: true if this DataSet is stacked (stacksize > 1) or not. var isStacked: Bool { get } diff --git a/Charts/Classes/Data/Interfaces/IBubbleChartDataSet.swift b/Charts/Classes/Data/Interfaces/IBubbleChartDataSet.swift index 4d43a900bb..4f84ac6a73 100644 --- a/Charts/Classes/Data/Interfaces/IBubbleChartDataSet.swift +++ b/Charts/Classes/Data/Interfaces/IBubbleChartDataSet.swift @@ -19,8 +19,6 @@ public protocol IBubbleChartDataSet: IBarLineScatterCandleBubbleChartDataSet { // MARK: - Data functions and accessors - var xMin: Double { get } - var xMax: Double { get } var maxSize: CGFloat { get } var isNormalizeSizeEnabled: Bool { get } diff --git a/Charts/Classes/Data/Interfaces/IChartDataSet.swift b/Charts/Classes/Data/Interfaces/IChartDataSet.swift index b7f43f7d56..565d88e4a6 100644 --- a/Charts/Classes/Data/Interfaces/IChartDataSet.swift +++ b/Charts/Classes/Data/Interfaces/IChartDataSet.swift @@ -21,12 +21,8 @@ public protocol IChartDataSet /// Use this method to tell the data set that the underlying data has changed func notifyDataSetChanged() - /// This is an opportunity to calculate the minimum and maximum y value in the specified range. - /// If your data is in an array, you might loop over them to find the values. - /// If your data is in a database, you might query for the min/max and put them in variables. - /// - parameter start: the index of the first y entry to calculate - /// - parameter end: the index of the last y entry to calculate - func calcMinMax(start start: Int, end: Int) + /// Calculates the minimum and maximum x and y values (_xMin, _xMax, _yMin, _yMax). + func calcMinMax() /// - returns: the minimum y-value this DataSet holds var yMin: Double { get } @@ -34,39 +30,47 @@ public protocol IChartDataSet /// - returns: the maximum y-value this DataSet holds var yMax: Double { get } + /// - returns: the minimum x-value this DataSet holds + var xMin: Double { get } + + /// - returns: the maximum x-value this DataSet holds + var xMax: Double { get } + /// - returns: the number of y-values this DataSet represents var entryCount: Int { get } - /// - returns: the value of the Entry object at the given xIndex. Returns NaN if no value is at the given x-index. - func yValForXIndex(x: Int) -> Double + /// - returns: the value of the Entry object at the given x-pos. Returns NaN if no value is at the given x-pos. + func yValueForXValue(x: Double) -> Double - /// - returns: all of the y values of the Entry objects at the given xIndex. Returns NaN if no value is at the given x-index. - func yValsForXIndex(x: Int) -> [Double] + /// - returns: all of the y values of the Entry objects at the given x-pos. Returns NaN if no value is at the given x-pos. + func yValuesForXValue(x: Double) -> [Double] - /// - returns: the entry object found at the given index (not x-index!) + /// - returns: the entry object found at the given index (not x-value!) /// - throws: out of bounds /// if `i` is out of bounds, it may throw an out-of-bounds exception func entryForIndex(i: Int) -> ChartDataEntry? - /// - returns: the first Entry object found at the given xIndex with binary search. - /// If the no Entry at the specifed x-index is found, this method returns the Entry at the closest x-index. - /// nil if no Entry object at that index. - func entryForXIndex(x: Int, rounding: ChartDataSetRounding) -> ChartDataEntry? + /// - returns: the first Entry object found at the given x-pos with binary search. + /// If the no Entry at the specifed x-pos is found, this method returns the Entry at the closest x-pox. + /// nil if no Entry object at that x-pos. + /// - parameter x: the x-pos + /// - parameter rounding: determine whether to round up/down/closest if there is no Entry matching the provided x-pos + func entryForXPos(x: Double, rounding: ChartDataSetRounding) -> ChartDataEntry? - /// - returns: the first Entry object found at the given xIndex with binary search. - /// If the no Entry at the specifed x-index is found, this method returns the Entry at the closest x-index. - /// nil if no Entry object at that index. - func entryForXIndex(x: Int) -> ChartDataEntry? + /// - returns: the first Entry object found at the given x-pos with binary search. + /// If the no Entry at the specifed x-pos is found, this method returns the Entry at the closest x-pos. + /// nil if no Entry object at that x-pos. + func entryForXPos(x: Double) -> ChartDataEntry? - /// - returns: all Entry objects found at the given xIndex with binary search. - /// An empty array if no Entry object at that index. - func entriesForXIndex(x: Int) -> [ChartDataEntry] + /// - returns: all Entry objects found at the given x-pos with binary search. + /// An empty array if no Entry object at that x-pos. + func entriesForXPos(x: Double) -> [ChartDataEntry] /// - returns: the array-index of the specified entry /// - /// - parameter x: x-index of the entry to search for - /// - parameter rounding: x-index of the entry to search for - func entryIndex(xIndex x: Int, rounding: ChartDataSetRounding) -> Int + /// - parameter x: x-pos of the entry to search for + /// - parameter rounding: x-pos of the entry to search for + func entryIndex(x x: Double, rounding: ChartDataSetRounding) -> Int /// - returns: the array-index of the specified entry /// @@ -83,7 +87,7 @@ public protocol IChartDataSet func addEntry(e: ChartDataEntry) -> Bool /// Adds an Entry to the DataSet dynamically. - /// Entries are added to their appropriate index respective to it's x-index. + /// Entries are added to their appropriate index in the values array respective to their x-position. /// This will also recalculate the current minimum and maximum values of the DataSet and the value-sum. /// /// *optional feature, can return false if not implemented* @@ -101,13 +105,21 @@ public protocol IChartDataSet /// - returns: true if the entry was removed successfully, false if the entry does not exist or if this feature is not supported func removeEntry(entry: ChartDataEntry) -> Bool - /// Removes the Entry object that has the given xIndex from the DataSet. + /// Removes the Entry object at the given index in the values array from the DataSet. + /// + /// *optional feature, can return false if not implemented* + /// + /// - parameter index: the index of the entry to remove + /// - returns: true if the entry was removed successfully, false if the entry does not exist or if this feature is not supported + func removeEntry(index index: Int) -> Bool + + /// Removes the Entry object closest to the given x-pos from the DataSet. /// /// *optional feature, can return false if not implemented* /// - /// - parameter xIndex: the xIndex to remove + /// - parameter x: the x-pos to remove /// - returns: true if the entry was removed successfully, false if the entry does not exist or if this feature is not supported - func removeEntry(xIndex xIndex: Int) -> Bool + func removeEntry(x x: Double) -> Bool /// Removes the first Entry (at index 0) of this DataSet from the entries array. /// diff --git a/Charts/Classes/Filters/ChartDataApproximator.swift b/Charts/Classes/Filters/ChartDataApproximator.swift new file mode 100644 index 0000000000..784a26dfda --- /dev/null +++ b/Charts/Classes/Filters/ChartDataApproximator.swift @@ -0,0 +1,122 @@ +// +// ChartDataApproximator.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/Charts +// + +import Foundation + +@objc +public class ChartDataApproximator: NSObject +{ + /// uses the douglas peuker algorithm to reduce the given arraylist of entries + public class func reduceWithDouglasPeuker(points: [CGPoint], tolerance: CGFloat) -> [CGPoint] + { + // if a shape has 2 or less points it cannot be reduced + if tolerance <= 0 || points.count < 3 + { + return points + } + + var keep = [Bool](count: points.count, repeatedValue: false) + + // first and last always stay + keep[0] = true + keep[points.count - 1] = true + + // first and last entry are entry point to recursion + reduceWithDouglasPeuker(points, + tolerance: tolerance, + start: 0, + end: points.count - 1, + keep: &keep) + + // create a new array with series, only take the kept ones + var reducedEntries = [CGPoint]() + for i in 0 ..< points.count + { + if keep[i] + { + reducedEntries.append(points[i]) + } + } + + return reducedEntries + } + + /// apply the Douglas-Peucker-Reduction to an array of `CGPoint`s with a given tolerance + /// + /// - parameter points: + /// - parameter tolerance: + /// - parameter start: + /// - parameter end: + public class func reduceWithDouglasPeuker( + points: [CGPoint], + tolerance: CGFloat, + start: Int, + end: Int, + inout keep: [Bool]) + { + if (end <= start + 1) + { + // recursion finished + return + } + + var greatestIndex = Int(0) + var greatestDistance = CGFloat(0.0) + + let line = Line(pt1: points[start], pt2: points[end]) + + for i in start + 1 ..< end + { + let distance = line.distance(toPoint: points[i]) + + if distance > greatestDistance + { + greatestDistance = distance + greatestIndex = i + } + } + + if greatestDistance > tolerance + { + // keep max dist point + keep[greatestIndex] = true + + // recursive call + reduceWithDouglasPeuker(points, tolerance: tolerance, start: start, end: greatestIndex, keep: &keep) + reduceWithDouglasPeuker(points, tolerance: tolerance, start: greatestIndex, end: end, keep: &keep) + } // else don't keep the point... + } + + private class Line + { + var sxey: CGFloat + var exsy: CGFloat + + var dx: CGFloat + var dy: CGFloat + + var length: CGFloat + + init(pt1: CGPoint, pt2: CGPoint) + { + dx = pt1.x - pt2.x + dy = pt1.y - pt2.y + sxey = pt1.x * pt2.y + exsy = pt2.x * pt1.y + length = sqrt(dx * dx + dy * dy) + } + + func distance(toPoint pt: CGPoint) -> CGFloat + { + return abs(dy * pt.x - dx * pt.y + sxey - exsy) / length + } + } +} \ No newline at end of file diff --git a/Charts/Classes/Filters/ChartDataApproximatorFilter.swift b/Charts/Classes/Filters/ChartDataApproximatorFilter.swift deleted file mode 100644 index d8da9361c3..0000000000 --- a/Charts/Classes/Filters/ChartDataApproximatorFilter.swift +++ /dev/null @@ -1,214 +0,0 @@ -// -// ChartDataApproximator.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -public class ChartDataApproximatorFilter: ChartDataBaseFilter -{ - @objc - public enum ApproximatorType: Int - { - case None - case RamerDouglasPeucker - } - - /// the type of filtering algorithm to use - public var type = ApproximatorType.None - - /// the tolerance to be filtered with - /// When using the Douglas-Peucker-Algorithm, the tolerance is an angle in degrees, that will trigger the filtering - public var tolerance = Double(0.0) - - public var scaleRatio = Double(1.0) - public var deltaRatio = Double(1.0) - - public override init() - { - super.init() - } - - /// Initializes the approximator with the given type and tolerance. - /// If toleranec <= 0, no filtering will be done. - public init(type: ApproximatorType, tolerance: Double) - { - super.init() - - setup(type, tolerance: tolerance) - } - - /// Sets type and tolerance. - /// If tolerance <= 0, no filtering will be done. - public func setup(type: ApproximatorType, tolerance: Double) - { - self.type = type - self.tolerance = tolerance - } - - /// Sets the ratios for x- and y-axis, as well as the ratio of the scale levels - public func setRatios(deltaRatio: Double, scaleRatio: Double) - { - self.deltaRatio = deltaRatio - self.scaleRatio = scaleRatio - } - - /// Filters according to type. Uses the pre set set tolerance - /// - /// - parameter points: the points to filter - public override func filter(points: [ChartDataEntry]) -> [ChartDataEntry] - { - return filter(points, tolerance: tolerance) - } - - /// Filters according to type. - /// - /// - parameter points: the points to filter - /// - parameter tolerance: the angle in degrees that will trigger the filtering - public func filter(points: [ChartDataEntry], tolerance: Double) -> [ChartDataEntry] - { - if (tolerance <= 0) - { - return points - } - - switch (type) - { - case .RamerDouglasPeucker: - return reduceWithDouglasPeuker(points, epsilon: tolerance) - case .None: - return points - } - } - - /// uses the douglas peuker algorithm to reduce the given arraylist of entries - private func reduceWithDouglasPeuker(entries: [ChartDataEntry], epsilon: Double) -> [ChartDataEntry] - { - // if a shape has 2 or less points it cannot be reduced - if (epsilon <= 0 || entries.count < 3) - { - return entries - } - - var keep = [Bool](count: entries.count, repeatedValue: false) - - // first and last always stay - keep[0] = true - keep[entries.count - 1] = true - - // first and last entry are entry point to recursion - algorithmDouglasPeucker(entries, epsilon: epsilon, start: 0, end: entries.count - 1, keep: &keep) - - // create a new array with series, only take the kept ones - var reducedEntries = [ChartDataEntry]() - for i in 0 ..< entries.count - { - if (keep[i]) - { - let curEntry = entries[i] - reducedEntries.append(ChartDataEntry(value: curEntry.value, xIndex: curEntry.xIndex)) - } - } - - return reducedEntries - } - - /// apply the Douglas-Peucker-Reduction to an ArrayList of Entry with a given epsilon (tolerance) - /// - /// - parameter entries: - /// - parameter epsilon: as y-value - /// - parameter start: - /// - parameter end: - private func algorithmDouglasPeucker(entries: [ChartDataEntry], epsilon: Double, start: Int, end: Int, inout keep: [Bool]) - { - if (end <= start + 1) - { - // recursion finished - return - } - - // find the greatest distance between start and endpoint - var maxDistIndex = Int(0) - var distMax = Double(0.0) - - let firstEntry = entries[start] - let lastEntry = entries[end] - - for i in start + 1 ..< end - { - let dist = calcAngleBetweenLines(firstEntry, end1: lastEntry, start2: firstEntry, end2: entries[i]) - - // keep the point with the greatest distance - if (dist > distMax) - { - distMax = dist - maxDistIndex = i - } - } - - if (distMax > epsilon) - { - // keep max dist point - keep[maxDistIndex] = true - - // recursive call - algorithmDouglasPeucker(entries, epsilon: epsilon, start: start, end: maxDistIndex, keep: &keep) - algorithmDouglasPeucker(entries, epsilon: epsilon, start: maxDistIndex, end: end, keep: &keep) - } // else don't keep the point... - } - - /// calculate the distance between a line between two entries and an entry (point) - /// - /// - parameter startEntry: line startpoint - /// - parameter endEntry: line endpoint - /// - parameter entryPoint: the point to which the distance is measured from the line - private func calcPointToLineDistance(startEntry: ChartDataEntry, endEntry: ChartDataEntry, entryPoint: ChartDataEntry) -> Double - { - let xDiffEndStart = Double(endEntry.xIndex) - Double(startEntry.xIndex) - let xDiffEntryStart = Double(entryPoint.xIndex) - Double(startEntry.xIndex) - - let normalLength = sqrt((xDiffEndStart) - * (xDiffEndStart) - + (endEntry.value - startEntry.value) - * (endEntry.value - startEntry.value)) - - return Double(fabs((xDiffEntryStart) - * (endEntry.value - startEntry.value) - - (entryPoint.value - startEntry.value) - * (xDiffEndStart))) / Double(normalLength) - } - - /// Calculates the angle between two given lines. The provided entries mark the starting and end points of the lines. - private func calcAngleBetweenLines(start1: ChartDataEntry, end1: ChartDataEntry, start2: ChartDataEntry, end2: ChartDataEntry) -> Double - { - let angle1 = calcAngleWithRatios(start1, p2: end1) - let angle2 = calcAngleWithRatios(start2, p2: end2) - - return fabs(angle1 - angle2) - } - - /// calculates the angle between two entries (points) in the chart taking ratios into consideration - private func calcAngleWithRatios(p1: ChartDataEntry, p2: ChartDataEntry) -> Double - { - let dx = Double(p2.xIndex) * Double(deltaRatio) - Double(p1.xIndex) * Double(deltaRatio) - let dy = p2.value * scaleRatio - p1.value * scaleRatio - return atan2(Double(dy), dx) * ChartUtils.Math.RAD2DEG - } - - // calculates the angle between two entries (points) in the chart - private func calcAngle(p1: ChartDataEntry, p2: ChartDataEntry) -> Double - { - let dx = p2.xIndex - p1.xIndex - let dy = p2.value - p1.value - return atan2(Double(dy), Double(dx)) * ChartUtils.Math.RAD2DEG - } -} \ No newline at end of file diff --git a/Charts/Classes/Filters/ChartDataBaseFilter.swift b/Charts/Classes/Filters/ChartDataBaseFilter.swift deleted file mode 100644 index e16517c884..0000000000 --- a/Charts/Classes/Filters/ChartDataBaseFilter.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// ChartDataFilter.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -public class ChartDataBaseFilter: NSObject -{ - public override init() - { - super.init() - } - - public func filter(points: [ChartDataEntry]) -> [ChartDataEntry] - { - fatalError("filter() cannot be called on ChartDataBaseFilter") - } -} \ No newline at end of file diff --git a/Charts/Classes/Formatters/ChartAxisValueFormatter.swift b/Charts/Classes/Formatters/ChartAxisValueFormatter.swift new file mode 100644 index 0000000000..8c5800e2b4 --- /dev/null +++ b/Charts/Classes/Formatters/ChartAxisValueFormatter.swift @@ -0,0 +1,30 @@ +// +// ChartAxisValueFormatter.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/Charts +// + +import Foundation + +/// An interface for providing custom axis Strings. +@objc +public protocol ChartAxisValueFormatter : NSObjectProtocol +{ + + /// Called when a value from an axis is formatted before being drawn. + /// + /// For performance reasons, avoid excessive calculations and memory allocations inside this method. + /// + /// - returns: the customized label that is drawn on the x-axis. + /// - parameter value: the value that is currently being drawn + /// - parameter axis: the axis that the value belongs to + /// + func stringForValue(value: Double, + axis: ChartAxisBase) -> String + +} \ No newline at end of file diff --git a/Charts/Classes/Formatters/ChartDefaultAxisValueFormatter.swift b/Charts/Classes/Formatters/ChartDefaultAxisValueFormatter.swift new file mode 100644 index 0000000000..bbbbf4e782 --- /dev/null +++ b/Charts/Classes/Formatters/ChartDefaultAxisValueFormatter.swift @@ -0,0 +1,63 @@ +// +// ChartDefaultAxisValueFormatter.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/Charts +// + +import Foundation + +public class ChartDefaultAxisValueFormatter: NSObject, ChartAxisValueFormatter +{ + public var formatter: NSNumberFormatter? + + private var _decimals: Int? + public var decimals: Int? + { + get { return _decimals } + set + { + _decimals = newValue + + if let digits = newValue + { + self.formatter?.minimumFractionDigits = digits + self.formatter?.maximumFractionDigits = digits + } + } + } + + public override init() + { + super.init() + + self.formatter = NSNumberFormatter() + } + + public init(formatter: NSNumberFormatter) + { + super.init() + + self.formatter = formatter + } + + public init(decimals: Int) + { + super.init() + + self.formatter = NSNumberFormatter() + self.formatter?.usesGroupingSeparator = true + self.decimals = decimals + } + + public func stringForValue(value: Double, + axis: ChartAxisBase) -> String + { + return formatter?.stringFromNumber(value) ?? "" + } + +} \ No newline at end of file diff --git a/Charts/Classes/Formatters/ChartDefaultXAxisValueFormatter.swift b/Charts/Classes/Formatters/ChartDefaultXAxisValueFormatter.swift deleted file mode 100644 index 6a35cae9c0..0000000000 --- a/Charts/Classes/Formatters/ChartDefaultXAxisValueFormatter.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// ChartDefaultXAxisValueFormatter.swift -// Charts -// -// Created by Daniel Cohen Gindi on 27/2/15. -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -/// An interface for providing custom x-axis Strings. -public class ChartDefaultXAxisValueFormatter: NSObject, ChartXAxisValueFormatter -{ - - public func stringForXValue(index: Int, original: String, viewPortHandler: ChartViewPortHandler) -> String - { - return original // just return original, no adjustments - } - -} \ No newline at end of file diff --git a/Charts/Classes/Formatters/ChartXAxisValueFormatter.swift b/Charts/Classes/Formatters/ChartXAxisValueFormatter.swift deleted file mode 100644 index a0365051d7..0000000000 --- a/Charts/Classes/Formatters/ChartXAxisValueFormatter.swift +++ /dev/null @@ -1,30 +0,0 @@ -// -// ChartXAxisValueFormatter.swift -// Charts -// -// Created by Daniel Cohen Gindi on 27/2/15. -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -/// An interface for providing custom x-axis Strings. -@objc -public protocol ChartXAxisValueFormatter -{ - - /// For performance reasons, avoid excessive calculations and memory allocations inside this method. - /// - /// - returns: the customized label that is drawn on the x-axis. - /// - parameter index: the x-index that is currently being drawn - /// - parameter original: the original x-axis label to be drawn - /// - parameter viewPortHandler: provides information about the current chart state (scale, translation, ...) - /// - func stringForXValue(index: Int, original: String, viewPortHandler: ChartViewPortHandler) -> String - -} \ No newline at end of file diff --git a/Charts/Classes/Highlight/BarChartHighlighter.swift b/Charts/Classes/Highlight/BarChartHighlighter.swift index 84ee718a70..f95e0e61f2 100644 --- a/Charts/Classes/Highlight/BarChartHighlighter.swift +++ b/Charts/Classes/Highlight/BarChartHighlighter.swift @@ -21,39 +21,22 @@ public class BarChartHighlighter: ChartHighlighter { if let barData = self.chart?.data as? BarChartData { - let xIndex = getXIndex(x) - let baseNoSpace = getBase(x) - let setCount = barData.dataSetCount - var dataSetIndex = Int(baseNoSpace) % setCount + let pos = getValsForTouch(x: x, y: y) - if dataSetIndex < 0 - { - dataSetIndex = 0 - } - else if dataSetIndex >= setCount - { - dataSetIndex = setCount - 1 - } - - guard let selectionDetail = getSelectionDetail(xIndex: xIndex, y: y, dataSetIndex: dataSetIndex) + guard let selectionDetail = getSelectionDetail(xValue: Double(pos.x), x: x, y: y) else { return nil } - if let set = barData.getDataSetByIndex(dataSetIndex) as? IBarChartDataSet + if let set = barData.getDataSetByIndex(selectionDetail.dataSetIndex) as? IBarChartDataSet where set.isStacked { - var pt = CGPoint(x: 0.0, y: y) - - // take any transformer to determine the x-axis value - self.chart?.getTransformer(set.axisDependency).pixelToValue(&pt) - return getStackedHighlight(selectionDetail: selectionDetail, set: set, - xIndex: xIndex, - yValue: Double(pt.y)) + xValue: Double(pos.x), + yValue: Double(pos.y)) } - return ChartHighlight(xIndex: xIndex, - value: selectionDetail.value, + return ChartHighlight(x: selectionDetail.xValue, + y: selectionDetail.yValue, dataIndex: selectionDetail.dataIndex, dataSetIndex: selectionDetail.dataSetIndex, stackIndex: -1) @@ -61,92 +44,45 @@ public class BarChartHighlighter: ChartHighlighter return nil } - public override func getXIndex(x: CGFloat) -> Int + internal override func getDistance(x x: CGFloat, y: CGFloat, selX: CGFloat, selY: CGFloat) -> CGFloat { - if let barData = self.chart?.data as? BarChartData - { - if !barData.isGrouped - { - return super.getXIndex(x) - } - else - { - let baseNoSpace = getBase(x) - - let setCount = barData.dataSetCount - var xIndex = Int(baseNoSpace) / setCount - - let valCount = barData.xValCount - - if xIndex < 0 - { - xIndex = 0 - } - else if xIndex >= valCount - { - xIndex = valCount - 1 - } - - return xIndex - } - } - else - { - return 0 - } - } - - public override func getSelectionDetail(xIndex xIndex: Int, y: CGFloat, dataSetIndex: Int?) -> ChartSelectionDetail? - { - if let barData = self.chart?.data as? BarChartData - { - let dataSetIndex = dataSetIndex ?? 0 - let dataSet = barData.dataSetCount > dataSetIndex ? barData.getDataSetByIndex(dataSetIndex) : nil - let yValue = dataSet.yValForXIndex(xIndex) - - if isnan(yValue) { return nil } - - return ChartSelectionDetail(value: yValue, dataSetIndex: dataSetIndex, dataSet: dataSet) - } - else - { - return nil - } + return abs(x - selX) } /// This method creates the Highlight object that also indicates which value of a stacked BarEntry has been selected. - /// - parameter selectionDetail: the selection detail to work with + /// - parameter selectionDetail: the selection detail to work with looking for stacked values /// - parameter set: /// - parameter xIndex: /// - parameter yValue: /// - returns: public func getStackedHighlight(selectionDetail selectionDetail: ChartSelectionDetail, set: IBarChartDataSet, - xIndex: Int, + xValue: Double, yValue: Double) -> ChartHighlight? { - guard let entry = set.entryForXIndex(xIndex) as? BarChartDataEntry + guard let entry = set.entryForXPos(xValue) as? BarChartDataEntry else { return nil } - if entry.values == nil + // Not stacked + if entry.yValues == nil { - return ChartHighlight(xIndex: xIndex, - value: entry.value, + return ChartHighlight(x: entry.x, + y: entry.y, dataIndex: selectionDetail.dataIndex, dataSetIndex: selectionDetail.dataSetIndex, stackIndex: -1) } - + if let ranges = getRanges(entry: entry) where ranges.count > 0 { let stackIndex = getClosestStackIndex(ranges: ranges, value: yValue) - return ChartHighlight(xIndex: xIndex, - value: entry.positiveSum - entry.negativeSum, - dataIndex: selectionDetail.dataIndex, - dataSetIndex: selectionDetail.dataSetIndex, - stackIndex: stackIndex, - range: ranges[stackIndex]) + return ChartHighlight(x: entry.x, + y: entry.positiveSum - entry.negativeSum, + dataIndex: selectionDetail.dataIndex, + dataSetIndex: selectionDetail.dataSetIndex, + stackIndex: stackIndex, + range: ranges[stackIndex]) } return nil @@ -182,40 +118,12 @@ public class BarChartHighlighter: ChartHighlighter return (value > ranges![length].to) ? length : 0 } - /// Returns the base x-value to the corresponding x-touch value in pixels. - /// - parameter x: - /// - returns: - public func getBase(x: CGFloat) -> Double - { - guard let barData = self.chart?.data as? BarChartData - else { return 0.0 } - - // create an array of the touch-point - var pt = CGPoint() - pt.x = CGFloat(x) - - // take any transformer to determine the x-axis value - self.chart?.getTransformer(ChartYAxis.AxisDependency.Left).pixelToValue(&pt) - let xVal = Double(pt.x) - - let setCount = barData.dataSetCount ?? 0 - - // calculate how often the group-space appears - let steps = Int(xVal / (Double(setCount) + Double(barData.groupSpace))) - - let groupSpaceSum = Double(barData.groupSpace) * Double(steps) - - let baseNoSpace = xVal - groupSpaceSum - - return baseNoSpace - } - /// Splits up the stack-values of the given bar-entry into Range objects. /// - parameter entry: /// - returns: public func getRanges(entry entry: BarChartDataEntry) -> [ChartRange]? { - let values = entry.values + let values = entry.yValues if (values == nil) { return nil diff --git a/Charts/Classes/Highlight/ChartHighlight.swift b/Charts/Classes/Highlight/ChartHighlight.swift index 9ea695d519..dc3b07f387 100644 --- a/Charts/Classes/Highlight/ChartHighlight.swift +++ b/Charts/Classes/Highlight/ChartHighlight.swift @@ -16,11 +16,17 @@ import Foundation public class ChartHighlight: NSObject { - /// the x-index of the highlighted value - private var _xIndex = Int(0) + /// the x-value of the highlighted value + private var _x = Double.NaN /// the y-value of the highlighted value - private var _value = Double.NaN + private var _y = Double.NaN + + /// the x-pixel of the highlight + private var _xPx = CGFloat.NaN + + /// the y-pixel of the highlight + private var _yPx = CGFloat.NaN /// the index of the data object - in case it refers to more than one private var _dataIndex = Int(0) @@ -41,71 +47,77 @@ public class ChartHighlight: NSObject super.init() } - /// - parameter xIndex: the index of the highlighted value on the x-axis - /// - parameter value: the y-value of the highlighted value + /// - parameter x: the x-value of the highlighted value + /// - parameter y: the y-value of the highlighted value /// - parameter dataIndex: the index of the Data the highlighted value belongs to /// - parameter dataSetIndex: the index of the DataSet the highlighted value belongs to /// - parameter stackIndex: references which value of a stacked-bar entry has been selected /// - parameter range: the range the selected stack-value is in - public init(xIndex x: Int, value: Double, dataIndex: Int, dataSetIndex: Int, stackIndex: Int, range: ChartRange?) + public init( + x: Double, y: Double, + dataIndex: Int, dataSetIndex: Int, + stackIndex: Int, range: ChartRange?) { super.init() - _xIndex = x - _value = value + _x = x + _y = y _dataIndex = dataIndex _dataSetIndex = dataSetIndex _stackIndex = stackIndex _range = range } - /// - parameter xIndex: the index of the highlighted value on the x-axis - /// - parameter value: the y-value of the highlighted value + /// - parameter x: the x-value of the highlighted value + /// - parameter y: the y-value of the highlighted value /// - parameter dataIndex: the index of the Data the highlighted value belongs to /// - parameter dataSetIndex: the index of the DataSet the highlighted value belongs to /// - parameter stackIndex: references which value of a stacked-bar entry has been selected - public convenience init(xIndex x: Int, value: Double, dataIndex: Int, dataSetIndex: Int, stackIndex: Int) + public convenience init( + x: Double, y: Double, + dataIndex: Int, dataSetIndex: Int, + stackIndex: Int) { - self.init(xIndex: x, value: value, dataIndex: dataIndex, dataSetIndex: dataSetIndex, stackIndex: stackIndex, range: nil) + self.init(x: x, y: y, dataIndex: dataIndex, dataSetIndex: dataSetIndex, stackIndex: stackIndex, range: nil) } - /// - parameter xIndex: the index of the highlighted value on the x-axis - /// - parameter value: the y-value of the highlighted value + /// - parameter x: the x-value of the highlighted value + /// - parameter y: the y-value of the highlighted value /// - parameter dataSetIndex: the index of the DataSet the highlighted value belongs to /// - parameter stackIndex: references which value of a stacked-bar entry has been selected /// - parameter range: the range the selected stack-value is in - public convenience init(xIndex x: Int, value: Double, dataSetIndex: Int, stackIndex: Int, range: ChartRange?) + public convenience init(x: Double, y: Double, xPx: CGFloat, yPx: CGFloat, dataSetIndex: Int, stackIndex: Int, range: ChartRange?) { - self.init(xIndex: x, value: value, dataIndex: 0, dataSetIndex: dataSetIndex, stackIndex: stackIndex, range: range) + self.init(x: x, y: y, dataIndex: 0, dataSetIndex: dataSetIndex, stackIndex: stackIndex, range: range) } - /// - parameter xIndex: the index of the highlighted value on the x-axis - /// - parameter value: the y-value of the highlighted value + /// - parameter x: the x-value of the highlighted value + /// - parameter y: the y-value of the highlighted value /// - parameter dataSetIndex: the index of the DataSet the highlighted value belongs to /// - parameter stackIndex: references which value of a stacked-bar entry has been selected /// - parameter range: the range the selected stack-value is in - public convenience init(xIndex x: Int, value: Double, dataSetIndex: Int, stackIndex: Int) + public convenience init(x: Double, y: Double, dataSetIndex: Int, stackIndex: Int) { - self.init(xIndex: x, value: value, dataIndex: 0, dataSetIndex: dataSetIndex, stackIndex: stackIndex, range: nil) + self.init(x: x, y: y, dataIndex: 0, dataSetIndex: dataSetIndex, stackIndex: stackIndex, range: nil) } - /// - parameter xIndex: the index of the highlighted value on the x-axis + /// - parameter x: the x-value of the highlighted value /// - parameter dataSetIndex: the index of the DataSet the highlighted value belongs to /// - parameter stackIndex: references which value of a stacked-bar entry has been selected - public convenience init(xIndex x: Int, dataSetIndex: Int, stackIndex: Int) + public convenience init(x: Double, dataSetIndex: Int, stackIndex: Int) { - self.init(xIndex: x, value: Double.NaN, dataSetIndex: dataSetIndex, stackIndex: stackIndex, range: nil) + self.init(x: x, y: Double.NaN, dataIndex: 0, dataSetIndex: dataSetIndex, stackIndex: stackIndex, range: nil) } - /// - parameter xIndex: the index of the highlighted value on the x-axis + /// - parameter x: the x-value of the highlighted value /// - parameter dataSetIndex: the index of the DataSet the highlighted value belongs to - public convenience init(xIndex x: Int, dataSetIndex: Int) + public convenience init(x: Double, dataSetIndex: Int) { - self.init(xIndex: x, value: Double.NaN, dataSetIndex: dataSetIndex, stackIndex: -1, range: nil) + self.init(x: x, y: Double.NaN, dataIndex: 0, dataSetIndex: dataSetIndex, stackIndex: -1, range: nil) } - public var xIndex: Int { return _xIndex } - public var value: Double { return _value } + public var x: Double { return _x } + public var y: Double { return _y } public var dataIndex: Int { return _dataIndex } public var dataSetIndex: Int { return _dataSetIndex } public var stackIndex: Int { return _stackIndex } @@ -117,7 +129,7 @@ public class ChartHighlight: NSObject public override var description: String { - return "Highlight, xIndex: \(_xIndex), dataIndex (combined charts): \(_dataIndex),dataSetIndex: \(_dataSetIndex), stackIndex (only stacked barentry): \(_stackIndex), value: \(_value)" + return "Highlight, x: \(_x), y: \(_y), dataIndex (combined charts): \(_dataIndex), dataSetIndex: \(_dataSetIndex), stackIndex (only stacked barentry): \(_stackIndex)" } public override func isEqual(object: AnyObject?) -> Bool @@ -132,27 +144,27 @@ public class ChartHighlight: NSObject return false } - if (object!.xIndex != _xIndex) + if (object!.x != _x) { return false } - if (object!.dataIndex != dataIndex) + if (object!.y != _y) { return false } - if (object!.dataSetIndex != _dataSetIndex) + if (object!.dataIndex != _dataIndex) { return false } - if (object!.stackIndex != _stackIndex) + if (object!.dataSetIndex != _dataSetIndex) { return false } - if (object!.value != value) + if (object!.stackIndex != _stackIndex) { return false } @@ -173,27 +185,27 @@ func ==(lhs: ChartHighlight, rhs: ChartHighlight) -> Bool return false } - if (lhs._xIndex != rhs._xIndex) + if (lhs._x != rhs._x) { return false } - if (lhs._dataIndex != rhs._dataIndex) + if (lhs._y != rhs._y) { return false } - if (lhs._dataSetIndex != rhs._dataSetIndex) + if (lhs._dataIndex != rhs._dataIndex) { return false } - if (lhs._stackIndex != rhs._stackIndex) + if (lhs._dataSetIndex != rhs._dataSetIndex) { return false } - if (lhs._value != rhs._value) + if (lhs._stackIndex != rhs._stackIndex) { return false } diff --git a/Charts/Classes/Highlight/ChartHighlighter.swift b/Charts/Classes/Highlight/ChartHighlighter.swift index ae24e0d73b..b7e8e97616 100644 --- a/Charts/Classes/Highlight/ChartHighlighter.swift +++ b/Charts/Classes/Highlight/ChartHighlighter.swift @@ -31,56 +31,60 @@ public class ChartHighlighter : NSObject /// - returns: public func getHighlight(x x: CGFloat, y: CGFloat) -> ChartHighlight? { - let xIndex = getXIndex(x) + let xVal = Double(getValsForTouch(x: x, y: y).x) guard let - selectionDetail = getSelectionDetail(xIndex: xIndex, y: y, dataSetIndex: nil) + selectionDetail = getSelectionDetail(xValue: xVal, x: x, y: y) else { return nil } - return ChartHighlight(xIndex: xIndex, value: selectionDetail.value, dataIndex: selectionDetail.dataIndex, dataSetIndex: selectionDetail.dataSetIndex, stackIndex: -1) + return ChartHighlight( + x: selectionDetail.xValue, + y: selectionDetail.yValue, + dataIndex: selectionDetail.dataIndex, + dataSetIndex: selectionDetail.dataSetIndex, + stackIndex: -1) } - /// Returns the corresponding x-index for a given touch-position in pixels. + /// Returns the corresponding x-pos for a given touch-position in pixels. /// - parameter x: /// - returns: - public func getXIndex(x: CGFloat) -> Int + public func getValsForTouch(x x: CGFloat, y: CGFloat) -> CGPoint { - // create an array of the touch-point - var pt = CGPoint(x: x, y: 0.0) + guard let chart = self.chart + else { return CGPointZero } - // take any transformer to determine the x-axis value - self.chart?.getTransformer(ChartYAxis.AxisDependency.Left).pixelToValue(&pt) - - return Int(round(pt.x)) + // take any transformer to determine the values + return chart.getTransformer(ChartYAxis.AxisDependency.Left).valueForTouchPoint(x: x, y: y) } - /// Returns the corresponding ChartSelectionDetail for a given xIndex and y-touch position in pixels. - /// - parameter xIndex: + /// Returns the corresponding ChartSelectionDetail for a given x-value and xy-touch position in pixels. + /// - parameter xValue: + /// - parameter x: /// - parameter y: - /// - parameter dataSetIndex: A dataset index to look at - or nil, to figure that out automatically /// - returns: - public func getSelectionDetail(xIndex xIndex: Int, y: CGFloat, dataSetIndex: Int?) -> ChartSelectionDetail? + public func getSelectionDetail(xValue xVal: Double, x: CGFloat, y: CGFloat) -> ChartSelectionDetail? { - let valsAtIndex = getSelectionDetailsAtIndex(xIndex, dataSetIndex: dataSetIndex) + guard let chart = chart + else { return nil } + + let valsAtIndex = getSelectionDetailsAtIndex(xVal) - let leftdist = ChartUtils.getMinimumDistance(valsAtIndex, y: y, axis: ChartYAxis.AxisDependency.Left) - let rightdist = ChartUtils.getMinimumDistance(valsAtIndex, y: y, axis: ChartYAxis.AxisDependency.Right) + let leftdist = getMinimumDistance(valsAtIndex, y: y, axis: ChartYAxis.AxisDependency.Left) + let rightdist = getMinimumDistance(valsAtIndex, y: y, axis: ChartYAxis.AxisDependency.Right) let axis = leftdist < rightdist ? ChartYAxis.AxisDependency.Left : ChartYAxis.AxisDependency.Right - let detail = ChartUtils.closestSelectionDetailByPixelY(valsAtIndex: valsAtIndex, y: y, axis: axis) + let detail = closestSelectionDetailByPixel(valsAtIndex: valsAtIndex, x: x, y: y, axis: axis, minSelectionDistance: chart.maxHighlightDistance) return detail } - /// Returns a list of SelectionDetail object corresponding to the given xIndex. - /// - parameter xIndex: - /// - parameter dataSetIndex: A dataset index to look at - or nil, to figure that out automatically + /// Returns a list of SelectionDetail object corresponding to the given x-value. + /// - parameter xValue: /// - returns: - public func getSelectionDetailsAtIndex(xIndex: Int, dataSetIndex: Int?) -> [ChartSelectionDetail] + public func getSelectionDetailsAtIndex(xValue: Double) -> [ChartSelectionDetail] { var vals = [ChartSelectionDetail]() - var pt = CGPoint() guard let data = self.chart?.data @@ -88,11 +92,6 @@ public class ChartHighlighter : NSObject for i in 0 ..< data.dataSetCount { - if dataSetIndex != nil && dataSetIndex != i - { - continue - } - let dataSet = data.getDataSetByIndex(i) // dont include datasets that cannot be highlighted @@ -101,21 +100,106 @@ public class ChartHighlighter : NSObject continue } - // extract all y-values from all DataSets at the given x-index - let yVals: [Double] = dataSet.yValsForXIndex(xIndex) - for yVal in yVals + // extract all y-values from all DataSets at the given x-value. + // some datasets (i.e bubble charts) make sense to have multiple values for an x-value. We'll have to find a way to handle that later on. It's more complicated now when x-indices are floating point. + + if let details = getDetails(dataSet, dataSetIndex: i, xValue: xValue, rounding: .Up) { - pt.y = CGFloat(yVal) - - self.chart!.getTransformer(dataSet.axisDependency).pointValueToPixel(&pt) + vals.append(details) + } + + if let details = getDetails(dataSet, dataSetIndex: i, xValue: xValue, rounding: .Down) + { + vals.append(details) + } + } + + return vals + } + + internal func getDetails( + set: IChartDataSet, + dataSetIndex: Int, + xValue: Double, + rounding: ChartDataSetRounding) -> ChartSelectionDetail? + { + guard let chart = self.chart + else { return nil } + + if let e = set.entryForXPos(xValue, rounding: rounding) + { + let px = chart.getTransformer(set.axisDependency).pixelForValue(x: e.x, y: e.y) + + return ChartSelectionDetail(x: px.x, y: px.y, xValue: e.x, yValue: e.y, dataSetIndex: dataSetIndex, dataSet: set) + } + + return nil + } + + // - MARK: - Utilities + + /// - returns: the `ChartSelectionDetail` of the closest value on the x-y cartesian axes + internal func closestSelectionDetailByPixel( + valsAtIndex valsAtIndex: [ChartSelectionDetail], + x: CGFloat, + y: CGFloat, + axis: ChartYAxis.AxisDependency?, + minSelectionDistance: CGFloat) -> ChartSelectionDetail? + { + var distance = minSelectionDistance + var detail: ChartSelectionDetail? + + for i in 0 ..< valsAtIndex.count + { + let sel = valsAtIndex[i] + + if (axis == nil || sel.dataSet?.axisDependency == axis) + { + let cDistance = getDistance(x: x, y: y, selX: sel.x, selY: sel.y) - if !pt.y.isNaN + if (cDistance < distance) { - vals.append(ChartSelectionDetail(y: pt.y, value: yVal, dataSetIndex: i, dataSet: dataSet)) + detail = sel + distance = cDistance } } } - return vals + return detail + } + + /// - returns: the minimum distance from a touch-y-value (in pixels) to the closest y-value (in pixels) that is displayed in the chart. + internal func getMinimumDistance( + valsAtIndex: [ChartSelectionDetail], + y: CGFloat, + axis: ChartYAxis.AxisDependency) -> CGFloat + { + var distance = CGFloat.max + + for i in 0 ..< valsAtIndex.count + { + let sel = valsAtIndex[i] + + if (sel.dataSet!.axisDependency == axis) + { + let cdistance = abs(getSelectionPos(sel: sel) - y) + if (cdistance < distance) + { + distance = cdistance + } + } + } + + return distance + } + + internal func getSelectionPos(sel sel: ChartSelectionDetail) -> CGFloat + { + return sel.y + } + + internal func getDistance(x x: CGFloat, y: CGFloat, selX: CGFloat, selY: CGFloat) -> CGFloat + { + return hypot(x - selX, y - selY) } } diff --git a/Charts/Classes/Highlight/CombinedHighlighter.swift b/Charts/Classes/Highlight/CombinedHighlighter.swift index 68a3f9499f..5226825b77 100644 --- a/Charts/Classes/Highlight/CombinedHighlighter.swift +++ b/Charts/Classes/Highlight/CombinedHighlighter.swift @@ -17,13 +17,12 @@ import CoreGraphics public class CombinedHighlighter: ChartHighlighter { - /// Returns a list of SelectionDetail object corresponding to the given xIndex. - /// - parameter xIndex: + /// Returns a list of SelectionDetail object corresponding to the given xValue. + /// - parameter xValue: /// - returns: - public override func getSelectionDetailsAtIndex(xIndex: Int, dataSetIndex: Int?) -> [ChartSelectionDetail] + public override func getSelectionDetailsAtIndex(xValue: Double) -> [ChartSelectionDetail] { var vals = [ChartSelectionDetail]() - var pt = CGPoint() guard let data = self.chart?.data as? CombinedChartData @@ -38,26 +37,22 @@ public class CombinedHighlighter: ChartHighlighter { let dataSet = dataObjects[i].getDataSetByIndex(j) - // dont include datasets that cannot be highlighted + // don't include datasets that cannot be highlighted if !dataSet.isHighlightEnabled { continue } - // extract all y-values from all DataSets at the given x-index - let yVals: [Double] = dataSet.yValsForXIndex(xIndex) - for yVal in yVals + if let s1 = getDetails(dataSet, dataSetIndex: j, xValue: xValue, rounding: .Up) { - pt.y = CGFloat(yVal) - - self.chart! - .getTransformer(dataSet.axisDependency) - .pointValueToPixel(&pt) - - if !pt.y.isNaN - { - vals.append(ChartSelectionDetail(y: pt.y, value: yVal, dataIndex: i, dataSetIndex: j, dataSet: dataSet)) - } + s1.dataIndex = i + vals.append(s1) + } + + if let s2 = getDetails(dataSet, dataSetIndex: j, xValue: xValue, rounding: .Down) + { + s2.dataIndex = i + vals.append(s2) } } } diff --git a/Charts/Classes/Highlight/HorizontalBarChartHighlighter.swift b/Charts/Classes/Highlight/HorizontalBarChartHighlighter.swift index efdd60495a..d772c8165a 100644 --- a/Charts/Classes/Highlight/HorizontalBarChartHighlighter.swift +++ b/Charts/Classes/Highlight/HorizontalBarChartHighlighter.swift @@ -21,39 +21,22 @@ public class HorizontalBarChartHighlighter: BarChartHighlighter { if let barData = self.chart?.data as? BarChartData { - let xIndex = getXIndex(x) - let baseNoSpace = getBase(x) - let setCount = barData.dataSetCount - var dataSetIndex = Int(baseNoSpace) % setCount + let pos = getValsForTouch(x: y, y: x) - if dataSetIndex < 0 - { - dataSetIndex = 0 - } - else if dataSetIndex >= setCount - { - dataSetIndex = setCount - 1 - } - - guard let selectionDetail = getSelectionDetail(xIndex: xIndex, y: y, dataSetIndex: dataSetIndex) + guard let selectionDetail = getSelectionDetail(xValue: Double(pos.y), x: y, y: x) else { return nil } - if let set = barData.getDataSetByIndex(dataSetIndex) as? IBarChartDataSet + if let set = barData.getDataSetByIndex(selectionDetail.dataSetIndex) as? IBarChartDataSet where set.isStacked { - var pt = CGPoint(x: y, y: 0.0) - - // take any transformer to determine the x-axis value - self.chart?.getTransformer(set.axisDependency).pixelToValue(&pt) - return getStackedHighlight(selectionDetail: selectionDetail, set: set, - xIndex: xIndex, - yValue: Double(pt.x)) + xValue: Double(pos.y), + yValue: Double(pos.x)) } - return ChartHighlight(xIndex: xIndex, - value: selectionDetail.value, + return ChartHighlight(x: selectionDetail.xValue, + y: selectionDetail.yValue, dataIndex: selectionDetail.dataIndex, dataSetIndex: selectionDetail.dataSetIndex, stackIndex: -1) @@ -61,54 +44,27 @@ public class HorizontalBarChartHighlighter: BarChartHighlighter return nil } - public override func getXIndex(x: CGFloat) -> Int + internal override func getDetails( + set: IChartDataSet, + dataSetIndex: Int, + xValue: Double, + rounding: ChartDataSetRounding) -> ChartSelectionDetail? { - if let barData = self.chart?.data as? BarChartData - where !barData.isGrouped + guard let chart = self.chart + else { return nil } + + if let e = set.entryForXPos(xValue, rounding: rounding) { - // create an array of the touch-point - var pt = CGPoint(x: 0.0, y: x) + let px = chart.getTransformer(set.axisDependency).pixelForValue(x: e.y, y: e.x) - // take any transformer to determine the x-axis value - self.chart?.getTransformer(ChartYAxis.AxisDependency.Left).pixelToValue(&pt) - - return Int(round(pt.y)) - } - else - { - return super.getXIndex(x) + return ChartSelectionDetail(x: px.x, y: px.y, xValue: e.x, yValue: e.y, dataSetIndex: dataSetIndex, dataSet: set) } + + return nil } - /// Returns the base y-value to the corresponding x-touch value in pixels. - /// - parameter y: - /// - returns: - public override func getBase(y: CGFloat) -> Double + internal override func getDistance(x x: CGFloat, y: CGFloat, selX: CGFloat, selY: CGFloat) -> CGFloat { - if let barData = self.chart?.data as? BarChartData - { - // create an array of the touch-point - var pt = CGPoint() - pt.y = CGFloat(y) - - // take any transformer to determine the x-axis value - self.chart?.getTransformer(ChartYAxis.AxisDependency.Left).pixelToValue(&pt) - let yVal = Double(pt.y) - - let setCount = barData.dataSetCount ?? 0 - - // calculate how often the group-space appears - let steps = Int(yVal / (Double(setCount) + Double(barData.groupSpace))) - - let groupSpaceSum = Double(barData.groupSpace) * Double(steps) - - let baseNoSpace = yVal - groupSpaceSum - - return baseNoSpace - } - else - { - return 0.0 - } + return abs(y - selY) } } diff --git a/Charts/Classes/Interfaces/BarChartDataProvider.swift b/Charts/Classes/Interfaces/BarChartDataProvider.swift index 74c3c4f42d..efb3cf0efe 100644 --- a/Charts/Classes/Interfaces/BarChartDataProvider.swift +++ b/Charts/Classes/Interfaces/BarChartDataProvider.swift @@ -21,5 +21,4 @@ public protocol BarChartDataProvider: BarLineScatterCandleBubbleChartDataProvide var isDrawBarShadowEnabled: Bool { get } var isDrawValueAboveBarEnabled: Bool { get } - var isDrawHighlightArrowEnabled: Bool { get } } \ No newline at end of file diff --git a/Charts/Classes/Interfaces/BarLineScatterCandleBubbleChartDataProvider.swift b/Charts/Classes/Interfaces/BarLineScatterCandleBubbleChartDataProvider.swift index 66ddde0f09..0fae617787 100644 --- a/Charts/Classes/Interfaces/BarLineScatterCandleBubbleChartDataProvider.swift +++ b/Charts/Classes/Interfaces/BarLineScatterCandleBubbleChartDataProvider.swift @@ -18,9 +18,8 @@ import CoreGraphics public protocol BarLineScatterCandleBubbleChartDataProvider: ChartDataProvider { func getTransformer(which: ChartYAxis.AxisDependency) -> ChartTransformer - var maxVisibleValueCount: Int { get } func isInverted(axis: ChartYAxis.AxisDependency) -> Bool - var lowestVisibleXIndex: Int { get } - var highestVisibleXIndex: Int { get } + var lowestVisibleX: Double { get } + var highestVisibleX: Double { get } } \ No newline at end of file diff --git a/Charts/Classes/Interfaces/ChartDataProvider.swift b/Charts/Classes/Interfaces/ChartDataProvider.swift index acf3c6f36a..8434b307bc 100644 --- a/Charts/Classes/Interfaces/ChartDataProvider.swift +++ b/Charts/Classes/Interfaces/ChartDataProvider.swift @@ -29,9 +29,13 @@ public protocol ChartDataProvider /// - returns: the maximum y-value of the chart, regardless of zoom or translation. var chartYMax: Double { get } - var xValCount: Int { get } + var maxHighlightDistance: CGFloat { get } + + var xRange: Double { get } var centerOffsets: CGPoint { get } var data: ChartData? { get } + + var maxVisibleCount: Int { get } } \ No newline at end of file diff --git a/Charts/Classes/Jobs/AnimatedMoveViewJob.swift b/Charts/Classes/Jobs/AnimatedMoveViewJob.swift index 41301ba4fb..59078bb01c 100644 --- a/Charts/Classes/Jobs/AnimatedMoveViewJob.swift +++ b/Charts/Classes/Jobs/AnimatedMoveViewJob.swift @@ -20,7 +20,7 @@ public class AnimatedMoveChartViewJob: AnimatedViewPortJob { public override init( viewPortHandler: ChartViewPortHandler, - xIndex: CGFloat, + xValue: Double, yValue: Double, transformer: ChartTransformer, view: ChartViewBase, @@ -30,7 +30,7 @@ public class AnimatedMoveChartViewJob: AnimatedViewPortJob easing: ChartEasingFunctionBlock?) { super.init(viewPortHandler: viewPortHandler, - xIndex: xIndex, + xValue: xValue, yValue: yValue, transformer: transformer, view: view, @@ -49,7 +49,7 @@ public class AnimatedMoveChartViewJob: AnimatedViewPortJob else { return } var pt = CGPoint( - x: xOrigin + (xIndex - xOrigin) * phase, + x: xOrigin + (CGFloat(xValue) - xOrigin) * phase, y: yOrigin + (CGFloat(yValue) - yOrigin) * phase ); diff --git a/Charts/Classes/Jobs/AnimatedViewPortJob.swift b/Charts/Classes/Jobs/AnimatedViewPortJob.swift index a1558c4b42..8dff9fdece 100644 --- a/Charts/Classes/Jobs/AnimatedViewPortJob.swift +++ b/Charts/Classes/Jobs/AnimatedViewPortJob.swift @@ -31,7 +31,7 @@ public class AnimatedViewPortJob: ChartViewPortJob public init( viewPortHandler: ChartViewPortHandler, - xIndex: CGFloat, + xValue: Double, yValue: Double, transformer: ChartTransformer, view: ChartViewBase, @@ -41,7 +41,7 @@ public class AnimatedViewPortJob: ChartViewPortJob easing: ChartEasingFunctionBlock?) { super.init(viewPortHandler: viewPortHandler, - xIndex: xIndex, + xValue: xValue, yValue: yValue, transformer: transformer, view: view) @@ -108,7 +108,7 @@ public class AnimatedViewPortJob: ChartViewPortJob if _easing != nil { - phase = _easing!(elapsed: elapsed, duration: duration) + phase = CGFloat(_easing!(elapsed: elapsed, duration: duration)) } else { diff --git a/Charts/Classes/Jobs/AnimatedZoomViewJob.swift b/Charts/Classes/Jobs/AnimatedZoomViewJob.swift index 9479aa32c2..0dc52b322b 100644 --- a/Charts/Classes/Jobs/AnimatedZoomViewJob.swift +++ b/Charts/Classes/Jobs/AnimatedZoomViewJob.swift @@ -15,7 +15,7 @@ import CoreGraphics public class AnimatedZoomChartViewJob: AnimatedViewPortJob { internal var yAxis: ChartYAxis? - internal var xValCount: Int = 0 + internal var xAxisRange: Double = 0.0 internal var scaleX: CGFloat = 0.0 internal var scaleY: CGFloat = 0.0 internal var zoomOriginX: CGFloat = 0.0 @@ -28,7 +28,7 @@ public class AnimatedZoomChartViewJob: AnimatedViewPortJob transformer: ChartTransformer, view: ChartViewBase, yAxis: ChartYAxis, - xValCount: Int, + xAxisRange: Double, scaleX: CGFloat, scaleY: CGFloat, xOrigin: CGFloat, @@ -41,7 +41,7 @@ public class AnimatedZoomChartViewJob: AnimatedViewPortJob easing: ChartEasingFunctionBlock?) { super.init(viewPortHandler: viewPortHandler, - xIndex: 0.0, + xValue: 0.0, yValue: 0.0, transformer: transformer, view: view, @@ -51,7 +51,7 @@ public class AnimatedZoomChartViewJob: AnimatedViewPortJob easing: easing) self.yAxis = yAxis - self.xValCount = xValCount + self.xAxisRange = xAxisRange self.scaleX = scaleX self.scaleY = scaleY self.zoomCenterX = zoomCenterX @@ -75,7 +75,7 @@ public class AnimatedZoomChartViewJob: AnimatedViewPortJob viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: false) let valsInView = CGFloat(yAxis?.axisRange ?? 0.0) / viewPortHandler.scaleY - let xsInView = CGFloat(xValCount) / viewPortHandler.scaleX + let xsInView = CGFloat(xAxisRange) / viewPortHandler.scaleX var pt = CGPoint( x: zoomOriginX + ((zoomCenterX - xsInView / 2.0) - zoomOriginX) * phase, diff --git a/Charts/Classes/Jobs/ChartViewPortJob.swift b/Charts/Classes/Jobs/ChartViewPortJob.swift index 31c08f4759..2b9dbf6e3d 100644 --- a/Charts/Classes/Jobs/ChartViewPortJob.swift +++ b/Charts/Classes/Jobs/ChartViewPortJob.swift @@ -17,20 +17,20 @@ public class ChartViewPortJob { internal var point: CGPoint = CGPoint() internal weak var viewPortHandler: ChartViewPortHandler? - internal var xIndex: CGFloat = 0.0 + internal var xValue: Double = 0.0 internal var yValue: Double = 0.0 internal weak var transformer: ChartTransformer? internal weak var view: ChartViewBase? public init( viewPortHandler: ChartViewPortHandler, - xIndex: CGFloat, + xValue: Double, yValue: Double, transformer: ChartTransformer, view: ChartViewBase) { self.viewPortHandler = viewPortHandler - self.xIndex = xIndex + self.xValue = xValue self.yValue = yValue self.transformer = transformer self.view = view diff --git a/Charts/Classes/Jobs/MoveChartViewJob.swift b/Charts/Classes/Jobs/MoveChartViewJob.swift index 5e28c47da4..638f6c68bd 100644 --- a/Charts/Classes/Jobs/MoveChartViewJob.swift +++ b/Charts/Classes/Jobs/MoveChartViewJob.swift @@ -20,14 +20,14 @@ public class MoveChartViewJob: ChartViewPortJob { public override init( viewPortHandler: ChartViewPortHandler, - xIndex: CGFloat, + xValue: Double, yValue: Double, transformer: ChartTransformer, view: ChartViewBase) { super.init( viewPortHandler: viewPortHandler, - xIndex: xIndex, + xValue: xValue, yValue: yValue, transformer: transformer, view: view) @@ -42,7 +42,7 @@ public class MoveChartViewJob: ChartViewPortJob else { return } var pt = CGPoint( - x: xIndex, + x: CGFloat(xValue), y: CGFloat(yValue) ); diff --git a/Charts/Classes/Jobs/ZoomChartViewJob.swift b/Charts/Classes/Jobs/ZoomChartViewJob.swift index a95b1fa52f..5d146b2845 100644 --- a/Charts/Classes/Jobs/ZoomChartViewJob.swift +++ b/Charts/Classes/Jobs/ZoomChartViewJob.swift @@ -26,7 +26,7 @@ public class ZoomChartViewJob: ChartViewPortJob viewPortHandler: ChartViewPortHandler, scaleX: CGFloat, scaleY: CGFloat, - xIndex: CGFloat, + xValue: Double, yValue: Double, transformer: ChartTransformer, axis: ChartYAxis.AxisDependency, @@ -34,7 +34,7 @@ public class ZoomChartViewJob: ChartViewPortJob { super.init( viewPortHandler: viewPortHandler, - xIndex: xIndex, + xValue: xValue, yValue: yValue, transformer: transformer, view: view) @@ -55,12 +55,12 @@ public class ZoomChartViewJob: ChartViewPortJob var matrix = viewPortHandler.setZoom(scaleX: scaleX, scaleY: scaleY) viewPortHandler.refresh(newMatrix: matrix, chart: view, invalidate: false) - let valsInView = (view as! BarLineChartViewBase).getDeltaY(axisDependency) / viewPortHandler.scaleY - let xsInView = CGFloat((view as! BarLineChartViewBase).xAxis.values.count) / viewPortHandler.scaleX + let valsInView = (view as! BarLineChartViewBase).getDeltaY(axisDependency) / Double(viewPortHandler.scaleY) + let xsInView = (view as! BarLineChartViewBase).xAxis.axisRange / Double(viewPortHandler.scaleX) var pt = CGPoint( - x: xIndex - xsInView / 2.0, - y: CGFloat(yValue) + valsInView / 2.0 + x: CGFloat(xValue - xsInView / 2.0), + y: CGFloat(yValue + valsInView / 2.0) ) transformer.pointValueToPixel(&pt) diff --git a/Charts/Classes/Renderers/BarChartRenderer.swift b/Charts/Classes/Renderers/BarChartRenderer.swift index 653b25dfe4..97f0b1c394 100644 --- a/Charts/Classes/Renderers/BarChartRenderer.swift +++ b/Charts/Classes/Renderers/BarChartRenderer.swift @@ -20,36 +20,57 @@ import CoreGraphics public class BarChartRenderer: ChartDataRendererBase { + private class Buffer + { + var rects = [CGRect]() + } + public weak var dataProvider: BarChartDataProvider? - public init(dataProvider: BarChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public init(dataProvider: BarChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) self.dataProvider = dataProvider } - public override func drawData(context context: CGContext) + // [CGRect] per dataset + private var _buffers = [Buffer]() + + public override func initBuffers() { - guard let dataProvider = dataProvider, barData = dataProvider.barData else { return } - - for i in 0 ..< barData.dataSetCount + if let barData = dataProvider?.barData { - guard let set = barData.getDataSetByIndex(i) else { continue } + // Matche buffers count to dataset count + if _buffers.count != barData.dataSetCount + { + while _buffers.count < barData.dataSetCount + { + _buffers.append(Buffer()) + } + while _buffers.count > barData.dataSetCount + { + _buffers.removeLast() + } + } - if set.isVisible && set.entryCount > 0 + for i in 0.stride(to: barData.dataSetCount, by: 1) { - if !(set is IBarChartDataSet) + let set = barData.dataSets[i] as! IBarChartDataSet + let size = set.entryCount * (set.isStacked ? set.stackSize : 1) + if _buffers[i].rects.count != size { - fatalError("Datasets for BarChartRenderer must conform to IBarChartDataset") + _buffers[i].rects = [CGRect](count: size, repeatedValue: CGRect()) } - - drawDataSet(context: context, dataSet: set as! IBarChartDataSet, index: i) } } + else + { + _buffers.removeAll() + } } - public func drawDataSet(context context: CGContext, dataSet: IBarChartDataSet, index: Int) + private func prepareBuffer(dataSet dataSet: IBarChartDataSet, index: Int) { guard let dataProvider = dataProvider, @@ -57,54 +78,46 @@ public class BarChartRenderer: ChartDataRendererBase animator = animator else { return } - CGContextSaveGState(context) - - let trans = dataProvider.getTransformer(dataSet.axisDependency) - - let drawBarShadowEnabled: Bool = dataProvider.isDrawBarShadowEnabled - let dataSetOffset = (barData.dataSetCount - 1) - let groupSpace = barData.groupSpace - let groupSpaceHalf = groupSpace / 2.0 - let barSpace = dataSet.barSpace - let barSpaceHalf = barSpace / 2.0 + let barWidthHalf = barData.barWidth / 2.0 + + let buffer = _buffers[index] + var bufferIndex = 0 let containsStacks = dataSet.isStacked + let isInverted = dataProvider.isInverted(dataSet.axisDependency) - let barWidth: CGFloat = 0.5 let phaseY = animator.phaseY var barRect = CGRect() - var barShadow = CGRect() - let borderWidth = dataSet.barBorderWidth - let borderColor = dataSet.barBorderColor - let drawBorder = borderWidth > 0.0 + var x: Double var y: Double - // do the drawing - for j in 0 ..< Int(ceil(CGFloat(dataSet.entryCount) * animator.phaseX)) + for i in 0.stride(to: min(Int(ceil(Double(dataSet.entryCount) * animator.phaseX)), dataSet.entryCount), by: 1) { - guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue } + guard let e = dataSet.entryForIndex(i) as? BarChartDataEntry else { continue } + + let vals = e.yValues - // calculate the x-position, depending on datasetcount - let x = CGFloat(e.xIndex + e.xIndex * dataSetOffset) + CGFloat(index) - + groupSpace * CGFloat(e.xIndex) + groupSpaceHalf - var vals = e.values + x = e.x + y = e.y - if (!containsStacks || vals == nil) + if !containsStacks || vals == nil { - y = e.value - - let left = x - barWidth + barSpaceHalf - let right = x + barWidth - barSpaceHalf - var top = isInverted ? (y <= 0.0 ? CGFloat(y) : 0) : (y >= 0.0 ? CGFloat(y) : 0) - var bottom = isInverted ? (y >= 0.0 ? CGFloat(y) : 0) : (y <= 0.0 ? CGFloat(y) : 0) + let left = CGFloat(x - barWidthHalf) + let right = CGFloat(x + barWidthHalf) + var top = isInverted + ? (y <= 0.0 ? CGFloat(y) : 0) + : (y >= 0.0 ? CGFloat(y) : 0) + var bottom = isInverted + ? (y >= 0.0 ? CGFloat(y) : 0) + : (y <= 0.0 ? CGFloat(y) : 0) // multiply the height of the rect with the phase if (top > 0) { - top *= phaseY + top *= CGFloat(phaseY) } else { - bottom *= phaseY + bottom *= CGFloat(phaseY) } barRect.origin.x = left @@ -112,40 +125,8 @@ public class BarChartRenderer: ChartDataRendererBase barRect.origin.y = top barRect.size.height = bottom - top - trans.rectValueToPixel(&barRect) - - if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) - { - continue - } - - if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) - { - break - } - - // if drawing the bar shadow is enabled - if (drawBarShadowEnabled) - { - barShadow.origin.x = barRect.origin.x - barShadow.origin.y = viewPortHandler.contentTop - barShadow.size.width = barRect.size.width - barShadow.size.height = viewPortHandler.contentHeight - - CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor) - CGContextFillRect(context, barShadow) - } - - // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. - CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor) - CGContextFillRect(context, barRect) - - if drawBorder - { - CGContextSetStrokeColorWithColor(context, borderColor.CGColor) - CGContextSetLineWidth(context, borderWidth) - CGContextStrokeRect(context, barRect) - } + buffer.rects[bufferIndex] = barRect + bufferIndex += 1 } else { @@ -153,41 +134,6 @@ public class BarChartRenderer: ChartDataRendererBase var negY = -e.negativeSum var yStart = 0.0 - // if drawing the bar shadow is enabled - if (drawBarShadowEnabled) - { - y = e.value - - let left = x - barWidth + barSpaceHalf - let right = x + barWidth - barSpaceHalf - var top = isInverted ? (y <= 0.0 ? CGFloat(y) : 0) : (y >= 0.0 ? CGFloat(y) : 0) - var bottom = isInverted ? (y >= 0.0 ? CGFloat(y) : 0) : (y <= 0.0 ? CGFloat(y) : 0) - - // multiply the height of the rect with the phase - if (top > 0) - { - top *= phaseY - } - else - { - bottom *= phaseY - } - - barRect.origin.x = left - barRect.size.width = right - left - barRect.origin.y = top - barRect.size.height = bottom - top - - trans.rectValueToPixel(&barRect) - - barShadow.origin.x = barRect.origin.x - barShadow.origin.y = viewPortHandler.contentTop - barShadow.size.width = barRect.size.width - barShadow.size.height = viewPortHandler.contentHeight - - CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor) - CGContextFillRect(context, barShadow) - } // fill the stack for k in 0 ..< vals!.count @@ -207,95 +153,174 @@ public class BarChartRenderer: ChartDataRendererBase negY += abs(value) } - let left = x - barWidth + barSpaceHalf - let right = x + barWidth - barSpaceHalf - var top: CGFloat, bottom: CGFloat - if isInverted - { - bottom = y >= yStart ? CGFloat(y) : CGFloat(yStart) - top = y <= yStart ? CGFloat(y) : CGFloat(yStart) - } - else - { - top = y >= yStart ? CGFloat(y) : CGFloat(yStart) - bottom = y <= yStart ? CGFloat(y) : CGFloat(yStart) - } + let left = CGFloat(x - barWidthHalf) + let right = CGFloat(x + barWidthHalf) + var top = isInverted + ? (y <= yStart ? CGFloat(y) : CGFloat(yStart)) + : (y >= yStart ? CGFloat(y) : CGFloat(yStart)) + var bottom = isInverted + ? (y >= yStart ? CGFloat(y) : CGFloat(yStart)) + : (y <= yStart ? CGFloat(y) : CGFloat(yStart)) // multiply the height of the rect with the phase - top *= phaseY - bottom *= phaseY + top *= CGFloat(phaseY) + bottom *= CGFloat(phaseY) barRect.origin.x = left barRect.size.width = right - left barRect.origin.y = top barRect.size.height = bottom - top - trans.rectValueToPixel(&barRect) - - if (k == 0 && !viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) - { - // Skip to next bar - break - } - - // avoid drawing outofbounds values - if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) - { - break - } - - // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. - CGContextSetFillColorWithColor(context, dataSet.colorAt(k).CGColor) - CGContextFillRect(context, barRect) - - if drawBorder - { - CGContextSetStrokeColorWithColor(context, borderColor.CGColor) - CGContextSetLineWidth(context, borderWidth) - CGContextStrokeRect(context, barRect) - } + buffer.rects[bufferIndex] = barRect + bufferIndex += 1 } } } + } + + public override func drawData(context context: CGContext) + { + guard let dataProvider = dataProvider, barData = dataProvider.barData else { return } - CGContextRestoreGState(context) + for i in 0 ..< barData.dataSetCount + { + guard let set = barData.getDataSetByIndex(i) else { continue } + + if set.isVisible && set.entryCount > 0 + { + if !(set is IBarChartDataSet) + { + fatalError("Datasets for BarChartRenderer must conform to IBarChartDataset") + } + + drawDataSet(context: context, dataSet: set as! IBarChartDataSet, index: i) + } + } } - - /// Prepares a bar for being highlighted. - public func prepareBarHighlight(x x: CGFloat, y1: Double, y2: Double, barspacehalf: CGFloat, trans: ChartTransformer, inout rect: CGRect) + + public func drawDataSet(context context: CGContext, dataSet: IBarChartDataSet, index: Int) { - let barWidth: CGFloat = 0.5 + guard let + dataProvider = dataProvider, + viewPortHandler = self.viewPortHandler + else { return } - let left = x - barWidth + barspacehalf - let right = x + barWidth - barspacehalf - let top = CGFloat(y1) - let bottom = CGFloat(y2) + let trans = dataProvider.getTransformer(dataSet.axisDependency) - rect.origin.x = left - rect.origin.y = top - rect.size.width = right - left - rect.size.height = bottom - top + prepareBuffer(dataSet: dataSet, index: index) + trans.rectValuesToPixel(&_buffers[index].rects) - trans.rectValueToPixel(&rect, phaseY: animator?.phaseY ?? 1.0) + let borderWidth = dataSet.barBorderWidth + let borderColor = dataSet.barBorderColor + let drawBorder = borderWidth > 0.0 + + CGContextSaveGState(context) + + let buffer = _buffers[index] + + // draw the bar shadow before the values + if dataProvider.isDrawBarShadowEnabled + { + for j in 0.stride(to: buffer.rects.count, by: 1) + { + let barRect = buffer.rects[j] + + if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) + { + continue + } + + if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) + { + break + } + + CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor) + CGContextFillRect(context, barRect) + } + } + + // FIXME: DRY code on Android + + let isSingleColor = dataSet.colors.count == 1 + + if isSingleColor + { + CGContextSetFillColorWithColor(context, dataSet.colorAt(0).CGColor) + } + + for j in 0.stride(to: buffer.rects.count, by: 1) + { + let barRect = buffer.rects[j] + + if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) + { + continue + } + + if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) + { + break + } + + if !isSingleColor + { + // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor) + } + + CGContextFillRect(context, barRect) + + if drawBorder + { + CGContextSetStrokeColorWithColor(context, borderColor.CGColor) + CGContextSetLineWidth(context, borderWidth) + CGContextStrokeRect(context, barRect) + } + } + + CGContextRestoreGState(context) } + public func prepareBarHighlight( + x x: Double, + y1: Double, + y2: Double, + barWidthHalf: Double, + trans: ChartTransformer, + inout rect: CGRect) + { + let left = x - barWidthHalf + let right = x + barWidthHalf + let top = y1 + let bottom = y2 + + rect.origin.x = CGFloat(left) + rect.origin.y = CGFloat(top) + rect.size.width = CGFloat(right - left) + rect.size.height = CGFloat(bottom - top) + + trans.rectValueToPixel(&rect, phaseY: animator?.phaseY ?? 1.0) + } + public override func drawValues(context context: CGContext) { // if values are drawn - if (passesCheck()) + if isDrawingValuesAllowed(dataProvider: dataProvider) { guard let dataProvider = dataProvider, + viewPortHandler = self.viewPortHandler, barData = dataProvider.barData, animator = animator else { return } var dataSets = barData.dataSets - - let drawValueAboveBar = dataProvider.isDrawValueAboveBarEnabled + let valueOffsetPlus: CGFloat = 4.5 var posOffset: CGFloat var negOffset: CGFloat + let drawValueAboveBar = dataProvider.isDrawValueAboveBarEnabled for dataSetIndex in 0 ..< barData.dataSetCount { @@ -309,7 +334,6 @@ public class BarChartRenderer: ChartDataRendererBase let isInverted = dataProvider.isInverted(dataSet.axisDependency) // calculate the correct offset depending on the draw position of the value - let valueOffsetPlus: CGFloat = 4.5 let valueFont = dataSet.valueFont let valueTextHeight = valueFont.lineHeight posOffset = (drawValueAboveBar ? -(valueTextHeight + valueOffsetPlus) : valueOffsetPlus) @@ -321,47 +345,45 @@ public class BarChartRenderer: ChartDataRendererBase negOffset = -negOffset - valueTextHeight } + let buffer = _buffers[dataSetIndex] + guard let formatter = dataSet.valueFormatter else { continue } let trans = dataProvider.getTransformer(dataSet.axisDependency) + // FIXME: Put in variable on Android let phaseY = animator.phaseY - let dataSetCount = barData.dataSetCount - let groupSpace = barData.groupSpace - + // if only single values are drawn (sum) - if (!dataSet.isStacked) + if !dataSet.isStacked { - for j in 0 ..< Int(ceil(CGFloat(dataSet.entryCount) * animator.phaseX)) + for j in 0 ..< Int(ceil(Double(dataSet.entryCount) * animator.phaseX)) { guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue } - let valuePoint = trans.getTransformedValueBarChart( - entry: e, - xIndex: e.xIndex, - dataSetIndex: dataSetIndex, - phaseY: phaseY, - dataSetCount: dataSetCount, - groupSpace: groupSpace - ) + let rect = buffer.rects[j] + + let x = rect.origin.x + rect.size.width / 2.0 - if (!viewPortHandler.isInBoundsRight(valuePoint.x)) + if !viewPortHandler.isInBoundsRight(x) { break } - if (!viewPortHandler.isInBoundsY(valuePoint.y) - || !viewPortHandler.isInBoundsLeft(valuePoint.x)) + if !viewPortHandler.isInBoundsY(rect.origin.y) + || !viewPortHandler.isInBoundsLeft(x) { continue } - let val = e.value + let val = e.y drawValue(context: context, value: formatter.stringFromNumber(val)!, - xPos: valuePoint.x, - yPos: valuePoint.y + (val >= 0.0 ? posOffset : negOffset), + xPos: x, + yPos: val >= 0.0 + ? (rect.origin.y + posOffset) + : (rect.origin.y + rect.size.height + negOffset), font: valueFont, align: .Center, color: dataSet.valueTextColorAt(j)) @@ -371,41 +393,46 @@ public class BarChartRenderer: ChartDataRendererBase { // if we have stacks - for j in 0 ..< Int(ceil(CGFloat(dataSet.entryCount) * animator.phaseX)) + var bufferIndex = 0 + + for index in 0 ..< Int(ceil(Double(dataSet.entryCount) * animator.phaseX)) { - guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue } + guard let e = dataSet.entryForIndex(index) as? BarChartDataEntry else { continue } + + let vals = e.yValues - let values = e.values + let rect = buffer.rects[bufferIndex] - let valuePoint = trans.getTransformedValueBarChart(entry: e, xIndex: e.xIndex, dataSetIndex: dataSetIndex, phaseY: phaseY, dataSetCount: dataSetCount, groupSpace: groupSpace) + let x = rect.origin.x + rect.size.width / 2.0 // we still draw stacked bars, but there is one non-stacked in between - if (values == nil) + if vals == nil { - if (!viewPortHandler.isInBoundsRight(valuePoint.x)) + if !viewPortHandler.isInBoundsRight(x) { break } - if (!viewPortHandler.isInBoundsY(valuePoint.y) - || !viewPortHandler.isInBoundsLeft(valuePoint.x)) + if !viewPortHandler.isInBoundsY(rect.origin.y) + || !viewPortHandler.isInBoundsLeft(x) { continue } drawValue(context: context, - value: formatter.stringFromNumber(e.value)!, - xPos: valuePoint.x, - yPos: valuePoint.y + (e.value >= 0.0 ? posOffset : negOffset), - font: valueFont, - align: .Center, - color: dataSet.valueTextColorAt(j)) + value: formatter.stringFromNumber(e.y)!, + xPos: x, + yPos: rect.origin.y + + (e.y >= 0 ? posOffset : negOffset), + font: valueFont, + align: .Center, + color: dataSet.valueTextColorAt(index)) } else { // draw stack values - let vals = values! + let vals = vals! var transformed = [CGPoint]() var posY = 0.0 @@ -427,22 +454,21 @@ public class BarChartRenderer: ChartDataRendererBase negY -= value } - transformed.append(CGPoint(x: 0.0, y: CGFloat(y) * animator.phaseY)) + transformed.append(CGPoint(x: 0.0, y: CGFloat(y * phaseY))) } trans.pointValuesToPixel(&transformed) for k in 0 ..< transformed.count { - let x = valuePoint.x let y = transformed[k].y + (vals[k] >= 0 ? posOffset : negOffset) - if (!viewPortHandler.isInBoundsRight(x)) + if !viewPortHandler.isInBoundsRight(x) { break } - if (!viewPortHandler.isInBoundsY(y) || !viewPortHandler.isInBoundsLeft(x)) + if !viewPortHandler.isInBoundsY(y) || !viewPortHandler.isInBoundsLeft(x) { continue } @@ -453,9 +479,11 @@ public class BarChartRenderer: ChartDataRendererBase yPos: y, font: valueFont, align: .Center, - color: dataSet.valueTextColorAt(j)) + color: dataSet.valueTextColorAt(index)) } } + + bufferIndex = vals == nil ? (bufferIndex + 1) : (bufferIndex + vals!.count) } } } @@ -473,20 +501,15 @@ public class BarChartRenderer: ChartDataRendererBase } - private var _highlightArrowPtsBuffer = [CGPoint](count: 3, repeatedValue: CGPoint()) - public override func drawHighlighted(context context: CGContext, indices: [ChartHighlight]) { guard let dataProvider = dataProvider, - barData = dataProvider.barData, - animator = animator + barData = dataProvider.barData else { return } CGContextSaveGState(context) - let setCount = barData.dataSetCount - let drawHighlightArrowEnabled = dataProvider.isDrawHighlightArrowEnabled var barRect = CGRect() for high in indices @@ -499,101 +522,51 @@ public class BarChartRenderer: ChartDataRendererBase { guard let set = barData.getDataSetByIndex(dataSetIndex) as? IBarChartDataSet else { continue } - if (!set.isHighlightEnabled) + if !set.isHighlightEnabled { continue } - let barspaceHalf = set.barSpace / 2.0 - let trans = dataProvider.getTransformer(set.axisDependency) CGContextSetFillColorWithColor(context, set.highlightColor.CGColor) CGContextSetAlpha(context, set.highlightAlpha) - let index = high.xIndex + let x = high.x - // check outofbounds - if (CGFloat(index) < (CGFloat(dataProvider.chartXMax) * animator.phaseX) / CGFloat(setCount)) + if let e = set.entryForXPos(x) as? BarChartDataEntry { - let e = set.entryForXIndex(index) as! BarChartDataEntry! - - if (e === nil || e.xIndex != index) + let entryIndex = set.entryIndex(entry: e) + if Double(entryIndex) > Double(set.entryCount) * (animator?.phaseX ?? 1.0) { continue } - - let groupspace = barData.groupSpace - let isStack = high.stackIndex < 0 ? false : true - - // calculate the correct x-position - let x = CGFloat(index * setCount + dataSetIndex) + groupspace / 2.0 + groupspace * CGFloat(index) + + let isStack = high.stackIndex >= 0 && e.isStacked let y1: Double let y2: Double - if (isStack) + if isStack { y1 = high.range?.from ?? 0.0 y2 = high.range?.to ?? 0.0 } else { - y1 = e.value + y1 = e.y y2 = 0.0 } - prepareBarHighlight(x: x, y1: y1, y2: y2, barspacehalf: barspaceHalf, trans: trans, rect: &barRect) + prepareBarHighlight(x: e.x, y1: y1, y2: y2, barWidthHalf: barData.barWidth / 2.0, trans: trans, rect: &barRect) + + // prepareBarHighlight(y1: y1, y2: y2, interval: interval, entryIndex: entryIndex, dataSetIndex: dataSetIndex, dataSetCount: setCount, barSpace: barSpace, groupSpace: groupSpace, trans: trans, rect: &barRect) CGContextFillRect(context, barRect) - - if (drawHighlightArrowEnabled) - { - CGContextSetAlpha(context, 1.0) - - // distance between highlight arrow and bar - let offsetY = animator.phaseY * 0.07 - - CGContextSaveGState(context) - - let pixelToValueMatrix = trans.pixelToValueMatrix - let xToYRel = abs(sqrt(pixelToValueMatrix.b * pixelToValueMatrix.b + pixelToValueMatrix.d * pixelToValueMatrix.d) / sqrt(pixelToValueMatrix.a * pixelToValueMatrix.a + pixelToValueMatrix.c * pixelToValueMatrix.c)) - - let arrowWidth = set.barSpace / 2.0 - let arrowHeight = arrowWidth * xToYRel - - let yArrow = (y1 > -y2 ? y1 : y1) * Double(animator.phaseY) - - _highlightArrowPtsBuffer[0].x = CGFloat(x) + 0.4 - _highlightArrowPtsBuffer[0].y = CGFloat(yArrow) + offsetY - _highlightArrowPtsBuffer[1].x = CGFloat(x) + 0.4 + arrowWidth - _highlightArrowPtsBuffer[1].y = CGFloat(yArrow) + offsetY - arrowHeight - _highlightArrowPtsBuffer[2].x = CGFloat(x) + 0.4 + arrowWidth - _highlightArrowPtsBuffer[2].y = CGFloat(yArrow) + offsetY + arrowHeight - - trans.pointValuesToPixel(&_highlightArrowPtsBuffer) - - CGContextBeginPath(context) - CGContextMoveToPoint(context, _highlightArrowPtsBuffer[0].x, _highlightArrowPtsBuffer[0].y) - CGContextAddLineToPoint(context, _highlightArrowPtsBuffer[1].x, _highlightArrowPtsBuffer[1].y) - CGContextAddLineToPoint(context, _highlightArrowPtsBuffer[2].x, _highlightArrowPtsBuffer[2].y) - CGContextClosePath(context) - - CGContextFillPath(context) - - CGContextRestoreGState(context) - } } } } CGContextRestoreGState(context) } - - internal func passesCheck() -> Bool - { - guard let dataProvider = dataProvider, barData = dataProvider.barData else { return false } - - return CGFloat(barData.yValCount) < CGFloat(dataProvider.maxVisibleValueCount) * viewPortHandler.scaleX - } } \ No newline at end of file diff --git a/Charts/Classes/Renderers/BarLineScatterCandleBubbleChartRenderer.swift b/Charts/Classes/Renderers/BarLineScatterCandleBubbleChartRenderer.swift new file mode 100644 index 0000000000..16166af679 --- /dev/null +++ b/Charts/Classes/Renderers/BarLineScatterCandleBubbleChartRenderer.swift @@ -0,0 +1,62 @@ +// +// BarLineScatterCandleBubbleChartRenderer.swift +// Charts +// +// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda +// A port of MPAndroidChart for iOS +// Licensed under Apache License 2.0 +// +// https://github.com/danielgindi/Charts +// + +import Foundation +import CoreGraphics + + +public class BarLineScatterCandleBubbleChartRenderer: ChartDataRendererBase +{ + public override init(animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) + { + super.init(animator: animator, viewPortHandler: viewPortHandler) + } + + /// Calculates and returns the x-bounds for the given DataSet in terms of index in their values array. + /// This includes minimum and maximum visible x, as well as range. + internal func xBounds(chart: BarLineScatterCandleBubbleChartDataProvider, + dataSet: IBarLineScatterCandleBubbleChartDataSet, + animator: ChartAnimator?) -> XBounds + { + return XBounds(chart: chart, dataSet: dataSet, animator: animator) + } + + /// Class representing the bounds of the current viewport in terms of indices in the values array of a DataSet. + public class XBounds + { + /// minimum visible entry index + public let min: Int + + /// maximum visible entry index + public let max: Int + + /// range of visible entry indices + public let range: Int + + public init(chart: BarLineScatterCandleBubbleChartDataProvider, + dataSet: IBarLineScatterCandleBubbleChartDataSet, + animator: ChartAnimator?) + { + let phaseX = Swift.max(0.0, Swift.min(1.0, animator?.phaseX ?? 1.0)) + + let low = chart.lowestVisibleX + let high = chart.highestVisibleX + + let entryFrom = dataSet.entryForXPos(low, rounding: ChartDataSetRounding.Down) + let entryTo = dataSet.entryForXPos(high, rounding: ChartDataSetRounding.Up) + + self.min = entryFrom == nil ? 0 : dataSet.entryIndex(entry: entryFrom!) + self.max = entryTo == nil ? 0 : dataSet.entryIndex(entry: entryTo!) + range = Int(Double(self.max - self.min) * phaseX) + } + } + +} \ No newline at end of file diff --git a/Charts/Classes/Renderers/BubbleChartRenderer.swift b/Charts/Classes/Renderers/BubbleChartRenderer.swift index 726fd137fc..cf3e1964ee 100644 --- a/Charts/Classes/Renderers/BubbleChartRenderer.swift +++ b/Charts/Classes/Renderers/BubbleChartRenderer.swift @@ -17,11 +17,11 @@ import CoreGraphics #endif -public class BubbleChartRenderer: ChartDataRendererBase +public class BubbleChartRenderer: BarLineScatterCandleBubbleChartRenderer { public weak var dataProvider: BubbleChartDataProvider? - public init(dataProvider: BubbleChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public init(dataProvider: BubbleChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) @@ -61,26 +61,18 @@ public class BubbleChartRenderer: ChartDataRendererBase { guard let dataProvider = dataProvider, + viewPortHandler = self.viewPortHandler, animator = animator else { return } let trans = dataProvider.getTransformer(dataSet.axisDependency) - let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY - let entryCount = dataSet.entryCount + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) let valueToPixelMatrix = trans.valueToPixelMatrix - - guard let - entryFrom = dataSet.entryForXIndex(self.minX), - entryTo = dataSet.entryForXIndex(self.maxX) - else { return } - - let minx = max(dataSet.entryIndex(entry: entryFrom), 0) - let maxx = min(dataSet.entryIndex(entry: entryTo) + 1, entryCount) - + _sizeBuffer[0].x = 0.0 _sizeBuffer[0].y = 0.0 _sizeBuffer[1].x = 1.0 @@ -97,12 +89,12 @@ public class BubbleChartRenderer: ChartDataRendererBase let maxBubbleHeight: CGFloat = abs(viewPortHandler.contentBottom - viewPortHandler.contentTop) let referenceSize: CGFloat = min(maxBubbleHeight, maxBubbleWidth) - for j in minx ..< maxx + for j in bounds.min ... bounds.range + bounds.min { guard let entry = dataSet.entryForIndex(j) as? BubbleChartDataEntry else { continue } - _pointBuffer.x = CGFloat(entry.xIndex - minx) * phaseX + CGFloat(minx) - _pointBuffer.y = CGFloat(entry.value) * phaseY + _pointBuffer.x = CGFloat(entry.x) + _pointBuffer.y = CGFloat(entry.y * phaseY) _pointBuffer = CGPointApplyAffineTransform(_pointBuffer, valueToPixelMatrix) let shapeSize = getShapeSize(entrySize: entry.size, maxSize: dataSet.maxSize, reference: referenceSize, normalizeSize: normalizeSize) @@ -124,7 +116,7 @@ public class BubbleChartRenderer: ChartDataRendererBase break } - let color = dataSet.colorAt(entry.xIndex) + let color = dataSet.colorAt(Int(entry.x)) let rect = CGRect( x: _pointBuffer.x - shapeHalf, @@ -144,12 +136,13 @@ public class BubbleChartRenderer: ChartDataRendererBase { guard let dataProvider = dataProvider, + viewPortHandler = self.viewPortHandler, bubbleData = dataProvider.bubbleData, animator = animator else { return } // if values are drawn - if (bubbleData.yValCount < Int(ceil(CGFloat(dataProvider.maxVisibleValueCount) * viewPortHandler.scaleX))) + if isDrawingValuesAllowed(dataProvider: dataProvider) { guard let dataSets = bubbleData.dataSets as? [IBubbleChartDataSet] else { return } @@ -169,27 +162,19 @@ public class BubbleChartRenderer: ChartDataRendererBase guard let formatter = dataSet.valueFormatter else { continue } + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) + let trans = dataProvider.getTransformer(dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix - let entryCount = dataSet.entryCount - - guard let - entryFrom = dataSet.entryForXIndex(self.minX), - entryTo = dataSet.entryForXIndex(self.maxX) - else { continue } - - let minx = max(dataSet.entryIndex(entry: entryFrom), 0) - let maxx = min(dataSet.entryIndex(entry: entryTo) + 1, entryCount) - - for j in minx ..< maxx + for j in bounds.min ... bounds.range + bounds.min { guard let e = dataSet.entryForIndex(j) as? BubbleChartDataEntry else { break } - let valueTextColor = dataSet.valueTextColorAt(j).colorWithAlphaComponent(alpha) + let valueTextColor = dataSet.valueTextColorAt(j).colorWithAlphaComponent(CGFloat(alpha)) - pt.x = CGFloat(e.xIndex - minx) * phaseX + CGFloat(minx) - pt.y = CGFloat(e.value) * phaseY + pt.x = CGFloat(e.x) + pt.y = CGFloat(e.y * phaseY) pt = CGPointApplyAffineTransform(pt, valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) @@ -230,13 +215,13 @@ public class BubbleChartRenderer: ChartDataRendererBase { guard let dataProvider = dataProvider, + viewPortHandler = self.viewPortHandler, bubbleData = dataProvider.bubbleData, animator = animator else { return } CGContextSaveGState(context) - let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY for high in indices @@ -251,19 +236,19 @@ public class BubbleChartRenderer: ChartDataRendererBase where dataSet.isHighlightEnabled else { continue } - let entries = dataSet.entriesForXIndex(high.xIndex) + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) + + let entries = dataSet.entriesForXPos(high.x) for entry in entries { guard let entry = entry as? BubbleChartDataEntry else { continue } - if !isnan(high.value) && entry.value != high.value { continue } - let entryFrom = dataSet.entryForXIndex(self.minX) - let entryTo = dataSet.entryForXIndex(self.maxX) + // FIXME: On Android, add y equals... - let minx = max(dataSet.entryIndex(entry: entryFrom!), 0) - let maxx = min(dataSet.entryIndex(entry: entryTo!) + 1, dataSet.entryCount) + // If `y` is NAN, then the selection is of any value on this x. Otherwise- it's a single, specific value selection. + if !isnan(high.y) && entry.y != high.y { continue } let trans = dataProvider.getTransformer(dataSet.axisDependency) @@ -281,8 +266,8 @@ public class BubbleChartRenderer: ChartDataRendererBase let maxBubbleHeight: CGFloat = abs(viewPortHandler.contentBottom - viewPortHandler.contentTop) let referenceSize: CGFloat = min(maxBubbleHeight, maxBubbleWidth) - _pointBuffer.x = CGFloat(entry.xIndex - minx) * phaseX + CGFloat(minx) - _pointBuffer.y = CGFloat(entry.value) * phaseY + _pointBuffer.x = CGFloat(entry.x) + _pointBuffer.y = CGFloat(entry.y * phaseY) trans.pointValueToPixel(&_pointBuffer) let shapeSize = getShapeSize(entrySize: entry.size, maxSize: dataSet.maxSize, reference: referenceSize, normalizeSize: normalizeSize) @@ -304,12 +289,12 @@ public class BubbleChartRenderer: ChartDataRendererBase break } - if (high.xIndex < minx || high.xIndex >= maxx) + if high.x < Double(bounds.min) || high.x >= Double(bounds.max) { continue } - let originalColor = dataSet.colorAt(entry.xIndex) + let originalColor = dataSet.colorAt(Int(entry.x)) var h: CGFloat = 0.0 var s: CGFloat = 0.0 diff --git a/Charts/Classes/Renderers/CandleStickChartRenderer.swift b/Charts/Classes/Renderers/CandleStickChartRenderer.swift index 7266ff5fb1..a6c86b6671 100644 --- a/Charts/Classes/Renderers/CandleStickChartRenderer.swift +++ b/Charts/Classes/Renderers/CandleStickChartRenderer.swift @@ -23,7 +23,7 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer { public weak var dataProvider: CandleChartDataProvider? - public init(dataProvider: CandleChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public init(dataProvider: CandleChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) @@ -53,35 +53,28 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer public func drawDataSet(context context: CGContext, dataSet: ICandleChartDataSet) { guard let - trans = dataProvider?.getTransformer(dataSet.axisDependency), + dataProvider = dataProvider, animator = animator else { return } + + let trans = dataProvider.getTransformer(dataSet.axisDependency) - let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY let barSpace = dataSet.barSpace let showCandleBar = dataSet.showCandleBar - let entryCount = dataSet.entryCount - - let minx = max(self.minX, 0) - let maxx = min(self.maxX + 1, entryCount) + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) CGContextSaveGState(context) CGContextSetLineWidth(context, dataSet.shadowWidth) - for j in minx.stride(to: Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx))), by: 1) + for j in bounds.min ... bounds.range + bounds.min { // get the entry guard let e = dataSet.entryForIndex(j) as? CandleChartDataEntry else { continue } - let xIndex = e.xIndex - - if (xIndex < minx || xIndex >= maxx) - { - continue - } + let xPos = e.x let open = e.open let close = e.close @@ -92,30 +85,30 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer { // calculate the shadow - _shadowPoints[0].x = CGFloat(xIndex) - _shadowPoints[1].x = CGFloat(xIndex) - _shadowPoints[2].x = CGFloat(xIndex) - _shadowPoints[3].x = CGFloat(xIndex) + _shadowPoints[0].x = CGFloat(xPos) + _shadowPoints[1].x = CGFloat(xPos) + _shadowPoints[2].x = CGFloat(xPos) + _shadowPoints[3].x = CGFloat(xPos) if (open > close) { - _shadowPoints[0].y = CGFloat(high) * phaseY - _shadowPoints[1].y = CGFloat(open) * phaseY - _shadowPoints[2].y = CGFloat(low) * phaseY - _shadowPoints[3].y = CGFloat(close) * phaseY + _shadowPoints[0].y = CGFloat(high * phaseY) + _shadowPoints[1].y = CGFloat(open * phaseY) + _shadowPoints[2].y = CGFloat(low * phaseY) + _shadowPoints[3].y = CGFloat(close * phaseY) } else if (open < close) { - _shadowPoints[0].y = CGFloat(high) * phaseY - _shadowPoints[1].y = CGFloat(close) * phaseY - _shadowPoints[2].y = CGFloat(low) * phaseY - _shadowPoints[3].y = CGFloat(open) * phaseY + _shadowPoints[0].y = CGFloat(high * phaseY) + _shadowPoints[1].y = CGFloat(close * phaseY) + _shadowPoints[2].y = CGFloat(low * phaseY) + _shadowPoints[3].y = CGFloat(open * phaseY) } else { - _shadowPoints[0].y = CGFloat(high) * phaseY - _shadowPoints[1].y = CGFloat(open) * phaseY - _shadowPoints[2].y = CGFloat(low) * phaseY + _shadowPoints[0].y = CGFloat(high * phaseY) + _shadowPoints[1].y = CGFloat(open * phaseY) + _shadowPoints[2].y = CGFloat(low * phaseY) _shadowPoints[3].y = _shadowPoints[1].y } @@ -150,10 +143,10 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer // calculate the body - _bodyRect.origin.x = CGFloat(xIndex) - 0.5 + barSpace - _bodyRect.origin.y = CGFloat(close) * phaseY - _bodyRect.size.width = (CGFloat(xIndex) + 0.5 - barSpace) - _bodyRect.origin.x - _bodyRect.size.height = (CGFloat(open) * phaseY) - _bodyRect.origin.y + _bodyRect.origin.x = CGFloat(xPos) - 0.5 + barSpace + _bodyRect.origin.y = CGFloat(close * phaseY) + _bodyRect.size.width = (CGFloat(xPos) + 0.5 - barSpace) - _bodyRect.origin.x + _bodyRect.size.height = CGFloat(open * phaseY) - _bodyRect.origin.y trans.rectValueToPixel(&_bodyRect) @@ -199,20 +192,20 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer } else { - _rangePoints[0].x = CGFloat(xIndex) - _rangePoints[0].y = CGFloat(high) * phaseY - _rangePoints[1].x = CGFloat(xIndex) - _rangePoints[1].y = CGFloat(low) * phaseY + _rangePoints[0].x = CGFloat(xPos) + _rangePoints[0].y = CGFloat(high * phaseY) + _rangePoints[1].x = CGFloat(xPos) + _rangePoints[1].y = CGFloat(low * phaseY) - _openPoints[0].x = CGFloat(xIndex) - 0.5 + barSpace - _openPoints[0].y = CGFloat(open) * phaseY - _openPoints[1].x = CGFloat(xIndex) - _openPoints[1].y = CGFloat(open) * phaseY + _openPoints[0].x = CGFloat(xPos) - 0.5 + barSpace + _openPoints[0].y = CGFloat(open * phaseY) + _openPoints[1].x = CGFloat(xPos) + _openPoints[1].y = CGFloat(open * phaseY) - _closePoints[0].x = CGFloat(xIndex) + 0.5 - barSpace - _closePoints[0].y = CGFloat(close) * phaseY - _closePoints[1].x = CGFloat(xIndex) - _closePoints[1].y = CGFloat(close) * phaseY + _closePoints[0].x = CGFloat(xPos) + 0.5 - barSpace + _closePoints[0].y = CGFloat(close * phaseY) + _closePoints[1].x = CGFloat(xPos) + _closePoints[1].y = CGFloat(close * phaseY) trans.pointValuesToPixel(&_rangePoints) trans.pointValuesToPixel(&_openPoints) @@ -248,23 +241,24 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer { guard let dataProvider = dataProvider, + viewPortHandler = self.viewPortHandler, candleData = dataProvider.candleData, animator = animator else { return } // if values are drawn - if (candleData.yValCount < Int(ceil(CGFloat(dataProvider.maxVisibleValueCount) * viewPortHandler.scaleX))) + if isDrawingValuesAllowed(dataProvider: dataProvider) { var dataSets = candleData.dataSets - let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY var pt = CGPoint() for i in 0 ..< dataSets.count { - let dataSet = dataSets[i] + guard let dataSet = dataSets[i] as? IBarLineScatterCandleBubbleChartDataSet + else { continue } if !dataSet.isDrawValuesEnabled || dataSet.entryCount == 0 { @@ -278,20 +272,17 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer let trans = dataProvider.getTransformer(dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix - let entryCount = dataSet.entryCount - - let minx = max(self.minX, 0) - let maxx = min(self.maxX + 1, entryCount) + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) let lineHeight = valueFont.lineHeight let yOffset: CGFloat = lineHeight + 5.0 - for j in minx.stride(to: Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx))), by: 1) + for j in bounds.min ..< Int(ceil(Double(bounds.max - bounds.min) * animator.phaseX)) { guard let e = dataSet.entryForIndex(j) as? CandleChartDataEntry else { break } - pt.x = CGFloat(e.xIndex) - pt.y = CGFloat(e.high) * phaseY + pt.x = CGFloat(e.x) + pt.y = CGFloat(e.high * phaseY) pt = CGPointApplyAffineTransform(pt, valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) @@ -321,8 +312,6 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer { } - private var _highlightPointBuffer = CGPoint() - public override func drawHighlighted(context context: CGContext, indices: [ChartHighlight]) { guard let @@ -348,14 +337,9 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer continue } - let xIndex = high.xIndex; // get the x-position - - guard let e = set.entryForXIndex(xIndex) as? CandleChartDataEntry else { continue } + let xPos = high.x; // get the x-position - if e.xIndex != xIndex - { - continue - } + guard let e = set.entryForXPos(xPos) as? CandleChartDataEntry else { continue } let trans = dataProvider.getTransformer(set.axisDependency) @@ -370,17 +354,14 @@ public class CandleStickChartRenderer: LineScatterCandleRadarChartRenderer CGContextSetLineDash(context, 0.0, nil, 0) } - let lowValue = CGFloat(e.low) * animator.phaseY - let highValue = CGFloat(e.high) * animator.phaseY + let lowValue = e.low * Double(animator.phaseY) + let highValue = e.high * Double(animator.phaseY) let y = (lowValue + highValue) / 2.0 - _highlightPointBuffer.x = CGFloat(xIndex) - _highlightPointBuffer.y = y - - trans.pointValueToPixel(&_highlightPointBuffer) + let pt = trans.pixelForValue(x: xPos, y: y) // draw the lines - drawHighlightLines(context: context, point: _highlightPointBuffer, set: set) + drawHighlightLines(context: context, point: pt, set: set) } } diff --git a/Charts/Classes/Renderers/ChartAxisRendererBase.swift b/Charts/Classes/Renderers/ChartAxisRendererBase.swift index 87a596dc80..7b60ddaa69 100644 --- a/Charts/Classes/Renderers/ChartAxisRendererBase.swift +++ b/Charts/Classes/Renderers/ChartAxisRendererBase.swift @@ -17,18 +17,23 @@ import CoreGraphics public class ChartAxisRendererBase: ChartRendererBase { - public var transformer: ChartTransformer! + /// base axis this axis renderer works with + public var axis: ChartAxisBase? + + /// transformer to transform values to screen pixels and return + public var transformer: ChartTransformer? public override init() { super.init() } - public init(viewPortHandler: ChartViewPortHandler, transformer: ChartTransformer!) + public init(viewPortHandler: ChartViewPortHandler?, transformer: ChartTransformer?, axis: ChartAxisBase?) { super.init(viewPortHandler: viewPortHandler) self.transformer = transformer + self.axis = axis } /// Draws the axis labels on the specified context @@ -54,4 +59,163 @@ public class ChartAxisRendererBase: ChartRendererBase { fatalError("renderLimitLines() cannot be called on ChartAxisRendererBase") } + + /// Computes the axis values. + /// - parameter min: the minimum value in the data object for this axis + /// - parameter max: the maximum value in the data object for this axis + public func computeAxis(min min: Double, max: Double, inverted: Bool) + { + var min = min, max = max + + if let transformer = self.transformer + { + // calculate the starting and entry point of the y-labels (depending on zoom / contentrect bounds) + if let viewPortHandler = viewPortHandler + { + if viewPortHandler.contentWidth > 10.0 && !viewPortHandler.isFullyZoomedOutY + { + let p1 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) + let p2 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)) + + if !inverted + { + min = Double(p2.y) + max = Double(p1.y) + } + else + { + min = Double(p1.y) + max = Double(p2.y) + } + } + } + } + + computeAxisValues(min: min, max: max) + } + + /// Sets up the axis values. Computes the desired number of labels between the two given extremes. + public func computeAxisValues(min min: Double, max: Double) + { + guard let axis = self.axis else { return } + + let yMin = min + let yMax = max + + let labelCount = axis.labelCount + let range = abs(yMax - yMin) + + if labelCount == 0 || range <= 0 + { + axis.entries = [Double]() + return + } + + // Find out how much spacing (in y value space) between axis values + let rawInterval = range / Double(labelCount) + var interval = ChartUtils.roundToNextSignificant(number: Double(rawInterval)) + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if axis.granularityEnabled + { + interval = interval < axis.granularity ? axis.granularity : interval + } + + // Normalize interval + let intervalMagnitude = ChartUtils.roundToNextSignificant(number: pow(10.0, Double(Int(log10(interval))))) + let intervalSigDigit = Int(interval / intervalMagnitude) + if intervalSigDigit > 5 + { + // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 + interval = floor(10.0 * Double(intervalMagnitude)) + } + + let centeringEnabled = axis.centerAxisLabelsEnabled + var n = centeringEnabled ? 1 : 0 + + // force label count + if axis.isForceLabelsEnabled + { + let step = Double(range) / Double(labelCount - 1) + + // Ensure stops contains at least n elements. + axis.entries.removeAll(keepCapacity: true) + axis.entries.reserveCapacity(labelCount) + + var v = yMin + + for _ in 0 ..< labelCount + { + axis.entries.append(v) + v += step + } + + n = labelCount + } + else + { + // no forced count + + var first = interval == 0.0 ? 0.0 : ceil(yMin / interval) * interval + + if centeringEnabled + { + first -= interval + } + + let last = interval == 0.0 ? 0.0 : ChartUtils.nextUp(floor(yMax / interval) * interval) + + if interval != 0.0 && last != first + { + for _ in first.stride(through: last, by: interval) + { + n += 1 + } + } + + // Ensure stops contains at least n elements. + axis.entries.removeAll(keepCapacity: true) + axis.entries.reserveCapacity(labelCount) + + var f = first + var i = 0 + while i < n + { + if f == 0.0 + { + // Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0) + f = 0.0 + } + + axis.entries.append(Double(f)) + + f += interval + i += 1 + } + } + + // set decimals + if interval < 1 + { + axis.decimals = Int(ceil(-log10(interval))) + } + else + { + axis.decimals = 0 + } + + if centeringEnabled + { + axis.centeredEntries.reserveCapacity(n) + axis.centeredEntries.removeAll() + + let offset = (axis.entries[1] - axis.entries[0]) / 2.0 + + for i in 0 ..< n + { + axis.centeredEntries.append(axis.entries[i] + offset) + } + } + } } \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartDataRendererBase.swift b/Charts/Classes/Renderers/ChartDataRendererBase.swift index 099fb8d675..56e74e9be0 100644 --- a/Charts/Classes/Renderers/ChartDataRendererBase.swift +++ b/Charts/Classes/Renderers/ChartDataRendererBase.swift @@ -18,7 +18,7 @@ public class ChartDataRendererBase: ChartRendererBase { public var animator: ChartAnimator? - public init(animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public init(animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(viewPortHandler: viewPortHandler) @@ -47,4 +47,16 @@ public class ChartDataRendererBase: ChartRendererBase { fatalError("drawHighlighted() cannot be called on ChartDataRendererBase") } + + /// An opportunity for initializing internal buffers used for rendering with a new size. + /// Since this might do memory allocations, it should only be called if necessary. + public func initBuffers() { } + + public func isDrawingValuesAllowed(dataProvider dataProvider: ChartDataProvider?) -> Bool + { + guard let data = dataProvider?.data + else { return false } + + return data.entryCount < Int(CGFloat(dataProvider?.maxVisibleCount ?? 0) * (viewPortHandler?.scaleX ?? 1.0)) + } } \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartLegendRenderer.swift b/Charts/Classes/Renderers/ChartLegendRenderer.swift index 26056b012f..c4af24026a 100755 --- a/Charts/Classes/Renderers/ChartLegendRenderer.swift +++ b/Charts/Classes/Renderers/ChartLegendRenderer.swift @@ -24,7 +24,7 @@ public class ChartLegendRenderer: ChartRendererBase /// the legend object this renderer renders public var legend: ChartLegend? - public init(viewPortHandler: ChartViewPortHandler, legend: ChartLegend?) + public init(viewPortHandler: ChartViewPortHandler?, legend: ChartLegend?) { super.init(viewPortHandler: viewPortHandler) @@ -34,7 +34,10 @@ public class ChartLegendRenderer: ChartRendererBase /// Prepares the legend and calculates all needed forms, labels and colors. public func computeLegend(data: ChartData) { - guard let legend = legend else { return } + guard let + legend = legend, + viewPortHandler = self.viewPortHandler + else { return } if (!legend.isLegendCustom) { @@ -70,12 +73,11 @@ public class ChartLegendRenderer: ChartRendererBase } else if (dataSet is IPieChartDataSet) { - var xVals = data.xVals let pds = dataSet as! IPieChartDataSet - for j in 0.. 10 && !viewPortHandler.isFullyZoomedOutX + { + let p1 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) + let p2 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)) + + if inverted + { + min = Double(p2.y) + max = Double(p1.y) + } + else + { + min = Double(p1.y) + max = Double(p2.y) + } + } } - let widthText = a as NSString + computeAxisValues(min: min, max: max); + } + + public override func computeAxisValues(min min: Double, max: Double) + { + super.computeAxisValues(min: min, max: max) + + computeSize() + } + + public func computeSize() + { + guard let + xAxis = self.axis as? ChartXAxis + else { return } + + let longest = xAxis.getLongestLabel() - let labelSize = widthText.sizeWithAttributes([NSFontAttributeName: xAxis.labelFont]) + let labelSize = longest.sizeWithAttributes([NSFontAttributeName: xAxis.labelFont]) let labelWidth = labelSize.width let labelHeight = labelSize.height @@ -56,13 +87,14 @@ public class ChartXAxisRenderer: ChartAxisRendererBase xAxis.labelHeight = labelHeight xAxis.labelRotatedWidth = labelRotatedSize.width xAxis.labelRotatedHeight = labelRotatedSize.height - - xAxis.values = xValues } public override func renderAxisLabels(context context: CGContext) { - guard let xAxis = xAxis else { return } + guard let + xAxis = self.axis as? ChartXAxis, + viewPortHandler = self.viewPortHandler + else { return } if (!xAxis.isEnabled || !xAxis.isDrawLabelsEnabled) { @@ -73,6 +105,7 @@ public class ChartXAxisRenderer: ChartAxisRendererBase if (xAxis.labelPosition == .Top) { + // FIXME: Anchor on Android drawLabels(context: context, pos: viewPortHandler.contentTop - yOffset, anchor: CGPoint(x: 0.5, y: 1.0)) } else if (xAxis.labelPosition == .TopInside) @@ -98,7 +131,10 @@ public class ChartXAxisRenderer: ChartAxisRendererBase public override func renderAxisLine(context context: CGContext) { - guard let xAxis = xAxis else { return } + guard let + xAxis = self.axis as? ChartXAxis, + viewPortHandler = self.viewPortHandler + else { return } if (!xAxis.isEnabled || !xAxis.isDrawAxisLineEnabled) { @@ -146,7 +182,11 @@ public class ChartXAxisRenderer: ChartAxisRendererBase /// draws the x-labels on the specified y-position public func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint) { - guard let xAxis = xAxis else { return } + guard let + xAxis = self.axis as? ChartXAxis, + viewPortHandler = self.viewPortHandler, + transformer = self.transformer + else { return } let paraStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle paraStyle.alignment = .Center @@ -156,6 +196,8 @@ public class ChartXAxisRenderer: ChartAxisRendererBase NSParagraphStyleAttributeName: paraStyle] let labelRotationAngleRadians = xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD + let centeringEnabled = xAxis.isCenterAxisLabelsEnabled + let valueToPixelMatrix = transformer.valueToPixelMatrix var position = CGPoint(x: 0.0, y: 0.0) @@ -167,26 +209,32 @@ public class ChartXAxisRenderer: ChartAxisRendererBase labelMaxSize.width = xAxis.wordWrapWidthPercent * valueToPixelMatrix.a } - for i in self.minX.stride(to: min(self.maxX + 1, xAxis.values.count), by: xAxis.axisLabelModulus) + let entries = xAxis.entries; + + for i in 0.stride(to: entries.count, by: 1) { - let label = xAxis.values[i] - if (label == nil) + if centeringEnabled { - continue + position.x = CGFloat(xAxis.centeredEntries[i]) + } + else + { + position.x = CGFloat(entries[i]) } - position.x = CGFloat(i) position.y = 0.0 position = CGPointApplyAffineTransform(position, valueToPixelMatrix) if (viewPortHandler.isInBoundsX(position.x)) { - let labelns = label! as NSString + let label = xAxis.valueFormatter?.stringForValue(xAxis.entries[i], axis: xAxis) ?? "" + + let labelns = label as NSString if (xAxis.isAvoidFirstLastClippingEnabled) { // avoid clipping of the last - if (i == xAxis.values.count - 1 && xAxis.values.count > 1) + if (i == xAxis.entryCount - 1 && xAxis.entryCount > 1) { let width = labelns.boundingRectWithSize(labelMaxSize, options: .UsesLineFragmentOrigin, attributes: labelAttrs, context: nil).size.width @@ -203,24 +251,47 @@ public class ChartXAxisRenderer: ChartAxisRendererBase } } - drawLabel(context: context, label: label!, xIndex: i, x: position.x, y: pos, attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians) + drawLabel(context: context, + formattedLabel: label, + x: position.x, + y: pos, + attributes: labelAttrs, + constrainedToSize: labelMaxSize, + anchor: anchor, + angleRadians: labelRotationAngleRadians) } } } - public func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, attributes: [String: NSObject], constrainedToSize: CGSize, anchor: CGPoint, angleRadians: CGFloat) + public func drawLabel( + context context: CGContext, + formattedLabel: String, + x: CGFloat, + y: CGFloat, + attributes: [String: NSObject], + constrainedToSize: CGSize, + anchor: CGPoint, + angleRadians: CGFloat) { - guard let xAxis = xAxis else { return } - - let formattedLabel = xAxis.valueFormatter?.stringForXValue(xIndex, original: label, viewPortHandler: viewPortHandler) ?? label - ChartUtils.drawMultilineText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, constrainedToSize: constrainedToSize, anchor: anchor, angleRadians: angleRadians) + ChartUtils.drawMultilineText( + context: context, + text: formattedLabel, + point: CGPoint(x: x, y: y), + attributes: attributes, + constrainedToSize: constrainedToSize, + anchor: anchor, + angleRadians: angleRadians) } private var _gridLineSegmentsBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()) public override func renderGridLines(context context: CGContext) { - guard let xAxis = xAxis else { return } + guard let + xAxis = self.axis as? ChartXAxis, + viewPortHandler = self.viewPortHandler, + transformer = self.transformer + else { return } if (!xAxis.isDrawGridLinesEnabled || !xAxis.isEnabled) { @@ -247,9 +318,11 @@ public class ChartXAxisRenderer: ChartAxisRendererBase var position = CGPoint(x: 0.0, y: 0.0) - for i in self.minX.stride(to: self.maxX, by: xAxis.axisLabelModulus) + let entries = xAxis.entries; + + for i in 0.stride(to: entries.count, by: 1) { - position.x = CGFloat(i) + position.x = CGFloat(entries[i]) position.y = 0.0 position = CGPointApplyAffineTransform(position, valueToPixelMatrix) @@ -269,7 +342,10 @@ public class ChartXAxisRenderer: ChartAxisRendererBase public override func renderLimitLines(context context: CGContext) { - guard let xAxis = xAxis else { return } + guard let + xAxis = self.axis as? ChartXAxis, + transformer = self.transformer + else { return } var limitLines = xAxis.limitLines @@ -308,6 +384,10 @@ public class ChartXAxisRenderer: ChartAxisRendererBase public func renderLimitLineLine(context context: CGContext, limitLine: ChartLimitLine, position: CGPoint) { + guard let + viewPortHandler = self.viewPortHandler + else { return } + _limitLineSegmentsBuffer[0].x = position.x _limitLineSegmentsBuffer[0].y = viewPortHandler.contentTop _limitLineSegmentsBuffer[1].x = position.x @@ -329,6 +409,10 @@ public class ChartXAxisRenderer: ChartAxisRendererBase public func renderLimitLineLabel(context context: CGContext, limitLine: ChartLimitLine, position: CGPoint, yOffset: CGFloat) { + guard let + viewPortHandler = self.viewPortHandler + else { return } + let label = limitLine.label // if drawing the limit-value label is enabled diff --git a/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift deleted file mode 100644 index d437912df1..0000000000 --- a/Charts/Classes/Renderers/ChartXAxisRendererBarChart.swift +++ /dev/null @@ -1,165 +0,0 @@ -// -// ChartXAxisRendererBarChart.swift -// Charts -// -// Created by Daniel Cohen Gindi on 3/3/15. -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation -import CoreGraphics - -#if !os(OSX) - import UIKit -#endif - - -public class ChartXAxisRendererBarChart: ChartXAxisRenderer -{ - public weak var chart: BarChartView? - - public init(viewPortHandler: ChartViewPortHandler, xAxis: ChartXAxis, transformer: ChartTransformer!, chart: BarChartView) - { - super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: transformer) - - self.chart = chart - } - - /// draws the x-labels on the specified y-position - public override func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint) - { - guard let - xAxis = xAxis, - barData = chart?.data as? BarChartData - else { return } - - let paraStyle = NSParagraphStyle.defaultParagraphStyle().mutableCopy() as! NSMutableParagraphStyle - paraStyle.alignment = .Center - - let labelAttrs = [NSFontAttributeName: xAxis.labelFont, - NSForegroundColorAttributeName: xAxis.labelTextColor, - NSParagraphStyleAttributeName: paraStyle] - let labelRotationAngleRadians = xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD - - let step = barData.dataSetCount - - let valueToPixelMatrix = transformer.valueToPixelMatrix - - var position = CGPoint(x: 0.0, y: 0.0) - - var labelMaxSize = CGSize() - - if (xAxis.isWordWrapEnabled) - { - labelMaxSize.width = xAxis.wordWrapWidthPercent * valueToPixelMatrix.a - } - - for i in self.minX.stride(to: min(self.maxX + 1, xAxis.values.count), by: xAxis.axisLabelModulus) - { - let label = i >= 0 && i < xAxis.values.count ? xAxis.values[i] : nil - if (label == nil) - { - continue - } - - position.x = CGFloat(i * step) + CGFloat(i) * barData.groupSpace + barData.groupSpace / 2.0 - position.y = 0.0 - - // consider groups (center label for each group) - if (step > 1) - { - position.x += (CGFloat(step) - 1.0) / 2.0 - } - - position = CGPointApplyAffineTransform(position, valueToPixelMatrix) - - if (viewPortHandler.isInBoundsX(position.x)) - { - if (xAxis.isAvoidFirstLastClippingEnabled) - { - // avoid clipping of the last - if (i == xAxis.values.count - 1) - { - let width = label!.sizeWithAttributes(labelAttrs).width - - if (position.x + width / 2.0 > viewPortHandler.contentRight) - { - position.x = viewPortHandler.contentRight - (width / 2.0) - } - } - else if (i == 0) - { // avoid clipping of the first - let width = label!.sizeWithAttributes(labelAttrs).width - - if (position.x - width / 2.0 < viewPortHandler.contentLeft) - { - position.x = viewPortHandler.contentLeft + (width / 2.0) - } - } - } - - drawLabel(context: context, label: label!, xIndex: i, x: position.x, y: pos, attributes: labelAttrs, constrainedToSize: labelMaxSize, anchor: anchor, angleRadians: labelRotationAngleRadians) - } - } - } - - private var _gridLineSegmentsBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()) - - public override func renderGridLines(context context: CGContext) - { - guard let - xAxis = xAxis, - barData = chart?.data as? BarChartData - else { return } - - if (!xAxis.isDrawGridLinesEnabled || !xAxis.isEnabled) - { - return - } - - let step = barData.dataSetCount - - CGContextSaveGState(context) - - CGContextSetShouldAntialias(context, xAxis.gridAntialiasEnabled) - CGContextSetStrokeColorWithColor(context, xAxis.gridColor.CGColor) - CGContextSetLineWidth(context, xAxis.gridLineWidth) - CGContextSetLineCap(context, xAxis.gridLineCap) - - if (xAxis.gridLineDashLengths != nil) - { - CGContextSetLineDash(context, xAxis.gridLineDashPhase, xAxis.gridLineDashLengths, xAxis.gridLineDashLengths.count) - } - else - { - CGContextSetLineDash(context, 0.0, nil, 0) - } - - let valueToPixelMatrix = transformer.valueToPixelMatrix - - var position = CGPoint(x: 0.0, y: 0.0) - - for i in self.minX.stride(to: self.maxX, by: xAxis.axisLabelModulus) - { - position.x = CGFloat(i * step) + CGFloat(i) * barData.groupSpace - 0.5 - position.y = 0.0 - position = CGPointApplyAffineTransform(position, valueToPixelMatrix) - - if (viewPortHandler.isInBoundsX(position.x)) - { - _gridLineSegmentsBuffer[0].x = position.x - _gridLineSegmentsBuffer[0].y = viewPortHandler.contentTop - _gridLineSegmentsBuffer[1].x = position.x - _gridLineSegmentsBuffer[1].y = viewPortHandler.contentBottom - CGContextStrokeLineSegments(context, _gridLineSegmentsBuffer, 2) - } - } - - CGContextRestoreGState(context) - } -} \ No newline at end of file diff --git a/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift index 83ad7a61ca..e0ffd24a5f 100644 --- a/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift +++ b/Charts/Classes/Renderers/ChartXAxisRendererHorizontalBarChart.swift @@ -19,18 +19,55 @@ import CoreGraphics #endif -public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart +public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRenderer { - public override init(viewPortHandler: ChartViewPortHandler, xAxis: ChartXAxis, transformer: ChartTransformer!, chart: BarChartView) + internal var chart: BarChartView? + + public init(viewPortHandler: ChartViewPortHandler?, xAxis: ChartXAxis?, transformer: ChartTransformer?, chart: BarChartView?) { - super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: transformer, chart: chart) + super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: transformer) + + self.chart = chart } - public override func computeAxis(xValAverageLength xValAverageLength: Double, xValues: [String?]) + public override func computeAxis(min min: Double, max: Double, inverted: Bool) { - guard let xAxis = xAxis else { return } + guard let + viewPortHandler = self.viewPortHandler + else { return } + + var min = min, max = max - xAxis.values = xValues + if let transformer = self.transformer + { + // calculate the starting and entry point of the y-labels (depending on + // zoom / contentrect bounds) + if viewPortHandler.contentWidth > 10 && !viewPortHandler.isFullyZoomedOutX + { + let p1 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)) + let p2 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) + + if inverted + { + min = Double(p2.y) + max = Double(p1.y) + } + else + { + min = Double(p1.y) + max = Double(p2.y) + } + } + } + + computeAxisValues(min: min, max: max); + } + + public override func computeSize() + { + guard let + xAxis = self.axis as? ChartXAxis + else { return } let longest = xAxis.getLongestLabel() as NSString @@ -49,7 +86,10 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart public override func renderAxisLabels(context context: CGContext) { - guard let xAxis = xAxis else { return } + guard let + xAxis = self.axis as? ChartXAxis, + viewPortHandler = self.viewPortHandler + else { return } if !xAxis.isEnabled || !xAxis.isDrawLabelsEnabled || chart?.data === nil { @@ -85,52 +125,70 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart public override func drawLabels(context context: CGContext, pos: CGFloat, anchor: CGPoint) { guard let - xAxis = xAxis, - bd = chart?.data as? BarChartData + xAxis = self.axis as? ChartXAxis, + transformer = self.transformer, + viewPortHandler = self.viewPortHandler else { return } let labelFont = xAxis.labelFont let labelTextColor = xAxis.labelTextColor let labelRotationAngleRadians = xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD + let centeringEnabled = xAxis.isCenterAxisLabelsEnabled + // pre allocate to save performance (dont allocate in loop) var position = CGPoint(x: 0.0, y: 0.0) - let step = bd.dataSetCount - - for i in self.minX.stride(to: min(self.maxX + 1, xAxis.values.count), by: xAxis.axisLabelModulus) + for i in 0.stride(to: xAxis.entryCount, by: 1) { - let label = xAxis.values[i] - - if (label == nil) - { - continue - } + // only fill x values position.x = 0.0 - position.y = CGFloat(i * step) + CGFloat(i) * bd.groupSpace + bd.groupSpace / 2.0 - // consider groups (center label for each group) - if (step > 1) + if centeringEnabled + { + position.y = CGFloat(xAxis.centeredEntries[i]) + } + else { - position.y += (CGFloat(step) - 1.0) / 2.0 + position.y = CGFloat(xAxis.entries[i]) } transformer.pointValueToPixel(&position) - if (viewPortHandler.isInBoundsY(position.y)) + if viewPortHandler.isInBoundsY(position.y) { - drawLabel(context: context, label: label!, xIndex: i, x: pos, y: position.y, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor], anchor: anchor, angleRadians: labelRotationAngleRadians) + if let label = xAxis.valueFormatter?.stringForValue(xAxis.entries[i], axis: xAxis) + { + drawLabel( + context: context, + formattedLabel: label, + x: pos, + y: position.y, + attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor], + anchor: anchor, + angleRadians: labelRotationAngleRadians) + } } } } - public func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, attributes: [String: NSObject], anchor: CGPoint, angleRadians: CGFloat) + public func drawLabel( + context context: CGContext, + formattedLabel: String, + x: CGFloat, + y: CGFloat, + attributes: [String: NSObject], + anchor: CGPoint, + angleRadians: CGFloat) { - guard let xAxis = xAxis else { return } - - let formattedLabel = xAxis.valueFormatter?.stringForXValue(xIndex, original: label, viewPortHandler: viewPortHandler) ?? label - ChartUtils.drawText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, anchor: anchor, angleRadians: angleRadians) + ChartUtils.drawText( + context: context, + text: formattedLabel, + point: CGPoint(x: x, y: y), + attributes: attributes, + anchor: anchor, + angleRadians: angleRadians) } private var _gridLineSegmentsBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()) @@ -138,8 +196,9 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart public override func renderGridLines(context context: CGContext) { guard let - xAxis = xAxis, - bd = chart?.data as? BarChartData + xAxis = self.axis as? ChartXAxis, + viewPortHandler = self.viewPortHandler, + transformer = self.transformer else { return } if !xAxis.isEnabled || !xAxis.isDrawGridLinesEnabled @@ -163,19 +222,17 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart CGContextSetLineDash(context, 0.0, nil, 0) } + // pre allocate to save performance (dont allocate in loop) var position = CGPoint(x: 0.0, y: 0.0) - // take into consideration that multiple DataSets increase _deltaX - let step = bd.dataSetCount - - for i in self.minX.stride(to: min(self.maxX + 1, xAxis.values.count), by: xAxis.axisLabelModulus) + for i in 0.stride(to: xAxis.entryCount, by: 1) { position.x = 0.0 - position.y = CGFloat(i * step) + CGFloat(i) * bd.groupSpace - 0.5 + position.y = CGFloat(xAxis.entries[i]) transformer.pointValueToPixel(&position) - if (viewPortHandler.isInBoundsY(position.y)) + if viewPortHandler.isInBoundsY(position.y) { _gridLineSegmentsBuffer[0].x = viewPortHandler.contentLeft _gridLineSegmentsBuffer[0].y = position.y @@ -192,7 +249,10 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart public override func renderAxisLine(context context: CGContext) { - guard let xAxis = xAxis else { return } + guard let + xAxis = self.axis as? ChartXAxis, + viewPortHandler = self.viewPortHandler + else { return } if (!xAxis.isEnabled || !xAxis.isDrawAxisLineEnabled) { @@ -241,7 +301,11 @@ public class ChartXAxisRendererHorizontalBarChart: ChartXAxisRendererBarChart public override func renderLimitLines(context context: CGContext) { - guard let xAxis = xAxis else { return } + guard let + xAxis = self.axis as? ChartXAxis, + viewPortHandler = self.viewPortHandler, + transformer = self.transformer + else { return } var limitLines = xAxis.limitLines diff --git a/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift b/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift index b8cbf03d1b..720804f58d 100644 --- a/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift +++ b/Charts/Classes/Renderers/ChartXAxisRendererRadarChart.swift @@ -23,7 +23,7 @@ public class ChartXAxisRendererRadarChart: ChartXAxisRenderer { public weak var chart: RadarChartView? - public init(viewPortHandler: ChartViewPortHandler, xAxis: ChartXAxis, chart: RadarChartView) + public init(viewPortHandler: ChartViewPortHandler?, xAxis: ChartXAxis?, chart: RadarChartView?) { super.init(viewPortHandler: viewPortHandler, xAxis: xAxis, transformer: nil) @@ -33,7 +33,7 @@ public class ChartXAxisRendererRadarChart: ChartXAxisRenderer public override func renderAxisLabels(context context: CGContext) { guard let - xAxis = xAxis, + xAxis = axis as? ChartXAxis, chart = chart else { return } @@ -45,7 +45,7 @@ public class ChartXAxisRendererRadarChart: ChartXAxisRenderer let labelFont = xAxis.labelFont let labelTextColor = xAxis.labelTextColor let labelRotationAngleRadians = xAxis.labelRotationAngle * ChartUtils.Math.FDEG2RAD - let drawLabelAnchor = CGPoint(x: 0.5, y: 0.0) + let drawLabelAnchor = CGPoint(x: 0.5, y: 0.25) let sliceangle = chart.sliceAngle @@ -54,30 +54,41 @@ public class ChartXAxisRendererRadarChart: ChartXAxisRenderer let center = chart.centerOffsets - let modulus = xAxis.axisLabelModulus - for i in 0.stride(to: xAxis.values.count, by: modulus) + for i in 0.stride(to: chart.data?.maxEntryCountSet?.entryCount ?? 0, by: 1) { - let label = xAxis.values[i] - if (label == nil) - { - continue - } + let label = xAxis.valueFormatter?.stringForValue(Double(i), axis: xAxis) ?? "" let angle = (sliceangle * CGFloat(i) + chart.rotationAngle) % 360.0 let p = ChartUtils.getPosition(center: center, dist: CGFloat(chart.yRange) * factor + xAxis.labelRotatedWidth / 2.0, angle: angle) - drawLabel(context: context, label: label!, xIndex: i, x: p.x, y: p.y - xAxis.labelRotatedHeight / 2.0, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor], anchor: drawLabelAnchor, angleRadians: labelRotationAngleRadians) + drawLabel(context: context, + formattedLabel: label, + x: p.x, + y: p.y - xAxis.labelRotatedHeight / 2.0, + attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor], + anchor: drawLabelAnchor, + angleRadians: labelRotationAngleRadians) } } - public func drawLabel(context context: CGContext, label: String, xIndex: Int, x: CGFloat, y: CGFloat, attributes: [String: NSObject], anchor: CGPoint, angleRadians: CGFloat) + public func drawLabel( + context context: CGContext, + formattedLabel: String, + x: CGFloat, + y: CGFloat, + attributes: [String: NSObject], + anchor: CGPoint, + angleRadians: CGFloat) { - guard let xAxis = xAxis else { return } - - let formattedLabel = xAxis.valueFormatter?.stringForXValue(xIndex, original: label, viewPortHandler: viewPortHandler) ?? label - ChartUtils.drawText(context: context, text: formattedLabel, point: CGPoint(x: x, y: y), attributes: attributes, anchor: anchor, angleRadians: angleRadians) + ChartUtils.drawText( + context: context, + text: formattedLabel, + point: CGPoint(x: x, y: y), + attributes: attributes, + anchor: anchor, + angleRadians: angleRadians) } public override func renderLimitLines(context context: CGContext) diff --git a/Charts/Classes/Renderers/ChartYAxisRenderer.swift b/Charts/Classes/Renderers/ChartYAxisRenderer.swift index d0aac57b98..9bf976784a 100644 --- a/Charts/Classes/Renderers/ChartYAxisRenderer.swift +++ b/Charts/Classes/Renderers/ChartYAxisRenderer.swift @@ -21,162 +21,20 @@ import CoreGraphics public class ChartYAxisRenderer: ChartAxisRendererBase { - public var yAxis: ChartYAxis? + // FIXME: Remove yAxis property on Android - public init(viewPortHandler: ChartViewPortHandler, yAxis: ChartYAxis, transformer: ChartTransformer!) + public init(viewPortHandler: ChartViewPortHandler?, yAxis: ChartYAxis?, transformer: ChartTransformer?) { - super.init(viewPortHandler: viewPortHandler, transformer: transformer) - - self.yAxis = yAxis - } - - /// Computes the axis values. - public func computeAxis(yMin yMin: Double, yMax: Double) - { - guard let yAxis = yAxis else { return } - var yMin = yMin, yMax = yMax - - // calculate the starting and entry point of the y-labels (depending on - // zoom / contentrect bounds) - if (viewPortHandler.contentWidth > 10.0 && !viewPortHandler.isFullyZoomedOutY) - { - let p1 = transformer.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) - let p2 = transformer.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentBottom)) - - if (!yAxis.isInverted) - { - yMin = Double(p2.y) - yMax = Double(p1.y) - } - else - { - yMin = Double(p1.y) - yMax = Double(p2.y) - } - } - - computeAxisValues(min: yMin, max: yMax) - } - - /// Sets up the y-axis labels. Computes the desired number of labels between - /// the two given extremes. Unlike the papareXLabels() method, this method - /// needs to be called upon every refresh of the view. - public func computeAxisValues(min min: Double, max: Double) - { - guard let yAxis = yAxis else { return } - - let yMin = min - let yMax = max - - let labelCount = yAxis.labelCount - let range = abs(yMax - yMin) - - if (labelCount == 0 || range <= 0) - { - yAxis.entries = [Double]() - return - } - - // Find out how much spacing (in y value space) between axis values - let rawInterval = range / Double(labelCount) - var interval = ChartUtils.roundToNextSignificant(number: Double(rawInterval)) - - // If granularity is enabled, then do not allow the interval to go below specified granularity. - // This is used to avoid repeated values when rounding values for display. - if yAxis.granularityEnabled - { - interval = interval < yAxis.granularity ? yAxis.granularity : interval - } - - // Normalize interval - let intervalMagnitude = ChartUtils.roundToNextSignificant(number: pow(10.0, Double(Int(log10(interval))))) - let intervalSigDigit = Int(interval / intervalMagnitude) - if (intervalSigDigit > 5) - { - // Use one order of magnitude higher, to avoid intervals like 0.9 or 90 - interval = floor(10.0 * Double(intervalMagnitude)) - } - - // force label count - if yAxis.isForceLabelsEnabled - { - let step = Double(range) / Double(labelCount - 1) - - if yAxis.entries.count < labelCount - { - // Ensure stops contains at least numStops elements. - yAxis.entries.removeAll(keepCapacity: true) - } - else - { - yAxis.entries = [Double]() - yAxis.entries.reserveCapacity(labelCount) - } - - var v = yMin - - for _ in 0 ..< labelCount - { - yAxis.entries.append(v) - v += step - } - - } - else - { - // no forced count - - // if the labels should only show min and max - if (yAxis.isShowOnlyMinMaxEnabled) - { - yAxis.entries = [yMin, yMax] - } - else - { - let first = interval == 0.0 ? 0.0 : ceil(Double(yMin) / interval) * interval - let last = interval == 0.0 ? 0.0 : ChartUtils.nextUp(floor(Double(yMax) / interval) * interval) - - var n = 0 - if interval != 0.0 && last != first - { - for _ in first.stride(through: last, by: interval) - { - n += 1 - } - } - - if (yAxis.entries.count < n) - { - // Ensure stops contains at least numStops elements. - yAxis.entries = [Double](count: n, repeatedValue: 0.0) - } - else if (yAxis.entries.count > n) - { - yAxis.entries.removeRange(n.. [CGPoint] + { + guard let + yAxis = self.axis as? ChartYAxis, + transformer = self.transformer + else { return [CGPoint]() } + + var positions = [CGPoint]() + positions.reserveCapacity(yAxis.entryCount) + + let entries = yAxis.entries + + for i in 0.stride(to: yAxis.entryCount, by: 1) + { + positions.append(CGPoint(x: 0.0, y: entries[i])) + } + + transformer.pointValuesToPixel(&positions) + + return positions + } + /// Draws the zero line at the specified position. - public func drawZeroLine( - context context: CGContext, - x1: CGFloat, - x2: CGFloat, - y1: CGFloat, - y2: CGFloat) + public func drawZeroLine(context context: CGContext) { guard let - yAxis = yAxis, + yAxis = self.axis as? ChartYAxis, + viewPortHandler = self.viewPortHandler, + transformer = self.transformer, zeroLineColor = yAxis.zeroLineColor else { return } @@ -383,7 +269,9 @@ public class ChartYAxisRenderer: ChartAxisRendererBase CGContextSetStrokeColorWithColor(context, zeroLineColor.CGColor) CGContextSetLineWidth(context, yAxis.zeroLineWidth) - if (yAxis.zeroLineDashLengths != nil) + let pos = transformer.pixelForValue(x: 0.0, y: 0.0) + + if yAxis.zeroLineDashLengths != nil { CGContextSetLineDash(context, yAxis.zeroLineDashPhase, yAxis.zeroLineDashLengths!, yAxis.zeroLineDashLengths!.count) } @@ -392,8 +280,8 @@ public class ChartYAxisRenderer: ChartAxisRendererBase CGContextSetLineDash(context, 0.0, nil, 0) } - CGContextMoveToPoint(context, x1, y1) - CGContextAddLineToPoint(context, x2, y2) + CGContextMoveToPoint(context, viewPortHandler.contentLeft, pos.y - 1.0) + CGContextAddLineToPoint(context, viewPortHandler.contentRight, pos.y - 1.0) CGContextDrawPath(context, CGPathDrawingMode.Stroke) CGContextRestoreGState(context) @@ -403,7 +291,11 @@ public class ChartYAxisRenderer: ChartAxisRendererBase public override func renderLimitLines(context context: CGContext) { - guard let yAxis = yAxis else { return } + guard let + yAxis = self.axis as? ChartYAxis, + viewPortHandler = self.viewPortHandler, + transformer = self.transformer + else { return } var limitLines = yAxis.limitLines diff --git a/Charts/Classes/Renderers/ChartYAxisRendererHorizontalBarChart.swift b/Charts/Classes/Renderers/ChartYAxisRendererHorizontalBarChart.swift index e532c084c0..df78d26952 100644 --- a/Charts/Classes/Renderers/ChartYAxisRendererHorizontalBarChart.swift +++ b/Charts/Classes/Renderers/ChartYAxisRendererHorizontalBarChart.swift @@ -21,59 +21,55 @@ import CoreGraphics public class ChartYAxisRendererHorizontalBarChart: ChartYAxisRenderer { - public override init(viewPortHandler: ChartViewPortHandler, yAxis: ChartYAxis, transformer: ChartTransformer!) + public override init(viewPortHandler: ChartViewPortHandler?, yAxis: ChartYAxis?, transformer: ChartTransformer?) { super.init(viewPortHandler: viewPortHandler, yAxis: yAxis, transformer: transformer) } /// Computes the axis values. - public override func computeAxis(yMin yMin: Double, yMax: Double) + public override func computeAxis(min min: Double, max: Double, inverted: Bool) { - guard let yAxis = yAxis else { return } + guard let + viewPortHandler = self.viewPortHandler, + transformer = self.transformer + else { return } - var yMin = yMin, yMax = yMax + var min = min, max = max // calculate the starting and entry point of the y-labels (depending on zoom / contentrect bounds) - if (viewPortHandler.contentHeight > 10.0 && !viewPortHandler.isFullyZoomedOutX) + if viewPortHandler.contentHeight > 10.0 && !viewPortHandler.isFullyZoomedOutX { - let p1 = transformer.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) - let p2 = transformer.getValueByTouchPoint(CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)) + let p1 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentLeft, y: viewPortHandler.contentTop)) + let p2 = transformer.valueForTouchPoint(CGPoint(x: viewPortHandler.contentRight, y: viewPortHandler.contentTop)) - if (!yAxis.isInverted) + if !inverted { - yMin = Double(p1.x) - yMax = Double(p2.x) + min = Double(p1.x) + max = Double(p2.x) } else { - yMin = Double(p2.x) - yMax = Double(p1.x) + min = Double(p2.x) + max = Double(p1.x) } } - computeAxisValues(min: yMin, max: yMax) + computeAxisValues(min: min, max: max) } /// draws the y-axis labels to the screen public override func renderAxisLabels(context context: CGContext) { - guard let yAxis = yAxis else { return } + guard let + yAxis = axis as? ChartYAxis, + viewPortHandler = self.viewPortHandler + else { return } if (!yAxis.isEnabled || !yAxis.isDrawLabelsEnabled) { return } - var positions = [CGPoint]() - positions.reserveCapacity(yAxis.entries.count) - - for i in 0 ..< yAxis.entries.count - { - positions.append(CGPoint(x: CGFloat(yAxis.entries[i]), y: 0.0)) - } - - transformer.pointValuesToPixel(&positions) - let lineHeight = yAxis.labelFont.lineHeight let baseYOffset: CGFloat = 2.5 @@ -82,7 +78,7 @@ public class ChartYAxisRendererHorizontalBarChart: ChartYAxisRenderer var yPos: CGFloat = 0.0 - if (dependency == .Left) + if dependency == .Left { if (labelPosition == .OutsideChart) { @@ -109,14 +105,21 @@ public class ChartYAxisRendererHorizontalBarChart: ChartYAxisRenderer // And here we pull the line back up yPos -= lineHeight - drawYLabels(context: context, fixedPosition: yPos, positions: positions, offset: yAxis.yOffset) + drawYLabels( + context: context, + fixedPosition: yPos, + positions: transformedPositions(), + offset: yAxis.yOffset) } private var _axisLineSegmentsBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()) public override func renderAxisLine(context context: CGContext) { - guard let yAxis = yAxis else { return } + guard let + yAxis = axis as? ChartYAxis, + viewPortHandler = self.viewPortHandler + else { return } if (!yAxis.isEnabled || !yAxis.drawAxisLineEnabled) { @@ -157,9 +160,15 @@ public class ChartYAxisRendererHorizontalBarChart: ChartYAxisRenderer } /// draws the y-labels on the specified x-position - public func drawYLabels(context context: CGContext, fixedPosition: CGFloat, positions: [CGPoint], offset: CGFloat) + public func drawYLabels( + context context: CGContext, + fixedPosition: CGFloat, + positions: [CGPoint], + offset: CGFloat) { - guard let yAxis = yAxis else { return } + guard let + yAxis = axis as? ChartYAxis + else { return } let labelFont = yAxis.labelFont let labelTextColor = yAxis.labelTextColor @@ -173,76 +182,97 @@ public class ChartYAxisRendererHorizontalBarChart: ChartYAxisRenderer return } - ChartUtils.drawText(context: context, text: text, point: CGPoint(x: positions[i].x, y: fixedPosition - offset), align: .Center, attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]) + ChartUtils.drawText( + context: context, + text: text, + point: CGPoint(x: positions[i].x, y: fixedPosition - offset), + align: .Center, + attributes: [NSFontAttributeName: labelFont, NSForegroundColorAttributeName: labelTextColor]) } } - - public override func renderGridLines(context context: CGContext) + + private var _gridLineBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()) + + public override func drawGridLine( + context context: CGContextRef, + position: CGPoint) + { + guard let + viewPortHandler = self.viewPortHandler + else { return } + + _gridLineBuffer[0].x = position.x + _gridLineBuffer[0].y = viewPortHandler.contentTop + _gridLineBuffer[1].x = position.x + _gridLineBuffer[1].y = viewPortHandler.contentBottom + + CGContextStrokeLineSegments(context, _gridLineBuffer, 2) + } + + public override func transformedPositions() -> [CGPoint] { - guard let yAxis = yAxis else { return } + guard let + yAxis = self.axis as? ChartYAxis, + transformer = self.transformer + else { return [CGPoint]() } + + var positions = [CGPoint]() + positions.reserveCapacity(yAxis.entryCount) - if !yAxis.isEnabled + let entries = yAxis.entries + + for i in 0.stride(to: yAxis.entryCount, by: 1) { - return + positions.append(CGPoint(x: entries[i], y: 0.0)) } - if yAxis.isDrawGridLinesEnabled + transformer.pointValuesToPixel(&positions) + + return positions + } + + /// Draws the zero line at the specified position. + public override func drawZeroLine(context context: CGContext) + { + guard let + yAxis = self.axis as? ChartYAxis, + viewPortHandler = self.viewPortHandler, + transformer = self.transformer, + zeroLineColor = yAxis.zeroLineColor + else { return } + + CGContextSaveGState(context) + + CGContextSetStrokeColorWithColor(context, zeroLineColor.CGColor) + CGContextSetLineWidth(context, yAxis.zeroLineWidth) + + let pos = transformer.pixelForValue(x: 0.0, y: 0.0) + + if yAxis.zeroLineDashLengths != nil { - CGContextSaveGState(context) - - // pre alloc - var position = CGPoint() - - CGContextSetShouldAntialias(context, yAxis.gridAntialiasEnabled) - CGContextSetStrokeColorWithColor(context, yAxis.gridColor.CGColor) - CGContextSetLineWidth(context, yAxis.gridLineWidth) - CGContextSetLineCap(context, yAxis.gridLineCap) - - if (yAxis.gridLineDashLengths != nil) - { - CGContextSetLineDash(context, yAxis.gridLineDashPhase, yAxis.gridLineDashLengths, yAxis.gridLineDashLengths.count) - } - else - { - CGContextSetLineDash(context, 0.0, nil, 0) - } - - // draw the horizontal grid - for i in 0 ..< yAxis.entryCount - { - position.x = CGFloat(yAxis.entries[i]) - position.y = 0.0 - transformer.pointValueToPixel(&position) - - CGContextBeginPath(context) - CGContextMoveToPoint(context, position.x, viewPortHandler.contentTop) - CGContextAddLineToPoint(context, position.x, viewPortHandler.contentBottom) - CGContextStrokePath(context) - } - - CGContextRestoreGState(context) + CGContextSetLineDash(context, yAxis.zeroLineDashPhase, yAxis.zeroLineDashLengths!, yAxis.zeroLineDashLengths!.count) } - - if yAxis.drawZeroLineEnabled + else { - // draw zero line - - var position = CGPoint(x: 0.0, y: 0.0) - transformer.pointValueToPixel(&position) - - drawZeroLine(context: context, - x1: position.x, - x2: position.x, - y1: viewPortHandler.contentTop, - y2: viewPortHandler.contentBottom); + CGContextSetLineDash(context, 0.0, nil, 0) } + + CGContextMoveToPoint(context, pos.x - 1.0, viewPortHandler.contentTop) + CGContextAddLineToPoint(context, pos.x - 1.0, viewPortHandler.contentBottom) + CGContextDrawPath(context, CGPathDrawingMode.Stroke) + + CGContextRestoreGState(context) } private var _limitLineSegmentsBuffer = [CGPoint](count: 2, repeatedValue: CGPoint()) public override func renderLimitLines(context context: CGContext) { - guard let yAxis = yAxis else { return } + guard let + yAxis = axis as? ChartYAxis, + viewPortHandler = self.viewPortHandler, + transformer = self.transformer + else { return } var limitLines = yAxis.limitLines diff --git a/Charts/Classes/Renderers/ChartYAxisRendererRadarChart.swift b/Charts/Classes/Renderers/ChartYAxisRendererRadarChart.swift index 3c096e4a81..97e17e02e5 100644 --- a/Charts/Classes/Renderers/ChartYAxisRendererRadarChart.swift +++ b/Charts/Classes/Renderers/ChartYAxisRendererRadarChart.swift @@ -23,34 +23,41 @@ public class ChartYAxisRendererRadarChart: ChartYAxisRenderer { private weak var chart: RadarChartView? - public init(viewPortHandler: ChartViewPortHandler, yAxis: ChartYAxis, chart: RadarChartView) + public init(viewPortHandler: ChartViewPortHandler?, yAxis: ChartYAxis?, chart: RadarChartView?) { super.init(viewPortHandler: viewPortHandler, yAxis: yAxis, transformer: nil) self.chart = chart } - public override func computeAxis(yMin yMin: Double, yMax: Double) - { - computeAxisValues(min: yMin, max: yMax) - } - public override func computeAxisValues(min yMin: Double, max yMax: Double) { - guard let yAxis = yAxis else { return } + guard let + axis = axis as? ChartYAxis + else { return } - let labelCount = yAxis.labelCount + let labelCount = axis.labelCount let range = abs(yMax - yMin) if (labelCount == 0 || range <= 0) { - yAxis.entries = [Double]() + axis.entries = [Double]() return } + // Find out how much spacing (in yValue space) between axis values let rawInterval = range / Double(labelCount) var interval = ChartUtils.roundToNextSignificant(number: Double(rawInterval)) - let intervalMagnitude = pow(10.0, round(log10(interval))) + + // If granularity is enabled, then do not allow the interval to go below specified granularity. + // This is used to avoid repeated values when rounding values for display. + if axis.isGranularityEnabled + { + interval = interval < axis.granularity ? axis.granularity : interval + } + + // Normalize interval + let intervalMagnitude = ChartUtils.roundToNextSignificant(number: pow(10.0, floor(log10(interval)))) let intervalSigDigit = Int(interval / intervalMagnitude) if (intervalSigDigit > 5) @@ -60,103 +67,104 @@ public class ChartYAxisRendererRadarChart: ChartYAxisRenderer interval = floor(10 * intervalMagnitude) } + let centeringEnabled = axis.isCenterAxisLabelsEnabled + var n = centeringEnabled ? 1 : 0 + // force label count - if yAxis.isForceLabelsEnabled + if axis.isForceLabelsEnabled { let step = Double(range) / Double(labelCount - 1) - if yAxis.entries.count < labelCount - { - // Ensure stops contains at least numStops elements. - yAxis.entries.removeAll(keepCapacity: true) - } - else - { - yAxis.entries = [Double]() - yAxis.entries.reserveCapacity(labelCount) - } + // Ensure stops contains at least n elements. + axis.entries.removeAll(keepCapacity: true) + axis.entries.reserveCapacity(labelCount) var v = yMin for _ in 0 ..< labelCount { - yAxis.entries.append(v) + axis.entries.append(v) v += step } + n = labelCount } else { // no forced count - // clean old values - if (yAxis.entries.count > 0) - { - yAxis.entries.removeAll(keepCapacity: false) - } + var first = interval == 0.0 ? 0.0 : ceil(yMin / interval) * interval - // if the labels should only show min and max - if (yAxis.isShowOnlyMinMaxEnabled) + if centeringEnabled { - yAxis.entries = [Double]() - yAxis.entries.append(yMin) - yAxis.entries.append(yMax) + first -= interval } - else + + let last = interval == 0.0 ? 0.0 : ChartUtils.nextUp(floor(yMax / interval) * interval) + + if interval != 0.0 { - let rawCount = Double(yMin) / interval - var first = rawCount < 0.0 ? floor(rawCount) * interval : ceil(rawCount) * interval; - - if (first == 0.0) - { // Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0) - first = 0.0 - } - - let last = ChartUtils.nextUp(floor(Double(yMax) / interval) * interval) - - var n = 0 for _ in first.stride(through: last, by: interval) { n += 1 } - - if !yAxis.isAxisMaxCustom - { - n += 1 - } - - if (yAxis.entries.count < n) + } + + n += 1 + + // Ensure stops contains at least n elements. + axis.entries.removeAll(keepCapacity: true) + axis.entries.reserveCapacity(labelCount) + + var f = first + var i = 0 + while i < n + { + if f == 0.0 { - // Ensure stops contains at least numStops elements. - yAxis.entries = [Double](count: n, repeatedValue: 0.0) + // Fix for IEEE negative zero case (Where value == -0.0, and 0.0 == -0.0) + f = 0.0 } + + axis.entries.append(Double(f)) - var f = first - var i = 0 - while (i < n) - { - yAxis.entries[i] = Double(f) - - f += interval - i += 1 - } + f += interval + i += 1 } } - if yAxis.entries[0] < yMin + // set decimals + if interval < 1 { - // If startAtZero is disabled, and the first label is lower that the axis minimum, - // Then adjust the axis minimum - yAxis._axisMinimum = yAxis.entries[0] + axis.decimals = Int(ceil(-log10(interval))) } - yAxis._axisMaximum = yAxis.entries[yAxis.entryCount - 1] - yAxis.axisRange = abs(yAxis._axisMaximum - yAxis._axisMinimum) + else + { + axis.decimals = 0 + } + + if centeringEnabled + { + axis.centeredEntries.reserveCapacity(n) + axis.centeredEntries.removeAll() + + let offset = (axis.entries[1] - axis.entries[0]) / 2.0 + + for i in 0 ..< n + { + axis.centeredEntries.append(axis.entries[i] + offset) + } + } + + axis._axisMinimum = axis.entries[0]; + axis._axisMaximum = axis.entries[n-1]; + axis.axisRange = abs(axis._axisMaximum - axis._axisMinimum) } public override func renderAxisLabels(context context: CGContext) { guard let - yAxis = yAxis, + yAxis = axis as? ChartYAxis, chart = chart else { return } @@ -195,8 +203,9 @@ public class ChartYAxisRendererRadarChart: ChartYAxisRenderer public override func renderLimitLines(context context: CGContext) { guard let - yAxis = yAxis, - chart = chart + yAxis = axis as? ChartYAxis, + chart = chart, + data = chart.data else { return } var limitLines = yAxis.limitLines @@ -239,7 +248,7 @@ public class ChartYAxisRendererRadarChart: ChartYAxisRenderer CGContextBeginPath(context) - for j in 0 ..< chart.data!.xValCount + for j in 0 ..< (data.maxEntryCountSet?.entryCount ?? 0) { let p = ChartUtils.getPosition(center: center, dist: r, angle: sliceangle * CGFloat(j) + chart.rotationAngle) diff --git a/Charts/Classes/Renderers/CombinedChartRenderer.swift b/Charts/Classes/Renderers/CombinedChartRenderer.swift index ed205098b1..cb252ab30c 100644 --- a/Charts/Classes/Renderers/CombinedChartRenderer.swift +++ b/Charts/Classes/Renderers/CombinedChartRenderer.swift @@ -18,9 +18,6 @@ public class CombinedChartRenderer: ChartDataRendererBase { public weak var chart: CombinedChartView? - /// flag that enables or disables the highlighting arrow - public var drawHighlightArrowEnabled = false - /// if set to true, all values are drawn above their bars, instead of below their top public var drawValueAboveBarEnabled = true @@ -31,7 +28,7 @@ public class CombinedChartRenderer: ChartDataRendererBase internal var _drawOrder: [CombinedChartView.DrawOrder] = [.Bar, .Bubble, .Line, .Candle, .Scatter] - public init(chart: CombinedChartView, animator: ChartAnimator, viewPortHandler: ChartViewPortHandler) + public init(chart: CombinedChartView?, animator: ChartAnimator, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) @@ -47,7 +44,8 @@ public class CombinedChartRenderer: ChartDataRendererBase guard let chart = chart, - animator = animator + animator = animator, + viewPortHandler = self.viewPortHandler else { return } for order in drawOrder @@ -93,6 +91,14 @@ public class CombinedChartRenderer: ChartDataRendererBase } + public override func initBuffers() + { + for renderer in _renderers + { + renderer.initBuffers() + } + } + public override func drawData(context context: CGContext) { for renderer in _renderers @@ -151,14 +157,6 @@ public class CombinedChartRenderer: ChartDataRendererBase renderer.drawHighlighted(context: context, indices: dataIndices) } } - - public override func calcXBounds(chart chart: BarLineScatterCandleBubbleChartDataProvider, xAxisModulus: Int) - { - for renderer in _renderers - { - renderer.calcXBounds(chart: chart, xAxisModulus: xAxisModulus) - } - } /// - returns: the sub-renderer object at the specified index. public func getSubRenderer(index index: Int) -> ChartDataRendererBase? @@ -182,9 +180,6 @@ public class CombinedChartRenderer: ChartDataRendererBase // MARK: Accessors - /// - returns: true if drawing the highlighting arrow is enabled, false if not - public var isDrawHighlightArrowEnabled: Bool { return drawHighlightArrowEnabled; } - /// - returns: true if drawing values above bars is enabled, false if not public var isDrawValueAboveBarEnabled: Bool { return drawValueAboveBarEnabled; } diff --git a/Charts/Classes/Renderers/HorizontalBarChartRenderer.swift b/Charts/Classes/Renderers/HorizontalBarChartRenderer.swift index 27d6fe920d..174f9a4040 100644 --- a/Charts/Classes/Renderers/HorizontalBarChartRenderer.swift +++ b/Charts/Classes/Renderers/HorizontalBarChartRenderer.swift @@ -21,12 +21,53 @@ import CoreGraphics public class HorizontalBarChartRenderer: BarChartRenderer { - public override init(dataProvider: BarChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + private class Buffer + { + var rects = [CGRect]() + } + + public override init(dataProvider: BarChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(dataProvider: dataProvider, animator: animator, viewPortHandler: viewPortHandler) } - public override func drawDataSet(context context: CGContext, dataSet: IBarChartDataSet, index: Int) + // [CGRect] per dataset + private var _buffers = [Buffer]() + + public override func initBuffers() + { + if let barData = dataProvider?.barData + { + // Matche buffers count to dataset count + if _buffers.count != barData.dataSetCount + { + while _buffers.count < barData.dataSetCount + { + _buffers.append(Buffer()) + } + while _buffers.count > barData.dataSetCount + { + _buffers.removeLast() + } + } + + for i in 0.stride(to: barData.dataSetCount, by: 1) + { + let set = barData.dataSets[i] as! IBarChartDataSet + let size = set.entryCount * (set.isStacked ? set.stackSize : 1) + if _buffers[i].rects.count != size + { + _buffers[i].rects = [CGRect](count: size, repeatedValue: CGRect()) + } + } + } + else + { + _buffers.removeAll() + } + } + + private func prepareBuffer(dataSet dataSet: IBarChartDataSet, index: Int) { guard let dataProvider = dataProvider, @@ -34,54 +75,46 @@ public class HorizontalBarChartRenderer: BarChartRenderer animator = animator else { return } - CGContextSaveGState(context) + let barWidthHalf = barData.barWidth / 2.0 - let trans = dataProvider.getTransformer(dataSet.axisDependency) - - let drawBarShadowEnabled: Bool = dataProvider.isDrawBarShadowEnabled - let dataSetOffset = (barData.dataSetCount - 1) - let groupSpace = barData.groupSpace - let groupSpaceHalf = groupSpace / 2.0 - let barSpace = dataSet.barSpace - let barSpaceHalf = barSpace / 2.0 + let buffer = _buffers[index] + var bufferIndex = 0 let containsStacks = dataSet.isStacked + let isInverted = dataProvider.isInverted(dataSet.axisDependency) - let barWidth: CGFloat = 0.5 let phaseY = animator.phaseY var barRect = CGRect() - var barShadow = CGRect() - let borderWidth = dataSet.barBorderWidth - let borderColor = dataSet.barBorderColor - let drawBorder = borderWidth > 0.0 + var x: Double var y: Double - // do the drawing - for j in 0 ..< Int(ceil(CGFloat(dataSet.entryCount) * animator.phaseX)) + for i in 0.stride(to: min(Int(ceil(Double(dataSet.entryCount) * animator.phaseX)), dataSet.entryCount), by: 1) { - guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue } + guard let e = dataSet.entryForIndex(i) as? BarChartDataEntry else { continue } + + let vals = e.yValues - // calculate the x-position, depending on datasetcount - let x = CGFloat(e.xIndex + e.xIndex * dataSetOffset) + CGFloat(index) - + groupSpace * CGFloat(e.xIndex) + groupSpaceHalf - let values = e.values + x = e.x + y = e.y - if (!containsStacks || values == nil) + if !containsStacks || vals == nil { - y = e.value - - let bottom = x - barWidth + barSpaceHalf - let top = x + barWidth - barSpaceHalf - var right = isInverted ? (y <= 0.0 ? CGFloat(y) : 0) : (y >= 0.0 ? CGFloat(y) : 0) - var left = isInverted ? (y >= 0.0 ? CGFloat(y) : 0) : (y <= 0.0 ? CGFloat(y) : 0) + let bottom = CGFloat(x - barWidthHalf) + let top = CGFloat(x + barWidthHalf) + var right = isInverted + ? (y <= 0.0 ? CGFloat(y) : 0) + : (y >= 0.0 ? CGFloat(y) : 0) + var left = isInverted + ? (y >= 0.0 ? CGFloat(y) : 0) + : (y <= 0.0 ? CGFloat(y) : 0) // multiply the height of the rect with the phase if (right > 0) { - right *= phaseY + right *= CGFloat(phaseY) } else { - left *= phaseY + left *= CGFloat(phaseY) } barRect.origin.x = left @@ -89,88 +122,20 @@ public class HorizontalBarChartRenderer: BarChartRenderer barRect.origin.y = top barRect.size.height = bottom - top - trans.rectValueToPixel(&barRect) - - if (!viewPortHandler.isInBoundsLeft(barRect.origin.x + barRect.size.width)) - { - continue - } - - if (!viewPortHandler.isInBoundsRight(barRect.origin.x)) - { - break - } - - // if drawing the bar shadow is enabled - if (drawBarShadowEnabled) - { - barShadow.origin.x = viewPortHandler.contentLeft - barShadow.origin.y = barRect.origin.y - barShadow.size.width = viewPortHandler.contentWidth - barShadow.size.height = barRect.size.height - - CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor) - CGContextFillRect(context, barShadow) - } - - // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. - CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor) - CGContextFillRect(context, barRect) - - if drawBorder - { - CGContextSetStrokeColorWithColor(context, borderColor.CGColor) - CGContextSetLineWidth(context, borderWidth) - CGContextStrokeRect(context, barRect) - } + buffer.rects[bufferIndex] = barRect + bufferIndex += 1 } else { - let vals = values! var posY = 0.0 var negY = -e.negativeSum var yStart = 0.0 - // if drawing the bar shadow is enabled - if (drawBarShadowEnabled) - { - y = e.value - - let bottom = x - barWidth + barSpaceHalf - let top = x + barWidth - barSpaceHalf - var right = isInverted ? (y <= 0.0 ? CGFloat(y) : 0) : (y >= 0.0 ? CGFloat(y) : 0) - var left = isInverted ? (y >= 0.0 ? CGFloat(y) : 0) : (y <= 0.0 ? CGFloat(y) : 0) - - // multiply the height of the rect with the phase - if (right > 0) - { - right *= phaseY - } - else - { - left *= phaseY - } - - barRect.origin.x = left - barRect.size.width = right - left - barRect.origin.y = top - barRect.size.height = bottom - top - - trans.rectValueToPixel(&barRect) - - barShadow.origin.x = viewPortHandler.contentLeft - barShadow.origin.y = barRect.origin.y - barShadow.size.width = viewPortHandler.contentWidth - barShadow.size.height = barRect.size.height - - CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor) - CGContextFillRect(context, barShadow) - } // fill the stack - for k in 0 ..< vals.count + for k in 0 ..< vals!.count { - let value = vals[k] + let value = vals![k] if value >= 0.0 { @@ -185,73 +150,132 @@ public class HorizontalBarChartRenderer: BarChartRenderer negY += abs(value) } - let bottom = x - barWidth + barSpaceHalf - let top = x + barWidth - barSpaceHalf - var right: CGFloat, left: CGFloat - if isInverted - { - left = y >= yStart ? CGFloat(y) : CGFloat(yStart) - right = y <= yStart ? CGFloat(y) : CGFloat(yStart) - } - else - { - right = y >= yStart ? CGFloat(y) : CGFloat(yStart) - left = y <= yStart ? CGFloat(y) : CGFloat(yStart) - } + let bottom = CGFloat(x - barWidthHalf) + let top = CGFloat(x + barWidthHalf) + var right = isInverted + ? (y <= yStart ? CGFloat(y) : CGFloat(yStart)) + : (y >= yStart ? CGFloat(y) : CGFloat(yStart)) + var left = isInverted + ? (y >= yStart ? CGFloat(y) : CGFloat(yStart)) + : (y <= yStart ? CGFloat(y) : CGFloat(yStart)) // multiply the height of the rect with the phase - right *= phaseY - left *= phaseY + right *= CGFloat(phaseY) + left *= CGFloat(phaseY) barRect.origin.x = left barRect.size.width = right - left barRect.origin.y = top barRect.size.height = bottom - top - trans.rectValueToPixel(&barRect) - - if (k == 0 && !viewPortHandler.isInBoundsTop(barRect.origin.y + barRect.size.height)) - { - // Skip to next bar - break - } - - // avoid drawing outofbounds values - if (!viewPortHandler.isInBoundsBottom(barRect.origin.y)) - { - break - } - - // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. - CGContextSetFillColorWithColor(context, dataSet.colorAt(k).CGColor) - CGContextFillRect(context, barRect) - - if drawBorder - { - CGContextSetStrokeColorWithColor(context, borderColor.CGColor) - CGContextSetLineWidth(context, borderWidth) - CGContextStrokeRect(context, barRect) - } + buffer.rects[bufferIndex] = barRect + bufferIndex += 1 + } + } + } + } + + public override func drawDataSet(context context: CGContext, dataSet: IBarChartDataSet, index: Int) + { + guard let + dataProvider = dataProvider, + viewPortHandler = self.viewPortHandler + else { return } + + let trans = dataProvider.getTransformer(dataSet.axisDependency) + + prepareBuffer(dataSet: dataSet, index: index) + trans.rectValuesToPixel(&_buffers[index].rects) + + let borderWidth = dataSet.barBorderWidth + let borderColor = dataSet.barBorderColor + let drawBorder = borderWidth > 0.0 + + CGContextSaveGState(context) + + let buffer = _buffers[index] + + // draw the bar shadow before the values + if dataProvider.isDrawBarShadowEnabled + { + for j in 0.stride(to: buffer.rects.count, by: 1) + { + let barRect = buffer.rects[j] + + if (!viewPortHandler.isInBoundsTop(barRect.origin.y + barRect.size.height)) + { + break + } + + if (!viewPortHandler.isInBoundsBottom(barRect.origin.y)) + { + continue } + + CGContextSetFillColorWithColor(context, dataSet.barShadowColor.CGColor) + CGContextFillRect(context, barRect) + } + } + + // FIXME: DRY code on Android + + let isSingleColor = dataSet.colors.count == 1 + + if isSingleColor + { + CGContextSetFillColorWithColor(context, dataSet.colorAt(0).CGColor) + } + + for j in 0.stride(to: buffer.rects.count, by: 1) + { + let barRect = buffer.rects[j] + + if (!viewPortHandler.isInBoundsTop(barRect.origin.y + barRect.size.height)) + { + break + } + + if (!viewPortHandler.isInBoundsBottom(barRect.origin.y)) + { + continue + } + + if !isSingleColor + { + // Set the color for the currently drawn value. If the index is out of bounds, reuse colors. + CGContextSetFillColorWithColor(context, dataSet.colorAt(j).CGColor) + } + + CGContextFillRect(context, barRect) + + if drawBorder + { + CGContextSetStrokeColorWithColor(context, borderColor.CGColor) + CGContextSetLineWidth(context, borderWidth) + CGContextStrokeRect(context, barRect) } } CGContextRestoreGState(context) } - public override func prepareBarHighlight(x x: CGFloat, y1: Double, y2: Double, barspacehalf: CGFloat, trans: ChartTransformer, inout rect: CGRect) + public override func prepareBarHighlight( + x x: Double, + y1: Double, + y2: Double, + barWidthHalf: Double, + trans: ChartTransformer, + inout rect: CGRect) { - let barWidth: CGFloat = 0.5 + let top = x - barWidthHalf + let bottom = x + barWidthHalf + let left = y1 + let right = y2 - let top = x - barWidth + barspacehalf - let bottom = x + barWidth - barspacehalf - let left = CGFloat(y1) - let right = CGFloat(y2) - - rect.origin.x = left - rect.origin.y = top - rect.size.width = right - left - rect.size.height = bottom - top + rect.origin.x = CGFloat(left) + rect.origin.y = CGFloat(top) + rect.size.width = CGFloat(right - left) + rect.size.height = CGFloat(bottom - top) trans.rectValueToPixelHorizontal(&rect, phaseY: animator?.phaseY ?? 1.0) } @@ -259,23 +283,23 @@ public class HorizontalBarChartRenderer: BarChartRenderer public override func drawValues(context context: CGContext) { // if values are drawn - if (passesCheck()) + if isDrawingValuesAllowed(dataProvider: dataProvider) { guard let dataProvider = dataProvider, barData = dataProvider.barData, - animator = animator + animator = animator, + viewPortHandler = self.viewPortHandler else { return } var dataSets = barData.dataSets - let drawValueAboveBar = dataProvider.isDrawValueAboveBarEnabled - let textAlign = NSTextAlignment.Left let valueOffsetPlus: CGFloat = 5.0 var posOffset: CGFloat var negOffset: CGFloat + let drawValueAboveBar = dataProvider.isDrawValueAboveBarEnabled for dataSetIndex in 0 ..< barData.dataSetCount { @@ -295,35 +319,38 @@ public class HorizontalBarChartRenderer: BarChartRenderer let trans = dataProvider.getTransformer(dataSet.axisDependency) + // FIXME: Put in variable on Android let phaseY = animator.phaseY - let dataSetCount = barData.dataSetCount - let groupSpace = barData.groupSpace + + let buffer = _buffers[dataSetIndex] // if only single values are drawn (sum) - if (!dataSet.isStacked) + if !dataSet.isStacked { - for j in 0 ..< Int(ceil(CGFloat(dataSet.entryCount) * animator.phaseX)) + for j in 0 ..< Int(ceil(Double(dataSet.entryCount) * animator.phaseX)) { guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue } - let valuePoint = trans.getTransformedValueHorizontalBarChart(entry: e, xIndex: e.xIndex, dataSetIndex: dataSetIndex, phaseY: phaseY, dataSetCount: dataSetCount, groupSpace: groupSpace) + let rect = buffer.rects[j] - if (!viewPortHandler.isInBoundsTop(valuePoint.y)) + let y = rect.origin.y + rect.size.height / 2.0 + + if !viewPortHandler.isInBoundsTop(rect.origin.y) { break } - if (!viewPortHandler.isInBoundsX(valuePoint.x)) + if !viewPortHandler.isInBoundsX(rect.origin.x) { continue } - if (!viewPortHandler.isInBoundsBottom(valuePoint.y)) + if !viewPortHandler.isInBoundsBottom(rect.origin.y) { continue } - let val = e.value + let val = e.y let valueText = formatter.stringFromNumber(val)! // calculate the correct offset depending on the draw position of the value @@ -340,8 +367,9 @@ public class HorizontalBarChartRenderer: BarChartRenderer drawValue( context: context, value: valueText, - xPos: valuePoint.x + (val >= 0.0 ? posOffset : negOffset), - yPos: valuePoint.y + yOffset, + xPos: (rect.origin.x + rect.size.width) + + (val >= 0.0 ? posOffset : negOffset), + yPos: y + yOffset, font: valueFont, align: textAlign, color: dataSet.valueTextColorAt(j)) @@ -351,33 +379,35 @@ public class HorizontalBarChartRenderer: BarChartRenderer { // if each value of a potential stack should be drawn - for j in 0 ..< Int(ceil(CGFloat(dataSet.entryCount) * animator.phaseX)) + var bufferIndex = 0 + + for index in 0 ..< Int(ceil(Double(dataSet.entryCount) * animator.phaseX)) { - guard let e = dataSet.entryForIndex(j) as? BarChartDataEntry else { continue } + guard let e = dataSet.entryForIndex(index) as? BarChartDataEntry else { continue } - let valuePoint = trans.getTransformedValueHorizontalBarChart(entry: e, xIndex: e.xIndex, dataSetIndex: dataSetIndex, phaseY: phaseY, dataSetCount: dataSetCount, groupSpace: groupSpace) + let rect = buffer.rects[bufferIndex] - let values = e.values + let vals = e.yValues // we still draw stacked bars, but there is one non-stacked in between - if (values == nil) + if vals == nil { - if (!viewPortHandler.isInBoundsTop(valuePoint.y)) + if !viewPortHandler.isInBoundsTop(rect.origin.y) { break } - if (!viewPortHandler.isInBoundsX(valuePoint.x)) + if !viewPortHandler.isInBoundsX(rect.origin.x) { continue } - if (!viewPortHandler.isInBoundsBottom(valuePoint.y)) + if !viewPortHandler.isInBoundsBottom(rect.origin.y) { continue } - let val = e.value + let val = e.y let valueText = formatter.stringFromNumber(val)! // calculate the correct offset depending on the draw position of the value @@ -385,7 +415,7 @@ public class HorizontalBarChartRenderer: BarChartRenderer posOffset = (drawValueAboveBar ? valueOffsetPlus : -(valueTextWidth + valueOffsetPlus)) negOffset = (drawValueAboveBar ? -(valueTextWidth + valueOffsetPlus) : valueOffsetPlus) - if (isInverted) + if isInverted { posOffset = -posOffset - valueTextWidth negOffset = -negOffset - valueTextWidth @@ -394,15 +424,16 @@ public class HorizontalBarChartRenderer: BarChartRenderer drawValue( context: context, value: valueText, - xPos: valuePoint.x + (val >= 0.0 ? posOffset : negOffset), - yPos: valuePoint.y + yOffset, + xPos: (rect.origin.x + rect.size.width) + + (val >= 0.0 ? posOffset : negOffset), + yPos: rect.origin.y + yOffset, font: valueFont, align: textAlign, - color: dataSet.valueTextColorAt(j)) + color: dataSet.valueTextColorAt(index)) } else { - let vals = values! + let vals = vals! var transformed = [CGPoint]() var posY = 0.0 @@ -424,7 +455,7 @@ public class HorizontalBarChartRenderer: BarChartRenderer negY -= value } - transformed.append(CGPoint(x: CGFloat(y) * animator.phaseY, y: 0.0)) + transformed.append(CGPoint(x: CGFloat(y * phaseY), y: 0.0)) } trans.pointValuesToPixel(&transformed) @@ -446,7 +477,7 @@ public class HorizontalBarChartRenderer: BarChartRenderer } let x = transformed[k].x + (val >= 0 ? posOffset : negOffset) - let y = valuePoint.y + let y = rect.origin.y + rect.size.height / 2.0 if (!viewPortHandler.isInBoundsTop(y)) { @@ -469,19 +500,22 @@ public class HorizontalBarChartRenderer: BarChartRenderer yPos: y + yOffset, font: valueFont, align: textAlign, - color: dataSet.valueTextColorAt(j)) + color: dataSet.valueTextColorAt(index)) } } + + bufferIndex = vals == nil ? (bufferIndex + 1) : (bufferIndex + vals!.count) } } } } } - internal override func passesCheck() -> Bool + public override func isDrawingValuesAllowed(dataProvider dataProvider: ChartDataProvider?) -> Bool { - guard let dataProvider = dataProvider, barData = dataProvider.barData else { return false } + guard let data = dataProvider?.data + else { return false } - return CGFloat(barData.yValCount) < CGFloat(dataProvider.maxVisibleValueCount) * viewPortHandler.scaleY + return data.entryCount < Int(CGFloat(dataProvider?.maxVisibleCount ?? 0) * (viewPortHandler?.scaleY ?? 1.0)) } } \ No newline at end of file diff --git a/Charts/Classes/Renderers/LineChartRenderer.swift b/Charts/Classes/Renderers/LineChartRenderer.swift index 81f98b450a..0cc19a47eb 100644 --- a/Charts/Classes/Renderers/LineChartRenderer.swift +++ b/Charts/Classes/Renderers/LineChartRenderer.swift @@ -23,7 +23,7 @@ public class LineChartRenderer: LineRadarChartRenderer { public weak var dataProvider: LineChartDataProvider? - public init(dataProvider: LineChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public init(dataProvider: LineChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) @@ -91,24 +91,16 @@ public class LineChartRenderer: LineRadarChartRenderer public func drawCubicBezier(context context: CGContext, dataSet: ILineChartDataSet) { guard let - trans = dataProvider?.getTransformer(dataSet.axisDependency), + dataProvider = dataProvider, animator = animator else { return } - let entryCount = dataSet.entryCount + let trans = dataProvider.getTransformer(dataSet.axisDependency) - guard let - entryFrom = dataSet.entryForXIndex(self.minX < 0 ? 0 : self.minX, rounding: .Down), - entryTo = dataSet.entryForXIndex(self.maxX, rounding: .Up) - else { return } - - let diff = (entryFrom == entryTo) ? 1 : 0 - let minx = max(dataSet.entryIndex(entry: entryFrom) - diff - 1, 0) - let maxx = min(max(minx + 2, dataSet.entryIndex(entry: entryTo) + 1), entryCount) - - let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) + // get the color that is specified for this position from the DataSet let drawingColor = dataSet.colors.first! @@ -119,46 +111,44 @@ public class LineChartRenderer: LineRadarChartRenderer var valueToPixelMatrix = trans.valueToPixelMatrix - let size = Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx))) - - if (size - minx >= 2) + if bounds.range >= 1 { var prevDx: CGFloat = 0.0 var prevDy: CGFloat = 0.0 var curDx: CGFloat = 0.0 var curDy: CGFloat = 0.0 - var prevPrev: ChartDataEntry! = dataSet.entryForIndex(minx) + var prevPrev: ChartDataEntry! = dataSet.entryForIndex(bounds.min) var prev: ChartDataEntry! = prevPrev var cur: ChartDataEntry! = prev - var next: ChartDataEntry! = dataSet.entryForIndex(minx + 1) + var next: ChartDataEntry! = dataSet.entryForIndex(bounds.min + 1) if cur == nil || next == nil { return } // let the spline start - CGPathMoveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY) + CGPathMoveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(cur.x), CGFloat(cur.y * phaseY)) - for j in (minx + 1).stride(to: min(size, entryCount), by: 1) + for j in (bounds.min + 1).stride(through: bounds.range + bounds.min, by: 1) { prevPrev = prev prev = cur cur = next - next = entryCount > j + 1 ? dataSet.entryForIndex(j + 1) : cur + next = bounds.max > j + 1 ? dataSet.entryForIndex(j + 1) : cur if next == nil { break } - prevDx = CGFloat(cur.xIndex - prevPrev.xIndex) * intensity - prevDy = CGFloat(cur.value - prevPrev.value) * intensity - curDx = CGFloat(next.xIndex - prev.xIndex) * intensity - curDy = CGFloat(next.value - prev.value) * intensity + prevDx = CGFloat(cur.x - prevPrev.x) * intensity + prevDy = CGFloat(cur.y - prevPrev.y) * intensity + curDx = CGFloat(next.x - prev.x) * intensity + curDy = CGFloat(next.y - prev.y) * intensity CGPathAddCurveToPoint(cubicPath, &valueToPixelMatrix, - CGFloat(prev.xIndex) + prevDx, - (CGFloat(prev.value) + prevDy) * phaseY, - CGFloat(cur.xIndex) - curDx, - (CGFloat(cur.value) - curDy) * phaseY, - CGFloat(cur.xIndex), - CGFloat(cur.value) * phaseY) + CGFloat(prev.x) + prevDx, + (CGFloat(prev.y) + prevDy) * CGFloat(phaseY), + CGFloat(cur.x) - curDx, + (CGFloat(cur.y) - curDy) * CGFloat(phaseY), + CGFloat(cur.x), + CGFloat(cur.y) * CGFloat(phaseY)) } } @@ -169,7 +159,7 @@ public class LineChartRenderer: LineRadarChartRenderer // Copy this path because we make changes to it let fillPath = CGPathCreateMutableCopy(cubicPath) - drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, from: minx, to: size) + drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, bounds: bounds) } CGContextBeginPath(context) @@ -183,24 +173,16 @@ public class LineChartRenderer: LineRadarChartRenderer public func drawHorizontalBezier(context context: CGContext, dataSet: ILineChartDataSet) { guard let - trans = dataProvider?.getTransformer(dataSet.axisDependency), + dataProvider = dataProvider, animator = animator else { return } - let entryCount = dataSet.entryCount - - guard let - entryFrom = dataSet.entryForXIndex(self.minX < 0 ? 0 : self.minX, rounding: .Down), - entryTo = dataSet.entryForXIndex(self.maxX, rounding: .Up) - else { return } - - let diff = (entryFrom == entryTo) ? 1 : 0 - let minx = max(dataSet.entryIndex(entry: entryFrom) - diff, 0) - let maxx = min(max(minx + 2, dataSet.entryIndex(entry: entryTo) + 1), entryCount) + let trans = dataProvider.getTransformer(dataSet.axisDependency) - let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) + // get the color that is specified for this position from the DataSet let drawingColor = dataSet.colors.first! @@ -209,41 +191,39 @@ public class LineChartRenderer: LineRadarChartRenderer var valueToPixelMatrix = trans.valueToPixelMatrix - let size = Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx))) - - if (size - minx >= 2) + if bounds.range >= 1 { - var prev: ChartDataEntry! = dataSet.entryForIndex(minx) + var prev: ChartDataEntry! = dataSet.entryForIndex(bounds.min) var cur: ChartDataEntry! = prev if cur == nil { return } // let the spline start - CGPathMoveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY) + CGPathMoveToPoint(cubicPath, &valueToPixelMatrix, CGFloat(cur.x), CGFloat(cur.x * phaseY)) - for j in (minx + 1).stride(to: min(size, entryCount), by: 1) + for j in (bounds.min + 1).stride(through: bounds.range + bounds.min, by: 1) { prev = cur cur = dataSet.entryForIndex(j) - let cpx = CGFloat(prev.xIndex) + CGFloat(cur.xIndex - prev.xIndex) / 2.0 + let cpx = CGFloat(prev.x + (cur.x - prev.x)) / 2.0 CGPathAddCurveToPoint(cubicPath, &valueToPixelMatrix, - cpx, CGFloat(prev.value) * phaseY, - cpx, CGFloat(cur.value) * phaseY, - CGFloat(cur.xIndex), CGFloat(cur.value) * phaseY) + cpx, CGFloat(prev.y * phaseY), + cpx, CGFloat(cur.y * phaseY), + CGFloat(cur.x), CGFloat(cur.y * phaseY)) } } CGContextSaveGState(context) - if (dataSet.isDrawFilledEnabled) + if dataSet.isDrawFilledEnabled { // Copy this path because we make changes to it let fillPath = CGPathCreateMutableCopy(cubicPath) - drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, from: minx, to: size) + drawCubicFill(context: context, dataSet: dataSet, spline: fillPath!, matrix: valueToPixelMatrix, bounds: bounds) } CGContextBeginPath(context) @@ -254,25 +234,24 @@ public class LineChartRenderer: LineRadarChartRenderer CGContextRestoreGState(context) } - public func drawCubicFill(context context: CGContext, dataSet: ILineChartDataSet, spline: CGMutablePath, matrix: CGAffineTransform, from: Int, to: Int) + public func drawCubicFill( + context context: CGContext, + dataSet: ILineChartDataSet, + spline: CGMutablePath, + matrix: CGAffineTransform, + bounds: XBounds) { guard let dataProvider = dataProvider else { return } - if to - from <= 1 + if bounds.range <= 0 { return } let fillMin = dataSet.fillFormatter?.getFillLinePosition(dataSet: dataSet, dataProvider: dataProvider) ?? 0.0 - - // Take the from/to xIndex from the entries themselves, - // so missing entries won't screw up the filling. - // What we need to draw is line from points of the xIndexes - not arbitrary entry indexes! - let xTo = dataSet.entryForIndex(to - 1)?.xIndex ?? 0 - let xFrom = dataSet.entryForIndex(from)?.xIndex ?? 0 - var pt1 = CGPoint(x: CGFloat(xTo), y: fillMin) - var pt2 = CGPoint(x: CGFloat(xFrom), y: fillMin) + var pt1 = CGPoint(x: CGFloat(bounds.min + bounds.range), y: fillMin) + var pt2 = CGPoint(x: CGFloat(bounds.min), y: fillMin) pt1 = CGPointApplyAffineTransform(pt1, matrix) pt2 = CGPointApplyAffineTransform(pt2, matrix) @@ -295,61 +274,45 @@ public class LineChartRenderer: LineRadarChartRenderer public func drawLinear(context context: CGContext, dataSet: ILineChartDataSet) { guard let - trans = dataProvider?.getTransformer(dataSet.axisDependency), - animator = animator + dataProvider = dataProvider, + animator = animator, + viewPortHandler = self.viewPortHandler else { return } + let trans = dataProvider.getTransformer(dataSet.axisDependency) + let valueToPixelMatrix = trans.valueToPixelMatrix let entryCount = dataSet.entryCount let isDrawSteppedEnabled = dataSet.mode == .Stepped let pointsPerEntryPair = isDrawSteppedEnabled ? 4 : 2 - let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY - - guard let - entryFrom = dataSet.entryForXIndex(self.minX < 0 ? 0 : self.minX, rounding: .Down), - entryTo = dataSet.entryForXIndex(self.maxX, rounding: .Up) - else { return } - - var diff = (entryFrom == entryTo) ? 1 : 0 - if dataSet.mode == .CubicBezier - { - diff += 1 - } - let minx = max(dataSet.entryIndex(entry: entryFrom) - diff, 0) - let maxx = min(max(minx + 2, dataSet.entryIndex(entry: entryTo) + 1), entryCount) + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) CGContextSaveGState(context) CGContextSetLineCap(context, dataSet.lineCapType) // more than 1 color - if (dataSet.colors.count > 1) + if dataSet.colors.count > 1 { - if (_lineSegments.count != pointsPerEntryPair) + if _lineSegments.count != pointsPerEntryPair { _lineSegments = [CGPoint](count: pointsPerEntryPair, repeatedValue: CGPoint()) } - let count = Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx))) - for j in minx.stride(to: count, by: 1) + for j in bounds.min.stride(through: bounds.range + bounds.min, by: 1) { - if (count > 1 && j == count - 1) - { // Last point, we have already drawn a line to this point - break - } - var e: ChartDataEntry! = dataSet.entryForIndex(j) if e == nil { continue } - _lineSegments[0].x = CGFloat(e.xIndex) - _lineSegments[0].y = CGFloat(e.value) * phaseY + _lineSegments[0].x = CGFloat(e.x) + _lineSegments[0].y = CGFloat(e.y * phaseY) - if (j + 1 < count) + if j < bounds.range { e = dataSet.entryForIndex(j + 1) @@ -357,13 +320,13 @@ public class LineChartRenderer: LineRadarChartRenderer if isDrawSteppedEnabled { - _lineSegments[1] = CGPoint(x: CGFloat(e.xIndex), y: _lineSegments[0].y) + _lineSegments[1] = CGPoint(x: CGFloat(e.x), y: _lineSegments[0].y) _lineSegments[2] = _lineSegments[1] - _lineSegments[3] = CGPoint(x: CGFloat(e.xIndex), y: CGFloat(e.value) * phaseY) + _lineSegments[3] = CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)) } else { - _lineSegments[1] = CGPoint(x: CGFloat(e.xIndex), y: CGFloat(e.value) * phaseY) + _lineSegments[1] = CGPoint(x: CGFloat(e.x), y: CGFloat(e.y * phaseY)) } } else @@ -400,19 +363,17 @@ public class LineChartRenderer: LineRadarChartRenderer var e1: ChartDataEntry! var e2: ChartDataEntry! - if (_lineSegments.count != max((entryCount - 1) * pointsPerEntryPair, pointsPerEntryPair)) + if _lineSegments.count != max((entryCount) * pointsPerEntryPair, pointsPerEntryPair) { - _lineSegments = [CGPoint](count: max((entryCount - 1) * pointsPerEntryPair, pointsPerEntryPair), repeatedValue: CGPoint()) + _lineSegments = [CGPoint](count: max((entryCount) * pointsPerEntryPair, pointsPerEntryPair), repeatedValue: CGPoint()) } - e1 = dataSet.entryForIndex(minx) + e1 = dataSet.entryForIndex(bounds.min) if e1 != nil { - let count = Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx))) - var j = 0 - for x in (count > 1 ? minx + 1 : minx).stride(to: count, by: 1) + for x in bounds.min.stride(through: bounds.range + bounds.min, by: 1) { e1 = dataSet.entryForIndex(x == 0 ? 0 : (x - 1)) e2 = dataSet.entryForIndex(x) @@ -421,8 +382,8 @@ public class LineChartRenderer: LineRadarChartRenderer _lineSegments[j] = CGPointApplyAffineTransform( CGPoint( - x: CGFloat(e1.xIndex), - y: CGFloat(e1.value) * phaseY + x: CGFloat(e1.x), + y: CGFloat(e1.y * phaseY) ), valueToPixelMatrix) j += 1 @@ -430,30 +391,30 @@ public class LineChartRenderer: LineRadarChartRenderer { _lineSegments[j] = CGPointApplyAffineTransform( CGPoint( - x: CGFloat(e2.xIndex), - y: CGFloat(e1.value) * phaseY + x: CGFloat(e2.x), + y: CGFloat(e1.y * phaseY) ), valueToPixelMatrix) j += 1 _lineSegments[j] = CGPointApplyAffineTransform( CGPoint( - x: CGFloat(e2.xIndex), - y: CGFloat(e1.value) * phaseY + x: CGFloat(e2.x), + y: CGFloat(e1.y * phaseY) ), valueToPixelMatrix) j += 1 } _lineSegments[j] = CGPointApplyAffineTransform( CGPoint( - x: CGFloat(e2.xIndex), - y: CGFloat(e2.value) * phaseY + x: CGFloat(e2.x), + y: CGFloat(e2.y * phaseY) ), valueToPixelMatrix) j += 1 } if j > 0 { - let size = max((count - minx - 1) * pointsPerEntryPair, pointsPerEntryPair) + let size = max((bounds.range + 1) * pointsPerEntryPair, pointsPerEntryPair) CGContextSetStrokeColorWithColor(context, dataSet.colorAt(0).CGColor) CGContextStrokeLineSegments(context, _lineSegments, size) } @@ -465,19 +426,18 @@ public class LineChartRenderer: LineRadarChartRenderer // if drawing filled is enabled if (dataSet.isDrawFilledEnabled && entryCount > 0) { - drawLinearFill(context: context, dataSet: dataSet, minx: minx, maxx: maxx, trans: trans) + drawLinearFill(context: context, dataSet: dataSet, trans: trans, bounds: bounds) } } - public func drawLinearFill(context context: CGContext, dataSet: ILineChartDataSet, minx: Int, maxx: Int, trans: ChartTransformer) + public func drawLinearFill(context context: CGContext, dataSet: ILineChartDataSet, trans: ChartTransformer, bounds: XBounds) { guard let dataProvider = dataProvider else { return } let filled = generateFilledPath( dataSet: dataSet, fillMin: dataSet.fillFormatter?.getFillLinePosition(dataSet: dataSet, dataProvider: dataProvider) ?? 0.0, - from: minx, - to: maxx, + bounds: bounds, matrix: trans.valueToPixelMatrix) if dataSet.fill != nil @@ -491,9 +451,8 @@ public class LineChartRenderer: LineRadarChartRenderer } /// Generates the path that is used for filled drawing. - private func generateFilledPath(dataSet dataSet: ILineChartDataSet, fillMin: CGFloat, from: Int, to: Int, matrix: CGAffineTransform) -> CGPath + private func generateFilledPath(dataSet dataSet: ILineChartDataSet, fillMin: CGFloat, bounds: XBounds, matrix: CGAffineTransform) -> CGPath { - let phaseX = max(0.0, min(1.0, animator?.phaseX ?? 1.0)) let phaseY = animator?.phaseY ?? 1.0 let isDrawSteppedEnabled = dataSet.mode == .Stepped var matrix = matrix @@ -502,32 +461,32 @@ public class LineChartRenderer: LineRadarChartRenderer let filled = CGPathCreateMutable() - e = dataSet.entryForIndex(from) + e = dataSet.entryForIndex(bounds.min) if e != nil { - CGPathMoveToPoint(filled, &matrix, CGFloat(e.xIndex), fillMin) - CGPathAddLineToPoint(filled, &matrix, CGFloat(e.xIndex), CGFloat(e.value) * phaseY) + CGPathMoveToPoint(filled, &matrix, CGFloat(e.x), fillMin) + CGPathAddLineToPoint(filled, &matrix, CGFloat(e.x), CGFloat(e.y * phaseY)) } // create a new path - for x in (from + 1).stride(to: Int(ceil(CGFloat(to - from) * phaseX + CGFloat(from))), by: 1) + for x in (bounds.min + 1).stride(through: bounds.range + bounds.min, by: 1) { guard let e = dataSet.entryForIndex(x) else { continue } if isDrawSteppedEnabled { guard let ePrev = dataSet.entryForIndex(x-1) else { continue } - CGPathAddLineToPoint(filled, &matrix, CGFloat(e.xIndex), CGFloat(ePrev.value) * phaseY) + CGPathAddLineToPoint(filled, &matrix, CGFloat(e.x), CGFloat(ePrev.y * phaseY)) } - CGPathAddLineToPoint(filled, &matrix, CGFloat(e.xIndex), CGFloat(e.value) * phaseY) + CGPathAddLineToPoint(filled, &matrix, CGFloat(e.x), CGFloat(e.y * phaseY)) } // close up - e = dataSet.entryForIndex(max(min(Int(ceil(CGFloat(to - from) * phaseX + CGFloat(from))) - 1, dataSet.entryCount - 1), 0)) + e = dataSet.entryForIndex(bounds.range + bounds.min) if e != nil { - CGPathAddLineToPoint(filled, &matrix, CGFloat(e.xIndex), fillMin) + CGPathAddLineToPoint(filled, &matrix, CGFloat(e.x), fillMin) } CGPathCloseSubpath(filled) @@ -539,10 +498,11 @@ public class LineChartRenderer: LineRadarChartRenderer guard let dataProvider = dataProvider, lineData = dataProvider.lineData, - animator = animator + animator = animator, + viewPortHandler = self.viewPortHandler else { return } - if (CGFloat(lineData.yValCount) < CGFloat(dataProvider.maxVisibleValueCount) * viewPortHandler.scaleX) + if isDrawingValuesAllowed(dataProvider: dataProvider) { var dataSets = lineData.dataSets @@ -570,33 +530,19 @@ public class LineChartRenderer: LineRadarChartRenderer // make sure the values do not interfear with the circles var valOffset = Int(dataSet.circleRadius * 1.75) - if (!dataSet.isDrawCirclesEnabled) + if !dataSet.isDrawCirclesEnabled { valOffset = valOffset / 2 } - let entryCount = dataSet.entryCount - - guard let - entryFrom = dataSet.entryForXIndex(self.minX < 0 ? 0 : self.minX, rounding: .Down), - entryTo = dataSet.entryForXIndex(self.maxX, rounding: .Up) - else { continue } + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) - var diff = (entryFrom == entryTo) ? 1 : 0 - if dataSet.mode == .CubicBezier - { - diff += 1 - } - - let minx = max(dataSet.entryIndex(entry: entryFrom) - diff, 0) - let maxx = min(max(minx + 2, dataSet.entryIndex(entry: entryTo) + 1), entryCount) - - for j in minx.stride(to: Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx))), by: 1) + for j in bounds.min.stride(to: Int(ceil(Double(bounds.max - bounds.min) * phaseX + Double(bounds.min))), by: 1) { guard let e = dataSet.entryForIndex(j) else { break } - pt.x = CGFloat(e.xIndex) - pt.y = CGFloat(e.value) * phaseY + pt.x = CGFloat(e.x) + pt.y = CGFloat(e.y * phaseY) pt = CGPointApplyAffineTransform(pt, valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) @@ -610,7 +556,7 @@ public class LineChartRenderer: LineRadarChartRenderer } ChartUtils.drawText(context: context, - text: formatter.stringFromNumber(e.value)!, + text: formatter.stringFromNumber(e.y)!, point: CGPoint( x: pt.x, y: pt.y - CGFloat(valOffset) - valueFont.lineHeight), @@ -631,10 +577,10 @@ public class LineChartRenderer: LineRadarChartRenderer guard let dataProvider = dataProvider, lineData = dataProvider.lineData, - animator = animator + animator = animator, + viewPortHandler = self.viewPortHandler else { return } - let phaseX = max(0.0, min(1.0, animator.phaseX)) let phaseY = animator.phaseY let dataSets = lineData.dataSets @@ -656,7 +602,7 @@ public class LineChartRenderer: LineRadarChartRenderer let trans = dataProvider.getTransformer(dataSet.axisDependency) let valueToPixelMatrix = trans.valueToPixelMatrix - let entryCount = dataSet.entryCount + let bounds = xBounds(dataProvider, dataSet: dataSet, animator: animator) let circleRadius = dataSet.circleRadius let circleDiameter = circleRadius * 2.0 @@ -670,26 +616,12 @@ public class LineChartRenderer: LineRadarChartRenderer (dataSet.circleHoleColor == nil || dataSet.circleHoleColor == NSUIColor.clearColor()) - guard let - entryFrom = dataSet.entryForXIndex(self.minX < 0 ? 0 : self.minX, rounding: .Down), - entryTo = dataSet.entryForXIndex(self.maxX, rounding: .Up) - else { continue } - - var diff = (entryFrom == entryTo) ? 1 : 0 - if dataSet.mode == .CubicBezier - { - diff += 1 - } - - let minx = max(dataSet.entryIndex(entry: entryFrom) - diff, 0) - let maxx = min(max(minx + 2, dataSet.entryIndex(entry: entryTo) + 1), entryCount) - - for j in minx.stride(to: Int(ceil(CGFloat(maxx - minx) * phaseX + CGFloat(minx))), by: 1) + for j in bounds.min.stride(through: bounds.range + bounds.min, by: 1) { guard let e = dataSet.entryForIndex(j) else { break } - pt.x = CGFloat(e.xIndex) - pt.y = CGFloat(e.value) * phaseY + pt.x = CGFloat(e.x) + pt.y = CGFloat(e.y * phaseY) pt = CGPointApplyAffineTransform(pt, valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) @@ -745,16 +677,16 @@ public class LineChartRenderer: LineRadarChartRenderer CGContextRestoreGState(context) } - private var _highlightPointBuffer = CGPoint() - public override func drawHighlighted(context context: CGContext, indices: [ChartHighlight]) { guard let - lineData = dataProvider?.lineData, - chartXMax = dataProvider?.chartXMax, + dataProvider = dataProvider, + lineData = dataProvider.lineData, animator = animator else { return } + let chartXMax = dataProvider.chartXMax + CGContextSaveGState(context) for high in indices @@ -783,30 +715,20 @@ public class LineChartRenderer: LineRadarChartRenderer CGContextSetLineDash(context, 0.0, nil, 0) } - let xIndex = high.xIndex; // get the x-position + let x = high.x; // get the x-position + let y = high.y * Double(animator.phaseY) - if (CGFloat(xIndex) > CGFloat(chartXMax) * animator.phaseX) + if (x > chartXMax * animator.phaseX) { continue } - let yValue = set.yValForXIndex(xIndex) - if (yValue.isNaN) - { - continue - } - - let y = CGFloat(yValue) * animator.phaseY; // get the y-position - - _highlightPointBuffer.x = CGFloat(xIndex) - _highlightPointBuffer.y = y - - let trans = dataProvider?.getTransformer(set.axisDependency) + let trans = dataProvider.getTransformer(set.axisDependency) - trans?.pointValueToPixel(&_highlightPointBuffer) + let pt = trans.pixelForValue(x: x, y: y) // draw the lines - drawHighlightLines(context: context, point: _highlightPointBuffer, set: set) + drawHighlightLines(context: context, point: pt, set: set) } } diff --git a/Charts/Classes/Renderers/LineRadarChartRenderer.swift b/Charts/Classes/Renderers/LineRadarChartRenderer.swift index 7f5ae4103d..80e345c646 100644 --- a/Charts/Classes/Renderers/LineRadarChartRenderer.swift +++ b/Charts/Classes/Renderers/LineRadarChartRenderer.swift @@ -17,7 +17,7 @@ import CoreGraphics public class LineRadarChartRenderer: LineScatterCandleRadarChartRenderer { - public override init(animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public override init(animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) } @@ -25,6 +25,9 @@ public class LineRadarChartRenderer: LineScatterCandleRadarChartRenderer /// Draws the provided path in filled mode with the provided drawable. public func drawFilledPath(context context: CGContext, path: CGPath, fill: ChartFill, fillAlpha: CGFloat) { + guard let viewPortHandler = self.viewPortHandler + else { return } + CGContextSaveGState(context) CGContextBeginPath(context) CGContextAddPath(context, path) diff --git a/Charts/Classes/Renderers/LineScatterCandleRadarChartRenderer.swift b/Charts/Classes/Renderers/LineScatterCandleRadarChartRenderer.swift index 838c8a3d47..b5d8b67824 100644 --- a/Charts/Classes/Renderers/LineScatterCandleRadarChartRenderer.swift +++ b/Charts/Classes/Renderers/LineScatterCandleRadarChartRenderer.swift @@ -15,9 +15,9 @@ import Foundation import CoreGraphics -public class LineScatterCandleRadarChartRenderer: ChartDataRendererBase +public class LineScatterCandleRadarChartRenderer: BarLineScatterCandleBubbleChartRenderer { - public override init(animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public override init(animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) } @@ -29,6 +29,10 @@ public class LineScatterCandleRadarChartRenderer: ChartDataRendererBase /// :param: vertical public func drawHighlightLines(context context: CGContext, point: CGPoint, set: ILineScatterCandleRadarChartDataSet) { + guard let + viewPortHandler = self.viewPortHandler + else { return } + // draw vertical highlight lines if set.isVerticalHighlightIndicatorEnabled { diff --git a/Charts/Classes/Renderers/PieChartRenderer.swift b/Charts/Classes/Renderers/PieChartRenderer.swift index 64844fd2e4..cc5c1e3106 100755 --- a/Charts/Classes/Renderers/PieChartRenderer.swift +++ b/Charts/Classes/Renderers/PieChartRenderer.swift @@ -23,7 +23,7 @@ public class PieChartRenderer: ChartDataRendererBase { public weak var chart: PieChartView? - public init(chart: PieChartView, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public init(chart: PieChartView?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) @@ -114,7 +114,7 @@ public class PieChartRenderer: ChartDataRendererBase for j in 0 ..< entryCount { guard let e = dataSet.entryForIndex(j) else { continue } - if ((abs(e.value) > 0.000001)) + if ((abs(e.y) > 0.000001)) { visibleAngleCount += 1 } @@ -132,9 +132,9 @@ public class PieChartRenderer: ChartDataRendererBase guard let e = dataSet.entryForIndex(j) else { continue } // draw only if the value is greater than zero - if ((abs(e.value) > 0.000001)) + if ((abs(e.y) > 0.000001)) { - if (!chart.needsHighlight(xIndex: e.xIndex, + if (!chart.needsHighlight(xValue: e.x, dataSetIndex: data.indexOfDataSet(dataSet))) { let accountForSliceSpacing = sliceSpace > 0.0 && sliceAngle <= 180.0 @@ -144,8 +144,8 @@ public class PieChartRenderer: ChartDataRendererBase let sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.0 : sliceSpace / (ChartUtils.Math.FDEG2RAD * radius) - let startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.0) * phaseY - var sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY + let startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.0) * CGFloat(phaseY) + var sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * CGFloat(phaseY) if (sweepAngleOuter < 0.0) { sweepAngleOuter = 0.0 @@ -178,7 +178,7 @@ public class PieChartRenderer: ChartDataRendererBase var minSpacedRadius = calculateMinimumRadiusForSpacedSlice( center: center, radius: radius, - angle: sliceAngle * phaseY, + angle: sliceAngle * CGFloat(phaseY), arcStartPointX: arcStartPointX, arcStartPointY: arcStartPointY, startAngle: startAngleOuter, @@ -193,8 +193,8 @@ public class PieChartRenderer: ChartDataRendererBase let sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.0 ? 0.0 : sliceSpace / (ChartUtils.Math.FDEG2RAD * innerRadius) - let startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.0) * phaseY - var sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY + let startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.0) * CGFloat(phaseY) + var sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * CGFloat(phaseY) if (sweepAngleInner < 0.0) { sweepAngleInner = 0.0 @@ -225,7 +225,7 @@ public class PieChartRenderer: ChartDataRendererBase calculateMinimumRadiusForSpacedSlice( center: center, radius: radius, - angle: sliceAngle * phaseY, + angle: sliceAngle * CGFloat(phaseY), arcStartPointX: arcStartPointX, arcStartPointY: arcStartPointY, startAngle: startAngleOuter, @@ -258,7 +258,7 @@ public class PieChartRenderer: ChartDataRendererBase } } - angle += sliceAngle * phaseX + angle += sliceAngle * CGFloat(phaseX) } CGContextRestoreGState(context) @@ -326,12 +326,8 @@ public class PieChartRenderer: ChartDataRendererBase for j in 0 ..< dataSet.entryCount { - if (drawXVals && !drawYVals && (j >= data.xValCount || data.xVals[j] == nil)) - { - continue - } - guard let e = dataSet.entryForIndex(j) else { continue } + let pe = e as? PieChartDataEntry if (xIndex == 0) { @@ -339,7 +335,7 @@ public class PieChartRenderer: ChartDataRendererBase } else { - angle = absoluteAngles[xIndex - 1] * phaseX + angle = absoluteAngles[xIndex - 1] * CGFloat(phaseX) } let sliceAngle = drawAngles[xIndex] @@ -351,9 +347,9 @@ public class PieChartRenderer: ChartDataRendererBase angle = angle + angleOffset - let transformedAngle = rotationAngle + angle * phaseY + let transformedAngle = rotationAngle + angle * CGFloat(phaseY) - let value = usePercentValuesEnabled ? e.value / yValueSum * 100.0 : e.value + let value = usePercentValuesEnabled ? e.y / yValueSum * 100.0 : e.y let valueText = formatter.stringFromNumber(value)! let sliceXBase = cos(transformedAngle * ChartUtils.Math.FDEG2RAD) @@ -432,22 +428,22 @@ public class PieChartRenderer: ChartDataRendererBase attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: dataSet.valueTextColorAt(j)] ) - if (j < data.xValCount && data.xVals[j] != nil) + if (j < data.entryCount && pe?.label != nil) { ChartUtils.drawText( context: context, - text: data.xVals[j]!, + text: pe!.label!, point: CGPoint(x: labelPoint.x, y: labelPoint.y + lineHeight), align: align, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: dataSet.valueTextColorAt(j)] ) } } - else if drawXOutside + else if drawXOutside && pe?.label != nil { ChartUtils.drawText( context: context, - text: data.xVals[j]!, + text: pe!.label!, point: CGPoint(x: labelPoint.x, y: labelPoint.y + lineHeight / 2.0), align: align, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: dataSet.valueTextColorAt(j)] @@ -481,22 +477,22 @@ public class PieChartRenderer: ChartDataRendererBase attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: dataSet.valueTextColorAt(j)] ) - if j < data.xValCount && data.xVals[j] != nil + if j < data.entryCount && pe?.label != nil { ChartUtils.drawText( context: context, - text: data.xVals[j]!, + text: pe!.label!, point: CGPoint(x: x, y: y + lineHeight), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: dataSet.valueTextColorAt(j)] ) } } - else if drawXInside + else if drawXInside && pe?.label != nil { ChartUtils.drawText( context: context, - text: data.xVals[j]!, + text: pe!.label!, point: CGPoint(x: x, y: y + lineHeight / 2.0), align: .Center, attributes: [NSFontAttributeName: valueFont, NSForegroundColorAttributeName: dataSet.valueTextColorAt(j)] @@ -561,7 +557,7 @@ public class PieChartRenderer: ChartDataRendererBase let secondHoleRadius = radius * chart.transparentCircleRadiusPercent // make transparent - CGContextSetAlpha(context, alpha); + CGContextSetAlpha(context, CGFloat(alpha)) CGContextSetFillColorWithColor(context, transparentCircleColor.CGColor) // draw the transparent-circle @@ -650,8 +646,8 @@ public class PieChartRenderer: ChartDataRendererBase for i in 0 ..< indices.count { // get the index to highlight - let xIndex = indices[i].xIndex - if (xIndex >= drawAngles.count) + let x = indices[i].x + if x >= Double(drawAngles.count) { continue } @@ -663,29 +659,31 @@ public class PieChartRenderer: ChartDataRendererBase continue } + let entryIndex = set.entryIndex(x: x, rounding: .Closest) + let entryCount = set.entryCount var visibleAngleCount = 0 for j in 0 ..< entryCount { guard let e = set.entryForIndex(j) else { continue } - if ((abs(e.value) > 0.000001)) + if ((abs(e.y) > 0.000001)) { visibleAngleCount += 1 } } - if (xIndex == 0) + if (x == 0) { angle = 0.0 } else { - angle = absoluteAngles[xIndex - 1] * phaseX + angle = absoluteAngles[entryIndex - 1] * CGFloat(phaseX) } let sliceSpace = visibleAngleCount <= 1 ? 0.0 : set.sliceSpace - let sliceAngle = drawAngles[xIndex] + let sliceAngle = drawAngles[entryIndex] var innerRadius = userInnerRadius let shift = set.selectionShift @@ -693,7 +691,7 @@ public class PieChartRenderer: ChartDataRendererBase let accountForSliceSpacing = sliceSpace > 0.0 && sliceAngle <= 180.0 - CGContextSetFillColorWithColor(context, set.colorAt(xIndex).CGColor) + CGContextSetFillColorWithColor(context, set.colorAt(entryIndex).CGColor) let sliceSpaceAngleOuter = visibleAngleCount == 1 ? 0.0 : @@ -703,15 +701,15 @@ public class PieChartRenderer: ChartDataRendererBase 0.0 : sliceSpace / (ChartUtils.Math.FDEG2RAD * highlightedRadius) - let startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.0) * phaseY - var sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * phaseY + let startAngleOuter = rotationAngle + (angle + sliceSpaceAngleOuter / 2.0) * CGFloat(phaseY) + var sweepAngleOuter = (sliceAngle - sliceSpaceAngleOuter) * CGFloat(phaseY) if (sweepAngleOuter < 0.0) { sweepAngleOuter = 0.0 } - let startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.0) * phaseY - var sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * phaseY + let startAngleShifted = rotationAngle + (angle + sliceSpaceAngleShifted / 2.0) * CGFloat(phaseY) + var sweepAngleShifted = (sliceAngle - sliceSpaceAngleShifted) * CGFloat(phaseY) if (sweepAngleShifted < 0.0) { sweepAngleShifted = 0.0 @@ -739,7 +737,7 @@ public class PieChartRenderer: ChartDataRendererBase sliceSpaceRadius = calculateMinimumRadiusForSpacedSlice( center: center, radius: radius, - angle: sliceAngle * phaseY, + angle: sliceAngle * CGFloat(phaseY), arcStartPointX: center.x + radius * cos(startAngleOuter * ChartUtils.Math.FDEG2RAD), arcStartPointY: center.y + radius * sin(startAngleOuter * ChartUtils.Math.FDEG2RAD), startAngle: startAngleOuter, @@ -762,8 +760,8 @@ public class PieChartRenderer: ChartDataRendererBase let sliceSpaceAngleInner = visibleAngleCount == 1 || innerRadius == 0.0 ? 0.0 : sliceSpace / (ChartUtils.Math.FDEG2RAD * innerRadius) - let startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.0) * phaseY - var sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * phaseY + let startAngleInner = rotationAngle + (angle + sliceSpaceAngleInner / 2.0) * CGFloat(phaseY) + var sweepAngleInner = (sliceAngle - sliceSpaceAngleInner) * CGFloat(phaseY) if (sweepAngleInner < 0.0) { sweepAngleInner = 0.0 diff --git a/Charts/Classes/Renderers/RadarChartRenderer.swift b/Charts/Classes/Renderers/RadarChartRenderer.swift index 4bfdda7671..02c6f30d79 100644 --- a/Charts/Classes/Renderers/RadarChartRenderer.swift +++ b/Charts/Classes/Renderers/RadarChartRenderer.swift @@ -23,7 +23,7 @@ public class RadarChartRenderer: LineRadarChartRenderer { public weak var chart: RadarChartView? - public init(chart: RadarChartView, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public init(chart: RadarChartView?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) @@ -38,15 +38,7 @@ public class RadarChartRenderer: LineRadarChartRenderer if (radarData != nil) { - var mostEntries = 0 - - for set in radarData!.dataSets - { - if set.entryCount > mostEntries - { - mostEntries = set.entryCount - } - } + let mostEntries = radarData?.maxEntryCountSet?.entryCount ?? 0 for set in radarData!.dataSets as! [IRadarChartDataSet] { @@ -91,8 +83,8 @@ public class RadarChartRenderer: LineRadarChartRenderer let p = ChartUtils.getPosition( center: center, - dist: CGFloat(e.value - chart.chartYMin) * factor * phaseY, - angle: sliceangle * CGFloat(j) * phaseX + chart.rotationAngle) + dist: CGFloat((e.y - chart.chartYMin) * Double(factor) * phaseY), + angle: sliceangle * CGFloat(j) * CGFloat(phaseX) + chart.rotationAngle) if p.x.isNaN { @@ -184,8 +176,8 @@ public class RadarChartRenderer: LineRadarChartRenderer let p = ChartUtils.getPosition( center: center, - dist: CGFloat(e.value) * factor * phaseY, - angle: sliceangle * CGFloat(j) * phaseX + chart.rotationAngle) + dist: CGFloat(e.y) * factor * CGFloat(phaseY), + angle: sliceangle * CGFloat(j) * CGFloat(phaseX) + chart.rotationAngle) let valueFont = dataSet.valueFont @@ -193,7 +185,7 @@ public class RadarChartRenderer: LineRadarChartRenderer ChartUtils.drawText( context: context, - text: formatter.stringFromNumber(e.value)!, + text: formatter.stringFromNumber(e.y)!, point: CGPoint(x: p.x, y: p.y - yoffset - valueFont.lineHeight), align: .Center, attributes: [NSFontAttributeName: valueFont, @@ -234,8 +226,9 @@ public class RadarChartRenderer: LineRadarChartRenderer CGContextSetAlpha(context, chart.webAlpha) let xIncrements = 1 + chart.skipWebLineCount - - for i in 0.stride(to: data.xValCount, by: xIncrements) + let maxEntryCount = chart.data?.maxEntryCountSet?.entryCount ?? 0 + + for i in 0.stride(to: maxEntryCount, by: xIncrements) { let p = ChartUtils.getPosition( center: center, @@ -259,7 +252,7 @@ public class RadarChartRenderer: LineRadarChartRenderer for j in 0 ..< labelCount { - for i in 0 ..< data.xValCount + for i in 0 ..< data.entryCount { let r = CGFloat(chart.yAxis.entries[j] - chart.chartYMin) * factor @@ -319,16 +312,16 @@ public class RadarChartRenderer: LineRadarChartRenderer CGContextSetStrokeColorWithColor(context, set.highlightColor.CGColor) // get the index to highlight - let xIndex = indices[i].xIndex + let x = indices[i].x - let e = set.entryForXIndex(xIndex) - if e?.xIndex != xIndex + let e = set.entryForXPos(x) + if e?.x != x { continue } let j = set.entryIndex(entry: e!) - let y = (e!.value - chart.chartYMin) + let y = (e!.y - chart.chartYMin) if (y.isNaN) { @@ -337,8 +330,8 @@ public class RadarChartRenderer: LineRadarChartRenderer _highlightPointBuffer = ChartUtils.getPosition( center: center, - dist: CGFloat(y) * factor * phaseY, - angle: sliceangle * CGFloat(j) * phaseX + chart.rotationAngle) + dist: CGFloat(y) * factor * CGFloat(phaseY), + angle: sliceangle * CGFloat(j) * CGFloat(phaseX) + chart.rotationAngle) // draw the lines drawHighlightLines(context: context, point: _highlightPointBuffer, set: set) diff --git a/Charts/Classes/Renderers/ScatterChartRenderer.swift b/Charts/Classes/Renderers/ScatterChartRenderer.swift index ce975fce41..1930a6d77e 100644 --- a/Charts/Classes/Renderers/ScatterChartRenderer.swift +++ b/Charts/Classes/Renderers/ScatterChartRenderer.swift @@ -23,7 +23,7 @@ public class ScatterChartRenderer: LineScatterCandleRadarChartRenderer { public weak var dataProvider: ScatterChartDataProvider? - public init(dataProvider: ScatterChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler) + public init(dataProvider: ScatterChartDataProvider?, animator: ChartAnimator?, viewPortHandler: ChartViewPortHandler?) { super.init(animator: animator, viewPortHandler: viewPortHandler) @@ -56,7 +56,8 @@ public class ScatterChartRenderer: LineScatterCandleRadarChartRenderer { guard let dataProvider = dataProvider, - animator = animator + animator = animator, + viewPortHandler = self.viewPortHandler else { return } let trans = dataProvider.getTransformer(dataSet.axisDependency) @@ -81,12 +82,12 @@ public class ScatterChartRenderer: LineScatterCandleRadarChartRenderer CGContextSaveGState(context) - for j in 0 ..< Int(min(ceil(CGFloat(entryCount) * animator.phaseX), CGFloat(entryCount))) + for j in 0 ..< Int(min(ceil(Double(entryCount) * animator.phaseX), Double(entryCount))) { guard let e = dataSet.entryForIndex(j) else { continue } - point.x = CGFloat(e.xIndex) - point.y = CGFloat(e.value) * phaseY + point.x = CGFloat(e.x) + point.y = CGFloat(e.y * phaseY) point = CGPointApplyAffineTransform(point, valueToPixelMatrix); if (!viewPortHandler.isInBoundsRight(point.x)) @@ -266,11 +267,12 @@ public class ScatterChartRenderer: LineScatterCandleRadarChartRenderer guard let dataProvider = dataProvider, scatterData = dataProvider.scatterData, - animator = animator + animator = animator, + viewPortHandler = self.viewPortHandler else { return } // if values are drawn - if (scatterData.yValCount < Int(ceil(CGFloat(dataProvider.maxVisibleValueCount) * viewPortHandler.scaleX))) + if isDrawingValuesAllowed(dataProvider: dataProvider) { guard let dataSets = scatterData.dataSets as? [IScatterChartDataSet] else { return } @@ -300,12 +302,12 @@ public class ScatterChartRenderer: LineScatterCandleRadarChartRenderer let shapeSize = dataSet.scatterShapeSize let lineHeight = valueFont.lineHeight - for j in 0 ..< Int(ceil(CGFloat(entryCount) * phaseX)) + for j in 0 ..< Int(ceil(Double(entryCount) * phaseX)) { guard let e = dataSet.entryForIndex(j) else { break } - pt.x = CGFloat(e.xIndex) - pt.y = CGFloat(e.value) * phaseY + pt.x = CGFloat(e.x) + pt.y = CGFloat(e.y * phaseY) pt = CGPointApplyAffineTransform(pt, valueToPixelMatrix) if (!viewPortHandler.isInBoundsRight(pt.x)) @@ -320,7 +322,7 @@ public class ScatterChartRenderer: LineScatterCandleRadarChartRenderer continue } - let text = formatter.stringFromNumber(e.value) + let text = formatter.stringFromNumber(e.y) ChartUtils.drawText( context: context, @@ -341,8 +343,6 @@ public class ScatterChartRenderer: LineScatterCandleRadarChartRenderer } - private var _highlightPointBuffer = CGPoint() - public override func drawHighlighted(context context: CGContext, indices: [ChartHighlight]) { guard let @@ -381,30 +381,20 @@ public class ScatterChartRenderer: LineScatterCandleRadarChartRenderer CGContextSetLineDash(context, 0.0, nil, 0) } - let xIndex = high.xIndex; // get the x-position + let x = high.x; // get the x-position + let y = high.y * Double(animator.phaseY) - if (CGFloat(xIndex) > CGFloat(chartXMax) * animator.phaseX) + if (x > chartXMax * animator.phaseX) { continue } - let yVal = set.yValForXIndex(xIndex) - if (yVal.isNaN) - { - continue - } - - let y = CGFloat(yVal) * animator.phaseY; // get the y-position - - _highlightPointBuffer.x = CGFloat(xIndex) - _highlightPointBuffer.y = y - let trans = dataProvider.getTransformer(set.axisDependency) - trans.pointValueToPixel(&_highlightPointBuffer) + let pt = trans.pixelForValue(x: x, y: y) // draw the lines - drawHighlightLines(context: context, point: _highlightPointBuffer, set: set) + drawHighlightLines(context: context, point: pt, set: set) } } diff --git a/Charts/Classes/Utils/ChartSelectionDetail.swift b/Charts/Classes/Utils/ChartSelectionDetail.swift index 89593efd46..44327cb703 100644 --- a/Charts/Classes/Utils/ChartSelectionDetail.swift +++ b/Charts/Classes/Utils/ChartSelectionDetail.swift @@ -17,61 +17,40 @@ import CoreGraphics public class ChartSelectionDetail: NSObject { - private var _y = CGFloat.NaN - private var _value = Double(0) - private var _dataIndex = Int(0) - private var _dataSetIndex = Int(0) - private var _dataSet: IChartDataSet! + public var x = CGFloat.NaN + public var y = CGFloat.NaN + public var xValue = Double(0) + public var yValue = Double(0) + public var dataIndex = Int(0) + public var dataSetIndex = Int(0) + public var dataSet: IChartDataSet! public override init() { super.init() } - public init(y: CGFloat, value: Double, dataIndex: Int, dataSetIndex: Int, dataSet: IChartDataSet) + public init(x: CGFloat, y: CGFloat, xValue: Double, yValue: Double, dataIndex: Int, dataSetIndex: Int, dataSet: IChartDataSet) { super.init() - _y = y - _value = value - _dataIndex = dataIndex - _dataSetIndex = dataSetIndex - _dataSet = dataSet + self.x = x + self.y = y + self.xValue = xValue + self.yValue = yValue + self.dataIndex = dataIndex + self.dataSetIndex = dataSetIndex + self.dataSet = dataSet } - public convenience init(y: CGFloat, value: Double, dataSetIndex: Int, dataSet: IChartDataSet) + public convenience init(x: CGFloat, y: CGFloat, xValue: Double, yValue: Double, dataSetIndex: Int, dataSet: IChartDataSet) { - self.init(y: y, value: value, dataIndex: 0, dataSetIndex: dataSetIndex, dataSet: dataSet) + self.init(x: x, y: y, xValue: xValue, yValue: yValue, dataIndex: 0, dataSetIndex: dataSetIndex, dataSet: dataSet) } - public convenience init(value: Double, dataSetIndex: Int, dataSet: IChartDataSet) + public convenience init(xValue: Double, yValue: Double, dataSetIndex: Int, dataSet: IChartDataSet) { - self.init(y: CGFloat.NaN, value: value, dataIndex: 0, dataSetIndex: dataSetIndex, dataSet: dataSet) - } - - public var y: CGFloat - { - return _y - } - - public var value: Double - { - return _value - } - - public var dataIndex: Int - { - return _dataIndex - } - - public var dataSetIndex: Int - { - return _dataSetIndex - } - - public var dataSet: IChartDataSet? - { - return _dataSet + self.init(x: CGFloat.NaN, y: CGFloat.NaN, xValue: xValue, yValue: yValue, dataIndex: 0, dataSetIndex: dataSetIndex, dataSet: dataSet) } // MARK: NSObject @@ -88,17 +67,22 @@ public class ChartSelectionDetail: NSObject return false } - if (object!.value != _value) + if (object!.xValue != self.xValue) { return false } - if (object!.dataSetIndex != _dataSetIndex) + if (object!.yValue != self.yValue) { return false } - if (object!.dataSet !== _dataSet) + if (object!.dataSetIndex != self.dataSetIndex) + { + return false + } + + if (object!.dataSet !== self.dataSet) { return false } @@ -119,7 +103,12 @@ public func ==(lhs: ChartSelectionDetail, rhs: ChartSelectionDetail) -> Bool return false } - if (lhs.value != rhs.value) + if (lhs.xValue != rhs.xValue) + { + return false + } + + if (lhs.yValue != rhs.yValue) { return false } diff --git a/Charts/Classes/Utils/ChartTransformer.swift b/Charts/Classes/Utils/ChartTransformer.swift index 811adb6854..4a82d875c6 100644 --- a/Charts/Classes/Utils/ChartTransformer.swift +++ b/Charts/Classes/Utils/ChartTransformer.swift @@ -64,40 +64,6 @@ public class ChartTransformer: NSObject _matrixOffset = CGAffineTransformTranslate(_matrixOffset, _viewPortHandler.offsetLeft, -_viewPortHandler.offsetTop) } } - - /// Transforms an Entry into a transformed point for bar chart - public func getTransformedValueBarChart(entry entry: ChartDataEntry, xIndex: Int, dataSetIndex: Int, phaseY: CGFloat, dataSetCount: Int, groupSpace: CGFloat) -> CGPoint - { - // calculate the x-position, depending on datasetcount - let x = CGFloat(xIndex + (xIndex * (dataSetCount - 1)) + dataSetIndex) + groupSpace * CGFloat(xIndex) + groupSpace / 2.0 - let y = entry.value - - var valuePoint = CGPoint( - x: x, - y: CGFloat(y) * phaseY - ) - - pointValueToPixel(&valuePoint) - - return valuePoint - } - - /// Transforms an Entry into a transformed point for horizontal bar chart - public func getTransformedValueHorizontalBarChart(entry entry: ChartDataEntry, xIndex: Int, dataSetIndex: Int, phaseY: CGFloat, dataSetCount: Int, groupSpace: CGFloat) -> CGPoint - { - // calculate the x-position, depending on datasetcount - let x = CGFloat(xIndex + (xIndex * (dataSetCount - 1)) + dataSetIndex) + groupSpace * CGFloat(xIndex) + groupSpace / 2.0 - let y = entry.value - - var valuePoint = CGPoint( - x: CGFloat(y) * phaseY, - y: x - ) - - pointValueToPixel(&valuePoint) - - return valuePoint - } /// Transform an array of points with all matrices. // VERY IMPORTANT: Keep matrix order "value-touch-offset" when transforming. @@ -115,6 +81,11 @@ public class ChartTransformer: NSObject point = CGPointApplyAffineTransform(point, valueToPixelMatrix) } + public func pixelForValue(x x: Double, y: Double) -> CGPoint + { + return CGPointApplyAffineTransform(CGPoint(x: x, y: y), valueToPixelMatrix) + } + /// Transform a rectangle with all matrices. public func rectValueToPixel(inout r: CGRect) { @@ -122,12 +93,12 @@ public class ChartTransformer: NSObject } /// Transform a rectangle with all matrices with potential animation phases. - public func rectValueToPixel(inout r: CGRect, phaseY: CGFloat) + public func rectValueToPixel(inout r: CGRect, phaseY: Double) { // multiply the height of the rect with the phase var bottom = r.origin.y + r.size.height - bottom *= phaseY - let top = r.origin.y * phaseY + bottom *= CGFloat(phaseY) + let top = r.origin.y * CGFloat(phaseY) r.size.height = bottom - top r.origin.y = top @@ -141,12 +112,11 @@ public class ChartTransformer: NSObject } /// Transform a rectangle with all matrices with potential animation phases. - public func rectValueToPixelHorizontal(inout r: CGRect, phaseY: CGFloat) + public func rectValueToPixelHorizontal(inout r: CGRect, phaseY: Double) { // multiply the height of the rect with the phase - var right = r.origin.x + r.size.width - right *= phaseY - let left = r.origin.x * phaseY + let left = r.origin.x * CGFloat(phaseY) + let right = (r.origin.x + r.size.width) * CGFloat(phaseY) r.size.width = right - left r.origin.x = left @@ -182,13 +152,21 @@ public class ChartTransformer: NSObject } /// - returns: the x and y values in the chart at the given touch point - /// (encapsulated in a PointD). This method transforms pixel coordinates to + /// (encapsulated in a CGPoint). This method transforms pixel coordinates to /// coordinates / values in the chart. - public func getValueByTouchPoint(point: CGPoint) -> CGPoint + public func valueForTouchPoint(point: CGPoint) -> CGPoint { return CGPointApplyAffineTransform(point, pixelToValueMatrix) } + /// - returns: the x and y values in the chart at the given touch point + /// (x/y). This method transforms pixel coordinates to + /// coordinates / values in the chart. + public func valueForTouchPoint(x x: CGFloat, y: CGFloat) -> CGPoint + { + return CGPointApplyAffineTransform(CGPoint(x: x, y: y), pixelToValueMatrix) + } + public var valueToPixelMatrix: CGAffineTransform { return diff --git a/Charts/Classes/Utils/ChartUtils.swift b/Charts/Classes/Utils/ChartUtils.swift index 8cc191f0d9..6331d9a529 100755 --- a/Charts/Classes/Utils/ChartUtils.swift +++ b/Charts/Classes/Utils/ChartUtils.swift @@ -68,15 +68,6 @@ public class ChartUtils } } - /// - returns: the index of the DataSet that contains the closest value on the y-axis - internal class func closestDataSetIndexByPixelY( - valsAtIndex valsAtIndex: [ChartSelectionDetail], - y: CGFloat, - axis: ChartYAxis.AxisDependency?) -> Int? - { - return closestSelectionDetailByPixelY(valsAtIndex: valsAtIndex, y: y, axis: axis)?.dataSetIndex - } - /// - returns: the index of the DataSet that contains the closest value on the y-axis internal class func closestDataSetIndexByValue( valsAtIndex valsAtIndex: [ChartSelectionDetail], @@ -86,33 +77,6 @@ public class ChartUtils return closestSelectionDetailByValue(valsAtIndex: valsAtIndex, value: value, axis: axis)?.dataSetIndex } - /// - returns: the `ChartSelectionDetail` of the closest value on the y-axis - internal class func closestSelectionDetailByPixelY( - valsAtIndex valsAtIndex: [ChartSelectionDetail], - y: CGFloat, - axis: ChartYAxis.AxisDependency?) -> ChartSelectionDetail? - { - var distance = CGFloat.max - var detail: ChartSelectionDetail? - - for i in 0 ..< valsAtIndex.count - { - let sel = valsAtIndex[i] - - if (axis == nil || sel.dataSet?.axisDependency == axis) - { - let cdistance = abs(sel.y - y) - if (cdistance < distance) - { - detail = sel - distance = cdistance - } - } - } - - return detail - } - /// - returns: the `ChartSelectionDetail` of the closest value on the y-axis internal class func closestSelectionDetailByValue( valsAtIndex valsAtIndex: [ChartSelectionDetail], @@ -126,10 +90,10 @@ public class ChartUtils { let sel = valsAtIndex[i] - if (axis == nil || sel.dataSet?.axisDependency == axis) + if axis == nil || sel.dataSet?.axisDependency == axis { - let cdistance = abs(sel.value - value) - if (cdistance < distance) + let cdistance = abs(sel.yValue - value) + if cdistance < distance { detail = sel distance = cdistance @@ -140,31 +104,6 @@ public class ChartUtils return detail } - /// - returns: the minimum distance from a touch-y-value (in pixels) to the closest y-value (in pixels) that is displayed in the chart. - internal class func getMinimumDistance( - valsAtIndex: [ChartSelectionDetail], - y: CGFloat, - axis: ChartYAxis.AxisDependency) -> CGFloat - { - var distance = CGFloat.max - - for i in 0 ..< valsAtIndex.count - { - let sel = valsAtIndex[i] - - if (sel.dataSet!.axisDependency == axis) - { - let cdistance = abs(sel.y - y) - if (cdistance < distance) - { - distance = cdistance - } - } - } - - return distance - } - /// Calculates the position around a center point, depending on the distance from the center, and the angle of the position around the center. internal class func getPosition(center center: CGPoint, dist: CGFloat, angle: CGFloat) -> CGPoint { diff --git a/Charts/Classes/Utils/ChartViewPortHandler.swift b/Charts/Classes/Utils/ChartViewPortHandler.swift index 606063ca74..f932ff66ea 100755 --- a/Charts/Classes/Utils/ChartViewPortHandler.swift +++ b/Charts/Classes/Utils/ChartViewPortHandler.swift @@ -424,13 +424,13 @@ public class ChartViewPortHandler: NSObject public func isInBoundsLeft(x: CGFloat) -> Bool { - return _contentRect.origin.x <= x ? true : false + return _contentRect.origin.x <= x + 1.0 ? true : false } public func isInBoundsRight(x: CGFloat) -> Bool { - let normalizedX = CGFloat(Int(x * 100.0)) / 100.0 - return (_contentRect.origin.x + _contentRect.size.width) >= normalizedX ? true : false + let x = CGFloat(Int(x * 100.0)) / 100.0 + return (_contentRect.origin.x + _contentRect.size.width) >= x - 1.0 ? true : false } public func isInBoundsTop(y: CGFloat) -> Bool diff --git a/ChartsDemo/ChartsDemo.xcodeproj/project.pbxproj b/ChartsDemo/ChartsDemo.xcodeproj/project.pbxproj index 314d7ec2a3..82ae8058b7 100644 --- a/ChartsDemo/ChartsDemo.xcodeproj/project.pbxproj +++ b/ChartsDemo/ChartsDemo.xcodeproj/project.pbxproj @@ -39,7 +39,6 @@ 5B57BBB51A9B26AA0036A6CC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B57BBB41A9B26AA0036A6CC /* main.m */; }; 5B57BBB81A9B26AA0036A6CC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B57BBB71A9B26AA0036A6CC /* AppDelegate.m */; }; 5B57BBBB1A9B26AA0036A6CC /* DemoListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B57BBBA1A9B26AA0036A6CC /* DemoListViewController.m */; }; - 5B6654D11BB0A36F00890030 /* MyCustomXValueFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B6654D01BB0A36F00890030 /* MyCustomXValueFormatter.m */; }; 5B7B3AD81C437CBC001C109B /* RealmDemoListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B7B3AD61C437CBC001C109B /* RealmDemoListViewController.m */; }; 5B7B3AD91C437CBC001C109B /* RealmDemoListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5B7B3AD71C437CBC001C109B /* RealmDemoListViewController.xib */; }; 5B7B3ADF1C437F61001C109B /* RealmCandleChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B7B3ADD1C437F61001C109B /* RealmCandleChartViewController.m */; }; @@ -68,6 +67,8 @@ 5BDEDC421ABB7F73007D3A60 /* HorizontalBarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BDEDC401ABB7F73007D3A60 /* HorizontalBarChartViewController.xib */; }; 5BDEDC471ABB871E007D3A60 /* CombinedChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BDEDC451ABB871E007D3A60 /* CombinedChartViewController.m */; }; 5BDEDC481ABB871E007D3A60 /* CombinedChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BDEDC461ABB871E007D3A60 /* CombinedChartViewController.xib */; }; + 5BE377DE1D425151006EB34F /* DayAxisValueFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BE377DD1D425151006EB34F /* DayAxisValueFormatter.m */; }; + 5BE377F21D47FDF1006EB34F /* IntAxisValueFormatter.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BE377F11D47FDF1006EB34F /* IntAxisValueFormatter.m */; }; 5BE7E7621C693098000A0377 /* PositiveNegativeBarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BE7E7601C693098000A0377 /* PositiveNegativeBarChartViewController.m */; }; 5BE7E7631C693098000A0377 /* PositiveNegativeBarChartViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5BE7E7611C693098000A0377 /* PositiveNegativeBarChartViewController.xib */; }; 5BEAED121ABBFB2B0013F194 /* AnotherBarChartViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5BEAED101ABBFB2B0013F194 /* AnotherBarChartViewController.m */; }; @@ -149,6 +150,13 @@ remoteGlobalIDString = 5BA8EC3F1A9D14DC00CE82E1; remoteInfo = "ChartsRealm-iOS"; }; + 5BE377DA1D42511A006EB34F /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5B8E088D1C635B6D00438BAF /* ChartsRealm.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 5BA899431CADB7F20012ED64; + remoteInfo = "ChartsRealm-OSX"; + }; 65B3F6561C73B4F6000983D0 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 5B8E08771C635B5200438BAF /* Charts.xcodeproj */; @@ -218,8 +226,6 @@ 5B57BBB71A9B26AA0036A6CC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 5B57BBB91A9B26AA0036A6CC /* DemoListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DemoListViewController.h; sourceTree = ""; }; 5B57BBBA1A9B26AA0036A6CC /* DemoListViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = DemoListViewController.m; sourceTree = ""; }; - 5B6654CF1BB0A36F00890030 /* MyCustomXValueFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MyCustomXValueFormatter.h; sourceTree = ""; }; - 5B6654D01BB0A36F00890030 /* MyCustomXValueFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MyCustomXValueFormatter.m; sourceTree = ""; }; 5B7B3AD51C437CBC001C109B /* RealmDemoListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RealmDemoListViewController.h; sourceTree = ""; }; 5B7B3AD61C437CBC001C109B /* RealmDemoListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RealmDemoListViewController.m; sourceTree = ""; }; 5B7B3AD71C437CBC001C109B /* RealmDemoListViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RealmDemoListViewController.xib; sourceTree = ""; }; @@ -258,6 +264,10 @@ 5BDEDC441ABB871E007D3A60 /* CombinedChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CombinedChartViewController.h; sourceTree = ""; }; 5BDEDC451ABB871E007D3A60 /* CombinedChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CombinedChartViewController.m; sourceTree = ""; }; 5BDEDC461ABB871E007D3A60 /* CombinedChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = CombinedChartViewController.xib; sourceTree = ""; }; + 5BE377DC1D425151006EB34F /* DayAxisValueFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DayAxisValueFormatter.h; sourceTree = ""; }; + 5BE377DD1D425151006EB34F /* DayAxisValueFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DayAxisValueFormatter.m; sourceTree = ""; }; + 5BE377F01D47FDF1006EB34F /* IntAxisValueFormatter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntAxisValueFormatter.h; sourceTree = ""; }; + 5BE377F11D47FDF1006EB34F /* IntAxisValueFormatter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntAxisValueFormatter.m; sourceTree = ""; }; 5BE7E75F1C693098000A0377 /* PositiveNegativeBarChartViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PositiveNegativeBarChartViewController.h; sourceTree = ""; }; 5BE7E7601C693098000A0377 /* PositiveNegativeBarChartViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PositiveNegativeBarChartViewController.m; sourceTree = ""; }; 5BE7E7611C693098000A0377 /* PositiveNegativeBarChartViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PositiveNegativeBarChartViewController.xib; sourceTree = ""; }; @@ -379,6 +389,7 @@ 5B7B3AD71C437CBC001C109B /* RealmDemoListViewController.xib */, 5B8EAF251AB32CF5009697AA /* DemoBaseViewController.h */, 5B8EAF261AB32CF5009697AA /* DemoBaseViewController.m */, + 5BE377D41D42511A006EB34F /* Formatters */, 5BD47E541ABB0177008FCEC6 /* Components */, 5BD8F06F1AB89C7100566E05 /* Demos */, 65BF12571BFBC6EE005C28D9 /* RealmBase */, @@ -413,6 +424,7 @@ children = ( 5B8E08941C635B6D00438BAF /* ChartsRealm.framework */, 5B8E08961C635B6D00438BAF /* ChartsRealm.framework */, + 5BE377DB1D42511A006EB34F /* ChartsRealm.framework */, ); name = Products; sourceTree = ""; @@ -431,8 +443,6 @@ isa = PBXGroup; children = ( 5BD47E5A1ABB0263008FCEC6 /* BalloonMarker.swift */, - 5B6654CF1BB0A36F00890030 /* MyCustomXValueFormatter.h */, - 5B6654D01BB0A36F00890030 /* MyCustomXValueFormatter.m */, ); path = Components; sourceTree = ""; @@ -504,6 +514,17 @@ path = Demos; sourceTree = ""; }; + 5BE377D41D42511A006EB34F /* Formatters */ = { + isa = PBXGroup; + children = ( + 5BE377DC1D425151006EB34F /* DayAxisValueFormatter.h */, + 5BE377DD1D425151006EB34F /* DayAxisValueFormatter.m */, + 5BE377F01D47FDF1006EB34F /* IntAxisValueFormatter.h */, + 5BE377F11D47FDF1006EB34F /* IntAxisValueFormatter.m */, + ); + path = Formatters; + sourceTree = ""; + }; 65BF124F1BFBBF9A005C28D9 /* Frameworks */ = { isa = PBXGroup; children = ( @@ -664,6 +685,13 @@ remoteRef = 5B8E08951C635B6D00438BAF /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + 5BE377DB1D42511A006EB34F /* ChartsRealm.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = ChartsRealm.framework; + remoteRef = 5BE377DA1D42511A006EB34F /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; 65B3F6571C73B4F6000983D0 /* Charts.framework */ = { isa = PBXReferenceProxy; fileType = wrapper.framework; @@ -740,8 +768,10 @@ 5B57BBBB1A9B26AA0036A6CC /* DemoListViewController.m in Sources */, 5BD47E651ABB424E008FCEC6 /* BarChartViewController.m in Sources */, 5BDEDC471ABB871E007D3A60 /* CombinedChartViewController.m in Sources */, + 5BE377F21D47FDF1006EB34F /* IntAxisValueFormatter.m in Sources */, 5BD8F0741AB89CE500566E05 /* LineChart1ViewController.m in Sources */, 65BF125D1BFBC702005C28D9 /* RealmFloat.m in Sources */, + 5BE377DE1D425151006EB34F /* DayAxisValueFormatter.m in Sources */, 5BEAED401ABC1AC60013F194 /* SinusBarChartViewController.m in Sources */, 5BE7E7621C693098000A0377 /* PositiveNegativeBarChartViewController.m in Sources */, 5B7B3AE91C4445E7001C109B /* RealmScatterChartViewController.m in Sources */, @@ -770,7 +800,6 @@ 65BF12B01BFC9B00005C28D9 /* RealmLineChartViewController.m in Sources */, 5B57BBB51A9B26AA0036A6CC /* main.m in Sources */, 5BEAED361ABC192F0013F194 /* RadarChartViewController.m in Sources */, - 5B6654D11BB0A36F00890030 /* MyCustomXValueFormatter.m in Sources */, 5B9624411B38608C007763E2 /* NegativeStackedBarChartViewController.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ChartsDemo/Classes/Components/BalloonMarker.swift b/ChartsDemo/Classes/Components/BalloonMarker.swift index 57fb29e031..77afb8c1b3 100644 --- a/ChartsDemo/Classes/Components/BalloonMarker.swift +++ b/ChartsDemo/Classes/Components/BalloonMarker.swift @@ -104,7 +104,7 @@ public class BalloonMarker: ChartMarker public override func refreshContent(entry entry: ChartDataEntry, highlight: ChartHighlight) { - let label = entry.value.description + let label = entry.y.description labelns = label as NSString _drawAttributes.removeAll() diff --git a/ChartsDemo/Classes/Components/MyCustomXValueFormatter.h b/ChartsDemo/Classes/Components/MyCustomXValueFormatter.h deleted file mode 100644 index 1db2071220..0000000000 --- a/ChartsDemo/Classes/Components/MyCustomXValueFormatter.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// MyCustomXValueFormatter.h -// ChartsDemo -// -// Created by Daniel Cohen Gindi on 21/9/15. -// Copyright © 2015 dcg. All rights reserved. -// - -#import -#import - -@interface MyCustomXValueFormatter : NSObject - -@end diff --git a/ChartsDemo/Classes/Components/MyCustomXValueFormatter.m b/ChartsDemo/Classes/Components/MyCustomXValueFormatter.m deleted file mode 100644 index 60f0b01d6c..0000000000 --- a/ChartsDemo/Classes/Components/MyCustomXValueFormatter.m +++ /dev/null @@ -1,36 +0,0 @@ -// -// MyCustomXValueFormatter.m -// ChartsDemo -// -// Created by Daniel Cohen Gindi on 21/9/15. -// Copyright © 2015 dcg. All rights reserved. -// - -#import "MyCustomXValueFormatter.h" - -@implementation MyCustomXValueFormatter - -- (NSString *)stringForXValue:(NSInteger)index - original:(NSString *)original - viewPortHandler:(ChartViewPortHandler *)viewPortHandler -{ - // e.g. adjust the x-axis values depending on scale / zoom level - if (viewPortHandler.scaleX > 5.f) - { - return @"4"; - } - else if (viewPortHandler.scaleX > 3.f) - { - return @"3"; - } - else if (viewPortHandler.scaleX > 1.f) - { - return @"2"; - } - else - { - return original; - } -} - -@end diff --git a/ChartsDemo/Classes/DemoBaseViewController.h b/ChartsDemo/Classes/DemoBaseViewController.h index a921c448a4..1a3efea096 100644 --- a/ChartsDemo/Classes/DemoBaseViewController.h +++ b/ChartsDemo/Classes/DemoBaseViewController.h @@ -17,7 +17,6 @@ @interface DemoBaseViewController : UIViewController { @protected - NSArray *months; NSArray *parties; } diff --git a/ChartsDemo/Classes/DemoBaseViewController.m b/ChartsDemo/Classes/DemoBaseViewController.m index 00f151ab3d..2bb24eda44 100644 --- a/ChartsDemo/Classes/DemoBaseViewController.m +++ b/ChartsDemo/Classes/DemoBaseViewController.m @@ -45,11 +45,6 @@ - (void)initialize { self.edgesForExtendedLayout = UIRectEdgeNone; - months = @[ - @"Jan", @"Feb", @"Mar", @"Apr", @"May", @"Jun", @"Jul", @"Aug", @"Sep", - @"Oct", @"Nov", @"Dec" - ]; - parties = @[ @"Party A", @"Party B", @"Party C", @"Party D", @"Party E", @"Party F", @"Party G", @"Party H", @"Party I", @"Party J", @"Party K", @"Party L", @@ -132,14 +127,6 @@ - (void)handleOption:(NSString *)key forChartView:(ChartViewBase *)chartView [chartView notifyDataSetChanged]; } - if ([key isEqualToString:@"toggleHighlightArrow"]) - { - BarChartView *barChart = (BarChartView *)chartView; - barChart.drawHighlightArrowEnabled = !barChart.isDrawHighlightArrowEnabled; - - [chartView setNeedsDisplay]; - } - if ([key isEqualToString:@"toggleData"]) { _shouldHideData = !_shouldHideData; diff --git a/ChartsDemo/Classes/DemoListViewController.m b/ChartsDemo/Classes/DemoListViewController.m index c12cb3b473..5475570514 100644 --- a/ChartsDemo/Classes/DemoListViewController.m +++ b/ChartsDemo/Classes/DemoListViewController.m @@ -156,6 +156,7 @@ - (void)viewDidLoad @"class": RealmDemoListViewController.class } ]; + //FIXME: Add TimeLineChart } - (void)didReceiveMemoryWarning diff --git a/ChartsDemo/Classes/Demos/AnotherBarChartViewController.m b/ChartsDemo/Classes/Demos/AnotherBarChartViewController.m index 560aafdc87..17c86a8d9b 100644 --- a/ChartsDemo/Classes/Demos/AnotherBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/AnotherBarChartViewController.m @@ -35,7 +35,6 @@ - (void)viewDidLoad self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -50,14 +49,13 @@ - (void)viewDidLoad _chartView.descriptionText = @""; _chartView.noDataTextDescription = @"You need to provide data for the chart."; - _chartView.maxVisibleValueCount = 60; + _chartView.maxVisibleCount = 60; _chartView.pinchZoomEnabled = NO; _chartView.drawBarShadowEnabled = NO; _chartView.drawGridBackgroundEnabled = NO; ChartXAxis *xAxis = _chartView.xAxis; xAxis.labelPosition = XAxisLabelPositionBottom; - xAxis.spaceBetweenLabels = 0.0; xAxis.drawGridLinesEnabled = NO; _chartView.leftAxis.drawGridLinesEnabled = NO; @@ -95,35 +93,29 @@ - (void)setDataCount:(int)count range:(double)range { double mult = (range + 1); double val = (double) (arc4random_uniform(mult)) + mult / 3.0; - [yVals addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; - } - - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - for (int i = 0; i < count; i++) - { - [xVals addObject:[@((int)((BarChartDataEntry *)yVals[i]).value) stringValue]]; + [yVals addObject:[[BarChartDataEntry alloc] initWithX:i y:val]]; } BarChartDataSet *set1 = nil; if (_chartView.data.dataSetCount > 0) { set1 = (BarChartDataSet *)_chartView.data.dataSets[0]; - set1.yVals = yVals; - _chartView.data.xValsObjc = xVals; + set1.values = yVals; [_chartView notifyDataSetChanged]; } else { - set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"DataSet"]; + set1 = [[BarChartDataSet alloc] initWithValues:yVals label:@"DataSet"]; set1.colors = ChartColorTemplates.vordiplom; set1.drawValuesEnabled = NO; NSMutableArray *dataSets = [[NSMutableArray alloc] init]; [dataSets addObject:set1]; - BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + BarChartData *data = [[BarChartData alloc] initWithDataSets:dataSets]; _chartView.data = data; + _chartView.fitBars = YES; } [_chartView setNeedsDisplay]; diff --git a/ChartsDemo/Classes/Demos/BarChartViewController.m b/ChartsDemo/Classes/Demos/BarChartViewController.m index 3bbd29e071..6d907f1856 100644 --- a/ChartsDemo/Classes/Demos/BarChartViewController.m +++ b/ChartsDemo/Classes/Demos/BarChartViewController.m @@ -13,6 +13,7 @@ #import "BarChartViewController.h" #import "ChartsDemo-Swift.h" +#import "DayAxisValueFormatter.h" @interface BarChartViewController () @@ -35,7 +36,6 @@ - (void)viewDidLoad self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -53,21 +53,26 @@ - (void)viewDidLoad _chartView.drawBarShadowEnabled = NO; _chartView.drawValueAboveBarEnabled = YES; - _chartView.maxVisibleValueCount = 60; + _chartView.maxVisibleCount = 60; ChartXAxis *xAxis = _chartView.xAxis; xAxis.labelPosition = XAxisLabelPositionBottom; xAxis.labelFont = [UIFont systemFontOfSize:10.f]; xAxis.drawGridLinesEnabled = NO; - xAxis.spaceBetweenLabels = 2.0; + xAxis.granularity = 1.0; // only intervals of 1 day + xAxis.labelCount = 7; + xAxis.valueFormatter = [[DayAxisValueFormatter alloc] initForChart:_chartView]; + + NSNumberFormatter *leftAxisFormatter = [[NSNumberFormatter alloc] init]; + leftAxisFormatter.minimumFractionDigits = 0; + leftAxisFormatter.maximumFractionDigits = 1; + leftAxisFormatter.negativeSuffix = @" $"; + leftAxisFormatter.positiveSuffix = @" $"; ChartYAxis *leftAxis = _chartView.leftAxis; leftAxis.labelFont = [UIFont systemFontOfSize:10.f]; leftAxis.labelCount = 8; - leftAxis.valueFormatter = [[NSNumberFormatter alloc] init]; - leftAxis.valueFormatter.maximumFractionDigits = 1; - leftAxis.valueFormatter.negativeSuffix = @" $"; - leftAxis.valueFormatter.positiveSuffix = @" $"; + leftAxis.valueFormatter = [[ChartDefaultAxisValueFormatter alloc] initWithFormatter:leftAxisFormatter]; leftAxis.labelPosition = YAxisLabelPositionOutsideChart; leftAxis.spaceTop = 0.15; leftAxis.axisMinValue = 0.0; // this replaces startAtZero = YES @@ -111,43 +116,41 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; + double start = 0.0; - for (int i = 0; i < count; i++) - { - [xVals addObject:months[i % 12]]; - } + _chartView.xAxis.axisMinValue = start; + _chartView.xAxis.axisMaxValue = start + count + 2; NSMutableArray *yVals = [[NSMutableArray alloc] init]; - for (int i = 0; i < count; i++) + for (int i = start; i < start + count + 1; i++) { double mult = (range + 1); - double val = (double) (arc4random_uniform(mult)); - [yVals addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + double val = (double) (arc4random_uniform(mult)); + [yVals addObject:[[BarChartDataEntry alloc] initWithX:(double)i + 1.0 y:val]]; } BarChartDataSet *set1 = nil; if (_chartView.data.dataSetCount > 0) { set1 = (BarChartDataSet *)_chartView.data.dataSets[0]; - set1.yVals = yVals; - _chartView.data.xValsObjc = xVals; + set1.values = yVals; [_chartView.data notifyDataChanged]; [_chartView notifyDataSetChanged]; } else { - set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"DataSet"]; - set1.barSpace = 0.35; + set1 = [[BarChartDataSet alloc] initWithValues:yVals label:@"The year 2017"]; [set1 setColors:ChartColorTemplates.material]; NSMutableArray *dataSets = [[NSMutableArray alloc] init]; [dataSets addObject:set1]; - BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + BarChartData *data = [[BarChartData alloc] initWithDataSets:dataSets]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]]; + data.barWidth = 0.9f; + _chartView.data = data; } } @@ -161,7 +164,7 @@ - (void)optionTapped:(NSString *)key - (IBAction)slidersValueChanged:(id)sender { - _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + _sliderTextX.text = [@((int)_sliderX.value + 2) stringValue]; _sliderTextY.text = [@((int)_sliderY.value) stringValue]; [self updateChartData]; diff --git a/ChartsDemo/Classes/Demos/BubbleChartViewController.m b/ChartsDemo/Classes/Demos/BubbleChartViewController.m index aaec6fefdd..e37333e06b 100644 --- a/ChartsDemo/Classes/Demos/BubbleChartViewController.m +++ b/ChartsDemo/Classes/Demos/BubbleChartViewController.m @@ -50,7 +50,7 @@ - (void)viewDidLoad _chartView.drawGridBackgroundEnabled = NO; _chartView.dragEnabled = YES; [_chartView setScaleEnabled:YES]; - _chartView.maxVisibleValueCount = 200; + _chartView.maxVisibleCount = 200; _chartView.pinchZoomEnabled = YES; ChartLegend *l = _chartView.legend; @@ -69,7 +69,7 @@ - (void)viewDidLoad xl.labelPosition = XAxisLabelPositionBottom; xl.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; - _sliderX.value = 5.0; + _sliderX.value = 1.0; _sliderY.value = 50.0; [self slidersValueChanged:nil]; } @@ -88,18 +88,11 @@ - (void)updateChartData return; } - [self setDataCount:(_sliderX.value + 1) range:_sliderY.value]; + [self setDataCount:_sliderX.value range:_sliderY.value]; } - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:[@(i) stringValue]]; - } - NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; NSMutableArray *yVals3 = [[NSMutableArray alloc] init]; @@ -108,24 +101,24 @@ - (void)setDataCount:(int)count range:(double)range { double val = (double) (arc4random_uniform(range)); double size = (double) (arc4random_uniform(range)); - [yVals1 addObject:[[BubbleChartDataEntry alloc] initWithXIndex:i value:val size:size]]; + [yVals1 addObject:[[BubbleChartDataEntry alloc] initWithX:i y:val size:size]]; val = (double) (arc4random_uniform(range)); size = (double) (arc4random_uniform(range)); - [yVals2 addObject:[[BubbleChartDataEntry alloc] initWithXIndex:i value:val size:size]]; + [yVals2 addObject:[[BubbleChartDataEntry alloc] initWithX:i y:val size:size]]; val = (double) (arc4random_uniform(range)); size = (double) (arc4random_uniform(range)); - [yVals3 addObject:[[BubbleChartDataEntry alloc] initWithXIndex:i value:val size:size]]; + [yVals3 addObject:[[BubbleChartDataEntry alloc] initWithX:i y:val size:size]]; } - BubbleChartDataSet *set1 = [[BubbleChartDataSet alloc] initWithYVals:yVals1 label:@"DS 1"]; + BubbleChartDataSet *set1 = [[BubbleChartDataSet alloc] initWithValues:yVals1 label:@"DS 1"]; [set1 setColor:ChartColorTemplates.colorful[0] alpha:0.50f]; [set1 setDrawValuesEnabled:YES]; - BubbleChartDataSet *set2 = [[BubbleChartDataSet alloc] initWithYVals:yVals2 label:@"DS 2"]; + BubbleChartDataSet *set2 = [[BubbleChartDataSet alloc] initWithValues:yVals2 label:@"DS 2"]; [set2 setColor:ChartColorTemplates.colorful[1] alpha:0.50f]; [set2 setDrawValuesEnabled:YES]; - BubbleChartDataSet *set3 = [[BubbleChartDataSet alloc] initWithYVals:yVals3 label:@"DS 3"]; + BubbleChartDataSet *set3 = [[BubbleChartDataSet alloc] initWithValues:yVals3 label:@"DS 3"]; [set3 setColor:ChartColorTemplates.colorful[2] alpha:0.50f]; [set3 setDrawValuesEnabled:YES]; @@ -134,7 +127,8 @@ - (void)setDataCount:(int)count range:(double)range [dataSets addObject:set2]; [dataSets addObject:set3]; - BubbleChartData *data = [[BubbleChartData alloc] initWithXVals:xVals dataSets:dataSets]; + BubbleChartData *data = [[BubbleChartData alloc] initWithDataSets:dataSets]; + [data setDrawValues:NO]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]]; [data setHighlightCircleWidth: 1.5]; [data setValueTextColor:UIColor.whiteColor]; diff --git a/ChartsDemo/Classes/Demos/CandleStickChartViewController.m b/ChartsDemo/Classes/Demos/CandleStickChartViewController.m index 462ed8daa4..5aab828294 100644 --- a/ChartsDemo/Classes/Demos/CandleStickChartViewController.m +++ b/ChartsDemo/Classes/Demos/CandleStickChartViewController.m @@ -50,13 +50,12 @@ - (void)viewDidLoad _chartView.descriptionText = @""; _chartView.noDataTextDescription = @"You need to provide data for the chart."; - _chartView.maxVisibleValueCount = 60; + _chartView.maxVisibleCount = 60; _chartView.pinchZoomEnabled = NO; _chartView.drawGridBackgroundEnabled = NO; ChartXAxis *xAxis = _chartView.xAxis; xAxis.labelPosition = XAxisLabelPositionBottom; - xAxis.spaceBetweenLabels = 2.0; xAxis.drawGridLinesEnabled = NO; ChartYAxis *leftAxis = _chartView.leftAxis; @@ -93,13 +92,6 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:[@(i + 1990) stringValue]]; - } - NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; for (int i = 0; i < count; i++) @@ -111,10 +103,10 @@ - (void)setDataCount:(int)count range:(double)range double open = (double) (arc4random_uniform(6)) + 1.0; double close = (double) (arc4random_uniform(6)) + 1.0; BOOL even = i % 2 == 0; - [yVals1 addObject:[[CandleChartDataEntry alloc] initWithXIndex:i shadowH:val + high shadowL:val - low open:even ? val + open : val - open close:even ? val - close : val + close]]; + [yVals1 addObject:[[CandleChartDataEntry alloc] initWithX:i shadowH:val + high shadowL:val - low open:even ? val + open : val - open close:even ? val - close : val + close]]; } - CandleChartDataSet *set1 = [[CandleChartDataSet alloc] initWithYVals:yVals1 label:@"Data Set"]; + CandleChartDataSet *set1 = [[CandleChartDataSet alloc] initWithValues:yVals1 label:@"Data Set"]; set1.axisDependency = AxisDependencyLeft; [set1 setColor:[UIColor colorWithWhite:80/255.f alpha:1.f]]; @@ -126,7 +118,7 @@ - (void)setDataCount:(int)count range:(double)range set1.increasingFilled = NO; set1.neutralColor = UIColor.blueColor; - CandleChartData *data = [[CandleChartData alloc] initWithXVals:xVals dataSet:set1]; + CandleChartData *data = [[CandleChartData alloc] initWithDataSet:set1]; _chartView.data = data; } diff --git a/ChartsDemo/Classes/Demos/ColoredLineChartViewController.m b/ChartsDemo/Classes/Demos/ColoredLineChartViewController.m index f6da68833c..a9a4dabd97 100644 --- a/ChartsDemo/Classes/Demos/ColoredLineChartViewController.m +++ b/ChartsDemo/Classes/Demos/ColoredLineChartViewController.m @@ -79,22 +79,15 @@ - (void)didReceiveMemoryWarning - (LineChartData *)dataWithCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:[@(i % 12) stringValue]]; - } - NSMutableArray *yVals = [[NSMutableArray alloc] init]; for (int i = 0; i < count; i++) { double val = (double) (arc4random_uniform(range)) + 3; - [yVals addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals addObject:[[ChartDataEntry alloc] initWithX:i y:val]]; } - LineChartDataSet *set1 = [[LineChartDataSet alloc] initWithYVals:yVals label:@"DataSet 1"]; + LineChartDataSet *set1 = [[LineChartDataSet alloc] initWithValues:yVals label:@"DataSet 1"]; set1.lineWidth = 1.75; set1.circleRadius = 5.0; @@ -104,7 +97,7 @@ - (LineChartData *)dataWithCount:(int)count range:(double)range set1.highlightColor = UIColor.whiteColor; set1.drawValuesEnabled = NO; - return [[LineChartData alloc] initWithXVals:xVals dataSet:set1]; + return [[LineChartData alloc] initWithDataSet:set1]; } #pragma mark - ChartViewDelegate diff --git a/ChartsDemo/Classes/Demos/CombinedChartViewController.m b/ChartsDemo/Classes/Demos/CombinedChartViewController.m index c995113655..ff2b7f6013 100644 --- a/ChartsDemo/Classes/Demos/CombinedChartViewController.m +++ b/ChartsDemo/Classes/Demos/CombinedChartViewController.m @@ -16,7 +16,10 @@ #define ITEM_COUNT 12 -@interface CombinedChartViewController () +@interface CombinedChartViewController () +{ + NSArray *months; +} @property (nonatomic, strong) IBOutlet CombinedChartView *chartView; @@ -38,6 +41,13 @@ - (void)viewDidLoad @{@"key": @"toggleBarBorders", @"label": @"Show Bar Borders"}, ]; + months = @[ + @"Jan", @"Feb", @"Mar", + @"Apr", @"May", @"Jun", + @"Jul", @"Aug", @"Sep", + @"Oct", @"Nov", @"Dec" + ]; + _chartView.delegate = self; _chartView.descriptionText = @""; @@ -54,6 +64,10 @@ - (void)viewDidLoad @(CombinedChartDrawOrderScatter) ]; + ChartLegend *l = _chartView.legend; + l.wordWrapEnabled = YES; + l.position = ChartLegendPositionBelowChartCenter; + ChartYAxis *rightAxis = _chartView.rightAxis; rightAxis.drawGridLinesEnabled = NO; rightAxis.axisMinValue = 0.0; // this replaces startAtZero = YES @@ -64,6 +78,9 @@ - (void)viewDidLoad ChartXAxis *xAxis = _chartView.xAxis; xAxis.labelPosition = XAxisLabelPositionBothSided; + xAxis.axisMinValue = 0.0; + xAxis.granularity = 1.0; + xAxis.valueFormatter = self; [self updateChartData]; } @@ -87,13 +104,15 @@ - (void)updateChartData - (void)setChartData { - CombinedChartData *data = [[CombinedChartData alloc] initWithXVals:months]; + CombinedChartData *data = [[CombinedChartData alloc] init]; data.lineData = [self generateLineData]; data.barData = [self generateBarData]; data.bubbleData = [self generateBubbleData]; - //data.scatterData = [self generateScatterData]; - //data.candleData = [self generateCandleData]; + data.scatterData = [self generateScatterData]; + data.candleData = [self generateCandleData]; + _chartView.xAxis.axisMaxValue = data.xMax + 0.25; + _chartView.data = data; } @@ -138,15 +157,15 @@ - (LineChartData *)generateLineData for (int index = 0; index < ITEM_COUNT; index++) { - [entries addObject:[[ChartDataEntry alloc] initWithValue:(arc4random_uniform(15) + 10) xIndex:index]]; + [entries addObject:[[ChartDataEntry alloc] initWithX:index + 0.5 y:(arc4random_uniform(15) + 5)]]; } - LineChartDataSet *set = [[LineChartDataSet alloc] initWithYVals:entries label:@"Line DataSet"]; + LineChartDataSet *set = [[LineChartDataSet alloc] initWithValues:entries label:@"Line DataSet"]; [set setColor:[UIColor colorWithRed:240/255.f green:238/255.f blue:70/255.f alpha:1.f]]; set.lineWidth = 2.5; [set setCircleColor:[UIColor colorWithRed:240/255.f green:238/255.f blue:70/255.f alpha:1.f]]; set.fillColor = [UIColor colorWithRed:240/255.f green:238/255.f blue:70/255.f alpha:1.f]; - set.drawCubicEnabled = YES; + set.mode = LineChartModeCubicBezier; set.drawValuesEnabled = YES; set.valueFont = [UIFont systemFontOfSize:10.f]; set.valueTextColor = [UIColor colorWithRed:240/255.f green:238/255.f blue:70/255.f alpha:1.f]; @@ -161,15 +180,16 @@ - (LineChartData *)generateLineData - (BarChartData *)generateBarData { BarChartData *d = [[BarChartData alloc] init]; + d.barWidth = 0.8; NSMutableArray *entries = [[NSMutableArray alloc] init]; for (int index = 0; index < ITEM_COUNT; index++) { - [entries addObject:[[BarChartDataEntry alloc] initWithValue:(arc4random_uniform(15) + 30) xIndex:index]]; + [entries addObject:[[BarChartDataEntry alloc] initWithX:index + 0.5 y:(arc4random_uniform(25) + 25)]]; } - BarChartDataSet *set = [[BarChartDataSet alloc] initWithYVals:entries label:@"Bar DataSet"]; + BarChartDataSet *set = [[BarChartDataSet alloc] initWithValues:entries label:@"Bar DataSet"]; [set setColor:[UIColor colorWithRed:60/255.f green:220/255.f blue:78/255.f alpha:1.f]]; set.valueTextColor = [UIColor colorWithRed:60/255.f green:220/255.f blue:78/255.f alpha:1.f]; set.valueFont = [UIFont systemFontOfSize:10.f]; @@ -187,13 +207,13 @@ - (ScatterChartData *)generateScatterData NSMutableArray *entries = [[NSMutableArray alloc] init]; - for (int index = 0; index < ITEM_COUNT; index++) + for (double index = 0; index < ITEM_COUNT; index += 0.5) { - [entries addObject:[[ChartDataEntry alloc] initWithValue:(arc4random_uniform(20) + 15) xIndex:index]]; + [entries addObject:[[ChartDataEntry alloc] initWithX:index + 0.25 y:(arc4random_uniform(10) + 55)]]; } - ScatterChartDataSet *set = [[ScatterChartDataSet alloc] initWithYVals:entries label:@"Scatter DataSet"]; - [set setColor:[UIColor greenColor]]; + ScatterChartDataSet *set = [[ScatterChartDataSet alloc] initWithValues:entries label:@"Scatter DataSet"]; + set.colors = ChartColorTemplates.material; set.scatterShapeSize = 7.5; [set setDrawValuesEnabled:YES]; set.valueFont = [UIFont systemFontOfSize:10.f]; @@ -209,14 +229,15 @@ - (CandleChartData *)generateCandleData NSMutableArray *entries = [[NSMutableArray alloc] init]; - for (int index = 0; index < ITEM_COUNT; index++) + for (int index = 0; index < ITEM_COUNT; index += 2) { - [entries addObject:[[CandleChartDataEntry alloc] initWithXIndex:index shadowH:20.0 shadowL:10.0 open:13.0 close:17.0]]; + [entries addObject:[[CandleChartDataEntry alloc] initWithX:index + 1 shadowH:90.0 shadowL:70.0 open:85.0 close:75.0]]; } - CandleChartDataSet *set = [[CandleChartDataSet alloc] initWithYVals:entries label:@"Candle DataSet"]; + CandleChartDataSet *set = [[CandleChartDataSet alloc] initWithValues:entries label:@"Candle DataSet"]; [set setColor:[UIColor colorWithRed:80/255.f green:80/255.f blue:80/255.f alpha:1.f]]; - set.barSpace = 0.3; + set.decreasingColor = [UIColor colorWithRed:142/255.0 green:150/255.0 blue:175/255.0 alpha:1.0]; + set.shadowColor = UIColor.darkGrayColor; set.valueFont = [UIFont systemFontOfSize:10.f]; [set setDrawValuesEnabled:NO]; @@ -233,11 +254,12 @@ - (BubbleChartData *)generateBubbleData for (int index = 0; index < ITEM_COUNT; index++) { - double rnd = arc4random_uniform(20) + 30.f; - [entries addObject:[[BubbleChartDataEntry alloc] initWithXIndex:index value:rnd size:rnd]]; + double y = arc4random_uniform(10) + 105.0; + double size = arc4random_uniform(50) + 105.0; + [entries addObject:[[BubbleChartDataEntry alloc] initWithX:index + 0.5 y:y size:size]]; } - BubbleChartDataSet *set = [[BubbleChartDataSet alloc] initWithYVals:entries label:@"Bubble DataSet"]; + BubbleChartDataSet *set = [[BubbleChartDataSet alloc] initWithValues:entries label:@"Bubble DataSet"]; [set setColors:ChartColorTemplates.vordiplom]; set.valueTextColor = UIColor.whiteColor; set.valueFont = [UIFont systemFontOfSize:10.f]; @@ -260,4 +282,12 @@ - (void)chartValueNothingSelected:(ChartViewBase * __nonnull)chartView NSLog(@"chartValueNothingSelected"); } +#pragma mark - ChartAxisValueFormatter + +- (NSString *)stringForValue:(double)value + axis:(ChartAxisBase *)axis +{ + return months[(int)value % months.count]; +} + @end diff --git a/ChartsDemo/Classes/Demos/CubicLineChartViewController.m b/ChartsDemo/Classes/Demos/CubicLineChartViewController.m index 5a16bda7da..a19004ef44 100644 --- a/ChartsDemo/Classes/Demos/CubicLineChartViewController.m +++ b/ChartsDemo/Classes/Demos/CubicLineChartViewController.m @@ -115,35 +115,27 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:[@(i + 1990) stringValue]]; - } - NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; for (int i = 0; i < count; i++) { double mult = (range + 1); double val = (double) (arc4random_uniform(mult)) + 20; - [yVals1 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals1 addObject:[[ChartDataEntry alloc] initWithX:i y:val]]; } LineChartDataSet *set1 = nil; if (_chartView.data.dataSetCount > 0) { set1 = (LineChartDataSet *)_chartView.data.dataSets[0]; - set1.yVals = yVals1; - _chartView.data.xValsObjc = xVals; + set1.values = yVals1; [_chartView.data notifyDataChanged]; [_chartView notifyDataSetChanged]; } else { - set1 = [[LineChartDataSet alloc] initWithYVals:yVals1 label:@"DataSet 1"]; - set1.drawCubicEnabled = YES; + set1 = [[LineChartDataSet alloc] initWithValues:yVals1 label:@"DataSet 1"]; + set1.mode = LineChartModeCubicBezier; set1.cubicIntensity = 0.2; set1.drawCirclesEnabled = NO; set1.lineWidth = 1.8; @@ -156,7 +148,7 @@ - (void)setDataCount:(int)count range:(double)range set1.drawHorizontalHighlightIndicatorEnabled = NO; set1.fillFormatter = [[CubicLineSampleFillFormatter alloc] init]; - LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSet:set1]; + LineChartData *data = [[LineChartData alloc] initWithDataSet:set1]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:9.f]]; [data setDrawValues:NO]; @@ -192,7 +184,7 @@ - (void)optionTapped:(NSString *)key { for (id set in _chartView.data.dataSets) { - set.drawCubicEnabled = !set.isDrawCubicEnabled; + set.mode = set.mode == LineChartModeCubicBezier ? LineChartModeLinear : LineChartModeCubicBezier; } [_chartView setNeedsDisplay]; @@ -203,7 +195,7 @@ - (void)optionTapped:(NSString *)key { for (id set in _chartView.data.dataSets) { - set.drawSteppedEnabled = !set.isDrawSteppedEnabled; + set.mode = set.mode == LineChartModeStepped ? LineChartModeLinear : LineChartModeStepped; } [_chartView setNeedsDisplay]; diff --git a/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.m b/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.m index a3c07bf18c..910066ee81 100644 --- a/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/HorizontalBarChartViewController.m @@ -35,7 +35,6 @@ - (void)viewDidLoad self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -53,20 +52,20 @@ - (void)viewDidLoad _chartView.drawBarShadowEnabled = NO; _chartView.drawValueAboveBarEnabled = YES; - _chartView.maxVisibleValueCount = 60; + _chartView.maxVisibleCount = 60; ChartXAxis *xAxis = _chartView.xAxis; xAxis.labelPosition = XAxisLabelPositionBottom; xAxis.labelFont = [UIFont systemFontOfSize:10.f]; xAxis.drawAxisLineEnabled = YES; - xAxis.drawGridLinesEnabled = YES; + xAxis.drawGridLinesEnabled = NO; xAxis.gridLineWidth = .3; + xAxis.granularity = 10.0; ChartYAxis *leftAxis = _chartView.leftAxis; leftAxis.labelFont = [UIFont systemFontOfSize:10.f]; leftAxis.drawAxisLineEnabled = YES; leftAxis.drawGridLinesEnabled = YES; - leftAxis.gridLineWidth = .3; leftAxis.axisMinValue = 0.0; // this replaces startAtZero = YES ChartYAxis *rightAxis = _chartView.rightAxis; @@ -82,6 +81,8 @@ - (void)viewDidLoad _chartView.legend.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:11.f]; _chartView.legend.xEntrySpace = 4.0; + _chartView.fitBars = YES; + _sliderX.value = 11.0; _sliderY.value = 50.0; [self slidersValueChanged:nil]; @@ -108,40 +109,35 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:months[i % 12]]; - } - + double barWidth = 9.0; + double spaceForBar = 10.0; + NSMutableArray *yVals = [[NSMutableArray alloc] init]; for (int i = 0; i < count; i++) { double mult = (range + 1); double val = (double) (arc4random_uniform(mult)); - [yVals addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals addObject:[[BarChartDataEntry alloc] initWithX:i * spaceForBar y:val]]; } BarChartDataSet *set1 = nil; if (_chartView.data.dataSetCount > 0) { set1 = (BarChartDataSet *)_chartView.data.dataSets[0]; - set1.yVals = yVals; - _chartView.data.xValsObjc = xVals; + set1.values = yVals; [_chartView notifyDataSetChanged]; } else { - set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"DataSet"]; - set1.barSpace = 0.35; + set1 = [[BarChartDataSet alloc] initWithValues:yVals label:@"DataSet"]; NSMutableArray *dataSets = [[NSMutableArray alloc] init]; [dataSets addObject:set1]; - BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + BarChartData *data = [[BarChartData alloc] initWithDataSets:dataSets]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]]; + data.barWidth = barWidth; _chartView.data = data; } diff --git a/ChartsDemo/Classes/Demos/LineChart1ViewController.m b/ChartsDemo/Classes/Demos/LineChart1ViewController.m index f1ff936581..bcfee2dc33 100644 --- a/ChartsDemo/Classes/Demos/LineChart1ViewController.m +++ b/ChartsDemo/Classes/Demos/LineChart1ViewController.m @@ -105,7 +105,7 @@ - (void)viewDidLoad _sliderY.value = 100.0; [self slidersValueChanged:nil]; - [_chartView animateWithXAxisDuration:2.5 easingOption:ChartEasingOptionEaseInOutQuart]; + [_chartView animateWithXAxisDuration:2.5]; } - (void)didReceiveMemoryWarning @@ -127,34 +127,25 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; + NSMutableArray *values = [[NSMutableArray alloc] init]; for (int i = 0; i < count; i++) { - [xVals addObject:[@(i) stringValue]]; - } - - NSMutableArray *yVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - double mult = (range + 1); - double val = (double) (arc4random_uniform(mult)) + 3; - [yVals addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + double val = arc4random_uniform(range) + 3; + [values addObject:[[ChartDataEntry alloc] initWithX:i y:val]]; } LineChartDataSet *set1 = nil; if (_chartView.data.dataSetCount > 0) { set1 = (LineChartDataSet *)_chartView.data.dataSets[0]; - set1.yVals = yVals; - _chartView.data.xValsObjc = xVals; + set1.values = values; [_chartView.data notifyDataChanged]; [_chartView notifyDataSetChanged]; } else { - set1 = [[LineChartDataSet alloc] initWithYVals:yVals label:@"DataSet 1"]; + set1 = [[LineChartDataSet alloc] initWithValues:values label:@"DataSet 1"]; set1.lineDashLengths = @[@5.f, @2.5f]; set1.highlightLineDashLengths = @[@5.f, @2.5f]; @@ -164,8 +155,6 @@ - (void)setDataCount:(int)count range:(double)range set1.circleRadius = 3.0; set1.drawCircleHoleEnabled = NO; set1.valueFont = [UIFont systemFontOfSize:9.f]; - //set1.fillAlpha = 65/255.0; - //set1.fillColor = UIColor.blackColor; NSArray *gradientColors = @[ (id)[ChartColorTemplates colorFromString:@"#00ff0000"].CGColor, @@ -182,7 +171,7 @@ - (void)setDataCount:(int)count range:(double)range NSMutableArray *dataSets = [[NSMutableArray alloc] init]; [dataSets addObject:set1]; - LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSets:dataSets]; + LineChartData *data = [[LineChartData alloc] initWithDataSets:dataSets]; _chartView.data = data; } diff --git a/ChartsDemo/Classes/Demos/LineChart2ViewController.m b/ChartsDemo/Classes/Demos/LineChart2ViewController.m index 21cd0484bb..15d6f2d9ab 100644 --- a/ChartsDemo/Classes/Demos/LineChart2ViewController.m +++ b/ChartsDemo/Classes/Demos/LineChart2ViewController.m @@ -67,11 +67,10 @@ - (void)viewDidLoad _chartView.legend.position = ChartLegendPositionBelowChartLeft; ChartXAxis *xAxis = _chartView.xAxis; - xAxis.labelFont = [UIFont systemFontOfSize:12.f]; + xAxis.labelFont = [UIFont systemFontOfSize:11.f]; xAxis.labelTextColor = UIColor.whiteColor; xAxis.drawGridLinesEnabled = NO; xAxis.drawAxisLineEnabled = NO; - xAxis.spaceBetweenLabels = 1.0; ChartYAxis *leftAxis = _chartView.leftAxis; leftAxis.labelTextColor = [UIColor colorWithRed:51/255.f green:181/255.f blue:229/255.f alpha:1.f]; @@ -114,20 +113,13 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:[@(i) stringValue]]; - } - NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; for (int i = 0; i < count; i++) { double mult = range / 2.0; double val = (double) (arc4random_uniform(mult)) + 50; - [yVals1 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals1 addObject:[[ChartDataEntry alloc] initWithX:i y:val]]; } NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; @@ -136,7 +128,7 @@ - (void)setDataCount:(int)count range:(double)range { double mult = range; double val = (double) (arc4random_uniform(mult)) + 450; - [yVals2 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals2 addObject:[[ChartDataEntry alloc] initWithX:i y:val]]; } LineChartDataSet *set1 = nil, *set2 = nil; @@ -145,15 +137,14 @@ - (void)setDataCount:(int)count range:(double)range { set1 = (LineChartDataSet *)_chartView.data.dataSets[0]; set2 = (LineChartDataSet *)_chartView.data.dataSets[1]; - set1.yVals = yVals1; - set2.yVals = yVals2; - _chartView.data.xValsObjc = xVals; + set1.values = yVals1; + set2.values = yVals2; [_chartView.data notifyDataChanged]; [_chartView notifyDataSetChanged]; } else { - set1 = [[LineChartDataSet alloc] initWithYVals:yVals1 label:@"DataSet 1"]; + set1 = [[LineChartDataSet alloc] initWithValues:yVals1 label:@"DataSet 1"]; set1.axisDependency = AxisDependencyLeft; [set1 setColor:[UIColor colorWithRed:51/255.f green:181/255.f blue:229/255.f alpha:1.f]]; [set1 setCircleColor:UIColor.whiteColor]; @@ -164,7 +155,7 @@ - (void)setDataCount:(int)count range:(double)range set1.highlightColor = [UIColor colorWithRed:244/255.f green:117/255.f blue:117/255.f alpha:1.f]; set1.drawCircleHoleEnabled = NO; - set2 = [[LineChartDataSet alloc] initWithYVals:yVals2 label:@"DataSet 2"]; + set2 = [[LineChartDataSet alloc] initWithValues:yVals2 label:@"DataSet 2"]; set2.axisDependency = AxisDependencyRight; [set2 setColor:UIColor.redColor]; [set2 setCircleColor:UIColor.whiteColor]; @@ -179,7 +170,7 @@ - (void)setDataCount:(int)count range:(double)range [dataSets addObject:set2]; [dataSets addObject:set1]; - LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSets:dataSets]; + LineChartData *data = [[LineChartData alloc] initWithDataSets:dataSets]; [data setValueTextColor:UIColor.whiteColor]; [data setValueFont:[UIFont systemFontOfSize:9.f]]; @@ -262,9 +253,9 @@ - (void)chartValueSelected:(ChartViewBase * __nonnull)chartView entry:(ChartData { NSLog(@"chartValueSelected"); - [_chartView centerViewToAnimatedWithXIndex:entry.xIndex yValue:entry.value axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; - //[_chartView moveViewToAnimatedWithXIndex:entry.xIndex yValue:entry.value axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; - //[_chartView zoomAndCenterViewAnimatedWithScaleX:1.8 scaleY:1.8 xIndex:entry.xIndex yValue:entry.value axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; + [_chartView centerViewToAnimatedWithXValue:entry.x yValue:entry.y axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; + //[_chartView moveViewToAnimatedWithXValue:entry.x yValue:entry.y axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; + //[_chartView zoomAndCenterViewAnimatedWithScaleX:1.8 scaleY:1.8 xValue:entry.x yValue:entry.y axis:[_chartView.data getDataSetByIndex:dataSetIndex].axisDependency duration:1.0]; } diff --git a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m index 53a1d4184d..d219c283a0 100644 --- a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.m @@ -13,6 +13,7 @@ #import "MultipleBarChartViewController.h" #import "ChartsDemo-Swift.h" +#import "IntAxisValueFormatter.h" @interface MultipleBarChartViewController () @@ -35,7 +36,6 @@ - (void)viewDidLoad self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -65,11 +65,16 @@ - (void)viewDidLoad ChartXAxis *xAxis = _chartView.xAxis; xAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; + xAxis.granularity = 1.f; + xAxis.centerAxisLabelsEnabled = YES; + xAxis.valueFormatter = [[IntAxisValueFormatter alloc] init]; + + NSNumberFormatter *leftAxisFormatter = [[NSNumberFormatter alloc] init]; + leftAxisFormatter.maximumFractionDigits = 1; ChartYAxis *leftAxis = _chartView.leftAxis; leftAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; - leftAxis.valueFormatter = [[NSNumberFormatter alloc] init]; - leftAxis.valueFormatter.maximumFractionDigits = 1; + leftAxis.valueFormatter = [[ChartDefaultAxisValueFormatter alloc] initWithFormatter:leftAxisFormatter]; leftAxis.drawGridLinesEnabled = NO; leftAxis.spaceTop = 0.25; @@ -99,29 +104,30 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:[@(i + 1990) stringValue]]; - } - + float groupSpace = 0.04f; + float barSpace = 0.02f; + float barWidth = 0.3f; + // (0.3 + 0.02) * 3 + 0.04 = 1.00 + NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; NSMutableArray *yVals3 = [[NSMutableArray alloc] init]; double mult = range * 1000.f; - for (int i = 0; i < count; i++) + int startYear = 1980; + int endYear = startYear + _sliderX.value; + + for (int i = startYear; i < endYear; i++) { double val = (double) (arc4random_uniform(mult) + 3.0); - [yVals1 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals1 addObject:[[BarChartDataEntry alloc] initWithX:i y:val]]; val = (double) (arc4random_uniform(mult) + 3.0); - [yVals2 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals2 addObject:[[BarChartDataEntry alloc] initWithX:i y:val]]; val = (double) (arc4random_uniform(mult) + 3.0); - [yVals3 addObject:[[BarChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals3 addObject:[[BarChartDataEntry alloc] initWithX:i y:val]]; } BarChartDataSet *set1 = nil, *set2 = nil, *set3 = nil; @@ -130,21 +136,26 @@ - (void)setDataCount:(int)count range:(double)range set1 = (BarChartDataSet *)_chartView.data.dataSets[0]; set2 = (BarChartDataSet *)_chartView.data.dataSets[1]; set3 = (BarChartDataSet *)_chartView.data.dataSets[2]; - set1.yVals = yVals1; - set2.yVals = yVals2; - set3.yVals = yVals3; - _chartView.data.xValsObjc = xVals; + set1.values = yVals1; + set2.values = yVals2; + set3.values = yVals3; + + _chartView.barData.barWidth = barWidth; + _chartView.xAxis.axisMinValue = startYear; + _chartView.xAxis.axisMaxValue = [_chartView.barData groupWidthWithGroupSpace:groupSpace barSpace: barSpace] * _sliderX.value + startYear; + [_chartView groupBarsFromX: startYear groupSpace: groupSpace barSpace: barSpace]; + [_chartView notifyDataSetChanged]; } else { - set1 = [[BarChartDataSet alloc] initWithYVals:yVals1 label:@"Company A"]; + set1 = [[BarChartDataSet alloc] initWithValues:yVals1 label:@"Company A"]; [set1 setColor:[UIColor colorWithRed:104/255.f green:241/255.f blue:175/255.f alpha:1.f]]; - set2 = [[BarChartDataSet alloc] initWithYVals:yVals2 label:@"Company B"]; + set2 = [[BarChartDataSet alloc] initWithValues:yVals2 label:@"Company B"]; [set2 setColor:[UIColor colorWithRed:164/255.f green:228/255.f blue:251/255.f alpha:1.f]]; - set3 = [[BarChartDataSet alloc] initWithYVals:yVals3 label:@"Company C"]; + set3 = [[BarChartDataSet alloc] initWithValues:yVals3 label:@"Company C"]; [set3 setColor:[UIColor colorWithRed:242/255.f green:247/255.f blue:158/255.f alpha:1.f]]; NSMutableArray *dataSets = [[NSMutableArray alloc] init]; @@ -152,10 +163,13 @@ - (void)setDataCount:(int)count range:(double)range [dataSets addObject:set2]; [dataSets addObject:set3]; - BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; - data.groupSpace = 0.8; + BarChartData *data = [[BarChartData alloc] initWithDataSets:dataSets]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]]; + [_chartView.barData groupBarsFromX:0.0 groupSpace: groupSpace barSpace: barSpace]; + _chartView.xAxis.axisMinValue = 0.0; + _chartView.xAxis.axisMaxValue = [_chartView.barData groupWidthWithGroupSpace:groupSpace barSpace: barSpace] * (double)count; + _chartView.data = data; } } @@ -169,7 +183,10 @@ - (void)optionTapped:(NSString *)key - (IBAction)slidersValueChanged:(id)sender { - _sliderTextX.text = [@((int)_sliderX.value + 1) stringValue]; + int startYear = 1980; + int endYear = startYear + _sliderX.value; + + _sliderTextX.text = [NSString stringWithFormat:@"%d-%d", startYear, endYear]; _sliderTextY.text = [@((int)_sliderY.value) stringValue]; [self updateChartData]; diff --git a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.xib b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.xib index 6d27d969b4..22bef13385 100644 --- a/ChartsDemo/Classes/Demos/MultipleBarChartViewController.xib +++ b/ChartsDemo/Classes/Demos/MultipleBarChartViewController.xib @@ -1,7 +1,8 @@ - + - + + diff --git a/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.m b/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.m index 399728bc92..3faef584a9 100644 --- a/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.m +++ b/ChartsDemo/Classes/Demos/MultipleLinesChartViewController.m @@ -92,13 +92,6 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:[@(i) stringValue]]; - } - NSArray *colors = @[ChartColorTemplates.vordiplom[0], ChartColorTemplates.vordiplom[1], ChartColorTemplates.vordiplom[2]]; NSMutableArray *dataSets = [[NSMutableArray alloc] init]; @@ -110,10 +103,10 @@ - (void)setDataCount:(int)count range:(double)range for (int i = 0; i < count; i++) { double val = (double) (arc4random_uniform(range) + 3); - [values addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + [values addObject:[[ChartDataEntry alloc] initWithX:i y:val]]; } - LineChartDataSet *d = [[LineChartDataSet alloc] initWithYVals:values label:[NSString stringWithFormat:@"DataSet %d", z + 1]]; + LineChartDataSet *d = [[LineChartDataSet alloc] initWithValues:values label:[NSString stringWithFormat:@"DataSet %d", z + 1]]; d.lineWidth = 2.5; d.circleRadius = 4.0; @@ -127,7 +120,7 @@ - (void)setDataCount:(int)count range:(double)range ((LineChartDataSet *)dataSets[0]).colors = ChartColorTemplates.vordiplom; ((LineChartDataSet *)dataSets[0]).circleColors = ChartColorTemplates.vordiplom; - LineChartData *data = [[LineChartData alloc] initWithXVals:xVals dataSets:dataSets]; + LineChartData *data = [[LineChartData alloc] initWithDataSets:dataSets]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]]; _chartView.data = data; } diff --git a/ChartsDemo/Classes/Demos/NegativeStackedBarChartViewController.m b/ChartsDemo/Classes/Demos/NegativeStackedBarChartViewController.m index 39861f40e8..b589bc51d5 100644 --- a/ChartsDemo/Classes/Demos/NegativeStackedBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/NegativeStackedBarChartViewController.m @@ -14,7 +14,7 @@ #import "NegativeStackedBarChartViewController.h" #import "ChartsDemo-Swift.h" -@interface NegativeStackedBarChartViewController () +@interface NegativeStackedBarChartViewController () @property (nonatomic, strong) IBOutlet HorizontalBarChartView *chartView; @@ -31,7 +31,6 @@ - (void)viewDidLoad self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -69,13 +68,20 @@ - (void)viewDidLoad _chartView.rightAxis.drawGridLinesEnabled = NO; _chartView.rightAxis.drawZeroLineEnabled = YES; _chartView.rightAxis.labelCount = 7; - _chartView.rightAxis.valueFormatter = customFormatter; + _chartView.rightAxis.valueFormatter = [[ChartDefaultAxisValueFormatter alloc] initWithFormatter:customFormatter]; _chartView.rightAxis.labelFont = [UIFont systemFontOfSize:9.f]; ChartXAxis *xAxis = _chartView.xAxis; xAxis.labelPosition = XAxisLabelPositionBothSided; xAxis.drawGridLinesEnabled = NO; xAxis.drawAxisLineEnabled = NO; + xAxis.axisMinValue = 0.0; + xAxis.axisMaxValue = 110.0; + xAxis.centerAxisLabelsEnabled = YES; + xAxis.labelCount = 12; + xAxis.granularity = 10.0; + xAxis.valueFormatter = self; + _chartView.rightAxis.labelFont = [UIFont systemFontOfSize:9.f]; ChartLegend *l = _chartView.legend; @@ -101,32 +107,38 @@ - (void)updateChartData - (void)setChartData { NSMutableArray *yValues = [NSMutableArray array]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-10, @10 ] xIndex: 0]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-12, @13 ] xIndex: 1]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-15, @15 ] xIndex: 2]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-17, @17 ] xIndex: 3]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-19, @20 ] xIndex: 4]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-19, @19 ] xIndex: 5]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-16, @16 ] xIndex: 6]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-13, @14 ] xIndex: 7]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-10, @11 ] xIndex: 8]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-5, @6 ] xIndex: 9]]; - [yValues addObject:[[BarChartDataEntry alloc] initWithValues:@[ @-1, @2 ] xIndex: 10]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:5 yValues:@[ @-10, @10 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:15 yValues:@[ @-12, @13 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:25 yValues:@[ @-15, @15 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:35 yValues:@[ @-17, @17 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:45 yValues:@[ @-19, @20 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:55 yValues:@[ @-19, @19 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:65 yValues:@[ @-16, @16 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:75 yValues:@[ @-13, @14 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:85 yValues:@[ @-10, @11 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:95 yValues:@[ @-5, @6 ]]]; + [yValues addObject:[[BarChartDataEntry alloc] initWithX:105 yValues:@[ @-1, @2 ]]]; BarChartDataSet *set = nil; if (_chartView.data.dataSetCount > 0) { set = (BarChartDataSet *)_chartView.data.dataSets[0]; - set.yVals = yValues; + set.values = yValues; [_chartView notifyDataSetChanged]; } else { - set = [[BarChartDataSet alloc] initWithYVals:yValues label:@"Age Distribution"]; - set.valueFormatter = _chartView.rightAxis.valueFormatter; + NSNumberFormatter *customFormatter = [[NSNumberFormatter alloc] init]; + customFormatter.negativePrefix = @""; + customFormatter.positiveSuffix = @"m"; + customFormatter.negativeSuffix = @"m"; + customFormatter.minimumSignificantDigits = 1; + customFormatter.minimumFractionDigits = 1; + + set = [[BarChartDataSet alloc] initWithValues:yValues label:@"Age Distribution"]; + set.valueFormatter = customFormatter; set.valueFont = [UIFont systemFontOfSize:7.f]; set.axisDependency = AxisDependencyRight; - set.barSpace = 0.4f; set.colors = @[ [UIColor colorWithRed:67/255.f green:67/255.f blue:72/255.f alpha:1.f], [UIColor colorWithRed:124/255.f green:181/255.f blue:236/255.f alpha:1.f] @@ -135,9 +147,10 @@ - (void)setChartData @"Men", @"Women" ]; - NSArray *xVals = @[ @"0-10", @"10-20", @"20-30", @"30-40", @"40-50", @"50-60", @"60-70", @"70-80", @"80-90", @"90-100", @"100+" ]; + BarChartData *data = [[BarChartData alloc] initWithDataSet:set]; + + data.barWidth = 8.5; - BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSet:set]; _chartView.data = data; [_chartView setNeedsDisplay]; } @@ -166,4 +179,12 @@ - (void)chartValueNothingSelected:(ChartViewBase * __nonnull)chartView NSLog(@"chartValueNothingSelected"); } +#pragma mark - ChartAxisValueFormatter + +- (NSString *)stringForValue:(double)value + axis:(ChartAxisBase *)axis +{ + return [NSString stringWithFormat:@"%03.0f-%03.0f", value, value + 10.0]; +} + @end diff --git a/ChartsDemo/Classes/Demos/PieChartViewController.m b/ChartsDemo/Classes/Demos/PieChartViewController.m index 6dddb21fd4..77f80661c2 100644 --- a/ChartsDemo/Classes/Demos/PieChartViewController.m +++ b/ChartsDemo/Classes/Demos/PieChartViewController.m @@ -83,17 +83,10 @@ - (void)setDataCount:(int)count range:(double)range // IMPORTANT: In a PieChart, no values (Entry) should have the same xIndex (even if from different DataSets), since no values can be drawn above each other. for (int i = 0; i < count; i++) { - [yVals1 addObject:[[BarChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 5) xIndex:i]]; + [yVals1 addObject:[[PieChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 5) label:parties[i % parties.count]]]; } - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:parties[i % parties.count]]; - } - - PieChartDataSet *dataSet = [[PieChartDataSet alloc] initWithYVals:yVals1 label:@"Election Results"]; + PieChartDataSet *dataSet = [[PieChartDataSet alloc] initWithValues:yVals1 label:@"Election Results"]; dataSet.sliceSpace = 2.0; // add a lot of colors @@ -108,7 +101,7 @@ - (void)setDataCount:(int)count range:(double)range dataSet.colors = colors; - PieChartData *data = [[PieChartData alloc] initWithXVals:xVals dataSet:dataSet]; + PieChartData *data = [[PieChartData alloc] initWithDataSet:dataSet]; NSNumberFormatter *pFormatter = [[NSNumberFormatter alloc] init]; pFormatter.numberStyle = NSNumberFormatterPercentStyle; diff --git a/ChartsDemo/Classes/Demos/PiePolylineChartViewController.m b/ChartsDemo/Classes/Demos/PiePolylineChartViewController.m index 32651f9a08..f0ac1800fa 100644 --- a/ChartsDemo/Classes/Demos/PiePolylineChartViewController.m +++ b/ChartsDemo/Classes/Demos/PiePolylineChartViewController.m @@ -80,17 +80,10 @@ - (void)setDataCount:(int)count range:(double)range // IMPORTANT: In a PieChart, no values (Entry) should have the same xIndex (even if from different DataSets), since no values can be drawn above each other. for (int i = 0; i < count; i++) { - [yVals1 addObject:[[BarChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 5) xIndex:i]]; + [yVals1 addObject:[[PieChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 5) label:parties[i % parties.count]]]; } - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:parties[i % parties.count]]; - } - - PieChartDataSet *dataSet = [[PieChartDataSet alloc] initWithYVals:yVals1 label:@"Election Results"]; + PieChartDataSet *dataSet = [[PieChartDataSet alloc] initWithValues:yVals1 label:@"Election Results"]; dataSet.sliceSpace = 2.0; // add a lot of colors @@ -111,7 +104,7 @@ - (void)setDataCount:(int)count range:(double)range //dataSet.xValuePosition = PieChartValuePositionOutsideSlice; dataSet.yValuePosition = PieChartValuePositionOutsideSlice; - PieChartData *data = [[PieChartData alloc] initWithXVals:xVals dataSet:dataSet]; + PieChartData *data = [[PieChartData alloc] initWithDataSet:dataSet]; NSNumberFormatter *pFormatter = [[NSNumberFormatter alloc] init]; pFormatter.numberStyle = NSNumberFormatterPercentStyle; diff --git a/ChartsDemo/Classes/Demos/PositiveNegativeBarChartViewController.m b/ChartsDemo/Classes/Demos/PositiveNegativeBarChartViewController.m index 196504ebf3..53de9e3713 100644 --- a/ChartsDemo/Classes/Demos/PositiveNegativeBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/PositiveNegativeBarChartViewController.m @@ -14,7 +14,10 @@ #import "PositiveNegativeBarChartViewController.h" #import "ChartsDemo-Swift.h" -@interface PositiveNegativeBarChartViewController () +@interface PositiveNegativeBarChartViewController () +{ + NSArray *dataList; +} @property (nonatomic, strong) IBOutlet BarChartView *chartView; @@ -31,7 +34,6 @@ - (void)viewDidLoad self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -66,8 +68,13 @@ - (void)viewDidLoad xAxis.labelFont = [UIFont systemFontOfSize:13.f]; xAxis.drawGridLinesEnabled = NO; xAxis.drawAxisLineEnabled = NO; - xAxis.spaceBetweenLabels = 2.0; xAxis.labelTextColor = [UIColor lightGrayColor]; + xAxis.axisMinValue = 0.0; + xAxis.axisMaxValue = 5.0; + xAxis.labelCount = 5; + xAxis.centerAxisLabelsEnabled = YES; + xAxis.granularity = 1.0; + xAxis.valueFormatter = self; ChartYAxis *leftAxis = _chartView.leftAxis; leftAxis.drawLabelsEnabled = NO; @@ -105,26 +112,25 @@ - (void)updateChartData - (void)setChartData { // THIS IS THE ORIGINAL DATA YOU WANT TO PLOT - NSArray *dataList = @[ - @{@"xIndex": @(0), + dataList = @[ + @{@"xValue": @(0.5), @"yValue": @(-224.1f), - @"xValue": @"12-19"}, - @{@"xIndex": @(1), + @"xLabel": @"12-19"}, + @{@"xValue": @(1.5), @"yValue": @(238.5f), - @"xValue": @"12-30"}, - @{@"xIndex": @(2), + @"xLabel": @"12-30"}, + @{@"xValue": @(2.5), @"yValue": @(1280.1f), - @"xValue": @"12-31"}, - @{@"xIndex": @(3), + @"xLabel": @"12-31"}, + @{@"xValue": @(3.5), @"yValue": @(-442.3f), - @"xValue": @"01-01"}, - @{@"xIndex": @(4), + @"xLabel": @"01-01"}, + @{@"xValue": @(4.5), @"yValue": @(-2280.1f), - @"xValue": @"01-02"}, + @"xLabel": @"01-02"}, ]; NSMutableArray *values = [[NSMutableArray alloc] init]; - NSMutableArray *dates = [[NSMutableArray alloc] init]; NSMutableArray *colors = [[NSMutableArray alloc] init]; UIColor *green = [UIColor colorWithRed:110/255.f green:190/255.f blue:102/255.f alpha:1.f]; @@ -133,11 +139,9 @@ - (void)setChartData for (int i = 0; i < dataList.count; i++) { NSDictionary *d = dataList[i]; - BarChartDataEntry *entry = [[BarChartDataEntry alloc] initWithValue:[d[@"yValue"] doubleValue] xIndex:[d[@"xIndex"] integerValue]]; + BarChartDataEntry *entry = [[BarChartDataEntry alloc] initWithX:[d[@"xValue"] doubleValue] y:[d[@"yValue"] doubleValue]]; [values addObject:entry]; - [dates addObject:d[@"xValue"]]; - // specific colors if ([d[@"yValue"] doubleValue] >= 0.f) { @@ -149,18 +153,19 @@ - (void)setChartData } } - BarChartDataSet *set = set = [[BarChartDataSet alloc] initWithYVals:values label:@"Values"]; - set.barSpace = 0.4f; + BarChartDataSet *set = set = [[BarChartDataSet alloc] initWithValues:values label:@"Values"]; set.colors = colors; set.valueColors = colors; - BarChartData *data = [[BarChartData alloc] initWithXVals:dates dataSet:set]; + BarChartData *data = [[BarChartData alloc] initWithDataSet:set]; [data setValueFont:[UIFont systemFontOfSize:13.f]]; NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init]; formatter.maximumFractionDigits = 1; [data setValueFormatter:formatter]; + data.barWidth = 0.8; + _chartView.data = data; } @@ -181,4 +186,13 @@ - (void)chartValueNothingSelected:(ChartViewBase * __nonnull)chartView NSLog(@"chartValueNothingSelected"); } +#pragma mark - ChartAxisValueFormatter + +- (NSString *)stringForValue:(double)value + axis:(ChartAxisBase *)axis +{ + return dataList[MIN(MAX((int) value, 0), dataList.count - 1)][@"xLabel"]; + +} + @end diff --git a/ChartsDemo/Classes/Demos/RadarChartViewController.m b/ChartsDemo/Classes/Demos/RadarChartViewController.m index 3a7c265832..8e1679d55b 100644 --- a/ChartsDemo/Classes/Demos/RadarChartViewController.m +++ b/ChartsDemo/Classes/Demos/RadarChartViewController.m @@ -14,7 +14,13 @@ #import "RadarChartViewController.h" #import "ChartsDemo-Swift.h" -@interface RadarChartViewController () +@interface RadarChartViewController () +{ + NSArray *activities; + UIColor *originalBarBgColor; + UIColor *originalBarTintColor; + UIBarStyle originalBarStyle; +} @property (nonatomic, strong) IBOutlet RadarChartView *chartView; @@ -26,6 +32,8 @@ - (void)viewDidLoad { [super viewDidLoad]; + activities = @[ @"Burger", @"Steak", @"Salad", @"Pasta", @"Pizza" ]; + self.title = @"Radar Bar Chart"; self.options = @[ @@ -47,8 +55,10 @@ - (void)viewDidLoad _chartView.delegate = self; _chartView.descriptionText = @""; - _chartView.webLineWidth = .75; - _chartView.innerWebLineWidth = 0.375; + _chartView.webLineWidth = 1.0; + _chartView.innerWebLineWidth = 1.0; + _chartView.webColor = UIColor.lightGrayColor; + _chartView.innerWebColor = UIColor.lightGrayColor; _chartView.webAlpha = 1.0; BalloonMarker *marker = [[BalloonMarker alloc] initWithColor:[UIColor colorWithWhite:180/255. alpha:1.0] font:[UIFont systemFontOfSize:12.0] insets: UIEdgeInsetsMake(8.0, 8.0, 20.0, 8.0)]; @@ -57,23 +67,58 @@ - (void)viewDidLoad ChartXAxis *xAxis = _chartView.xAxis; xAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:9.f]; + xAxis.xOffset = 0.0; + xAxis.yOffset = 0.0; + xAxis.valueFormatter = self; + xAxis.labelTextColor = UIColor.whiteColor; ChartYAxis *yAxis = _chartView.yAxis; yAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:9.f]; yAxis.labelCount = 5; yAxis.axisMinValue = 0.0; + yAxis.axisMaxValue = 80.0; + yAxis.drawLabelsEnabled = NO; ChartLegend *l = _chartView.legend; - l.position = ChartLegendPositionRightOfChart; + l.position = ChartLegendPositionAboveChartCenter; l.font = [UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]; l.xEntrySpace = 7.0; l.yEntrySpace = 5.0; + l.textColor = UIColor.whiteColor; [self updateChartData]; [_chartView animateWithXAxisDuration:1.4 yAxisDuration:1.4 easingOption:ChartEasingOptionEaseOutBack]; } +- (void)viewWillAppear:(BOOL)animated +{ + [super viewWillAppear:animated]; + + [UIView animateWithDuration:0.15 animations:^{ + UINavigationBar *navigationBar = self.navigationController.navigationBar; + originalBarBgColor = self.navigationController.navigationBar.barTintColor; + originalBarTintColor = self.navigationController.navigationBar.tintColor; + originalBarStyle = self.navigationController.navigationBar.barStyle; + + navigationBar.barTintColor = self.view.backgroundColor; + navigationBar.tintColor = UIColor.whiteColor; + navigationBar.barStyle = UIBarStyleBlack; + }]; +} + +- (void)viewWillDisappear:(BOOL)animated +{ + [super viewWillDisappear:animated]; + + [UIView animateWithDuration:0.15 animations:^{ + UINavigationBar *navigationBar = self.navigationController.navigationBar; + navigationBar.barTintColor = originalBarBgColor; + navigationBar.tintColor = originalBarTintColor; + navigationBar.barStyle = originalBarStyle; + }]; +} + - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; @@ -93,40 +138,41 @@ - (void)updateChartData - (void)setChartData { - double mult = 150.f; - int count = 9; + double mult = 80; + double min = 20; + int cnt = 5; NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; - for (int i = 0; i < count; i++) - { - [yVals1 addObject:[[ChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 2) xIndex:i]]; - [yVals2 addObject:[[ChartDataEntry alloc] initWithValue:(arc4random_uniform(mult) + mult / 2) xIndex:i]]; - } - - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) + for (int i = 0; i < cnt; i++) { - [xVals addObject:parties[i % parties.count]]; + [yVals1 addObject:[[ChartDataEntry alloc] initWithX:i y:(arc4random_uniform(mult) + min)]]; + [yVals2 addObject:[[ChartDataEntry alloc] initWithX:i y:(arc4random_uniform(mult) + min)]]; } - RadarChartDataSet *set1 = [[RadarChartDataSet alloc] initWithYVals:yVals1 label:@"Set 1"]; - [set1 setColor:ChartColorTemplates.vordiplom[0]]; - set1.fillColor = ChartColorTemplates.vordiplom[0]; + RadarChartDataSet *set1 = [[RadarChartDataSet alloc] initWithValues:yVals1 label:@"Last Week"]; + [set1 setColor:[UIColor colorWithRed:103/255.0 green:110/255.0 blue:129/255.0 alpha:1.0]]; + set1.fillColor = [UIColor colorWithRed:103/255.0 green:110/255.0 blue:129/255.0 alpha:1.0]; set1.drawFilledEnabled = YES; + set1.fillAlpha = 0.7; set1.lineWidth = 2.0; - - RadarChartDataSet *set2 = [[RadarChartDataSet alloc] initWithYVals:yVals2 label:@"Set 2"]; - [set2 setColor:ChartColorTemplates.vordiplom[4]]; - set2.fillColor = ChartColorTemplates.vordiplom[4]; + set1.drawHighlightCircleEnabled = YES; + [set1 setDrawHighlightIndicators:NO]; + + RadarChartDataSet *set2 = [[RadarChartDataSet alloc] initWithValues:yVals2 label:@"This Week"]; + [set2 setColor:[UIColor colorWithRed:121/255.0 green:162/255.0 blue:175/255.0 alpha:1.0]]; + set2.fillColor = [UIColor colorWithRed:121/255.0 green:162/255.0 blue:175/255.0 alpha:1.0]; set2.drawFilledEnabled = YES; + set2.fillAlpha = 0.7; set2.lineWidth = 2.0; + set2.drawHighlightCircleEnabled = YES; + [set2 setDrawHighlightIndicators:NO]; - RadarChartData *data = [[RadarChartData alloc] initWithXVals:xVals dataSets:@[set1, set2]]; + RadarChartData *data = [[RadarChartData alloc] initWithDataSets:@[set1, set2]]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:8.f]]; [data setDrawValues:NO]; + data.valueTextColor = UIColor.whiteColor; _chartView.data = data; } @@ -216,4 +262,12 @@ - (void)chartValueNothingSelected:(ChartViewBase * __nonnull)chartView NSLog(@"chartValueNothingSelected"); } +#pragma mark - ChartAxisValueFormatter + +- (NSString *)stringForValue:(double)value + axis:(ChartAxisBase *)axis +{ + return activities[(int) value % activities.count]; +} + @end diff --git a/ChartsDemo/Classes/Demos/RadarChartViewController.xib b/ChartsDemo/Classes/Demos/RadarChartViewController.xib index 8bd99e2037..6f001190f3 100644 --- a/ChartsDemo/Classes/Demos/RadarChartViewController.xib +++ b/ChartsDemo/Classes/Demos/RadarChartViewController.xib @@ -1,7 +1,9 @@ - + - + + + @@ -20,23 +22,31 @@ - + + - - + + - + - + + + diff --git a/ChartsDemo/Classes/Demos/ScatterChartViewController.m b/ChartsDemo/Classes/Demos/ScatterChartViewController.m index 8f7fcf29c2..887f002a1f 100644 --- a/ChartsDemo/Classes/Demos/ScatterChartViewController.m +++ b/ChartsDemo/Classes/Demos/ScatterChartViewController.m @@ -49,10 +49,12 @@ - (void)viewDidLoad _chartView.descriptionText = @""; _chartView.noDataTextDescription = @"You need to provide data for the chart."; + _chartView.maxHighlightDistance = 50.0; + _chartView.drawGridBackgroundEnabled = NO; _chartView.dragEnabled = YES; [_chartView setScaleEnabled:YES]; - _chartView.maxVisibleValueCount = 200; + _chartView.maxVisibleCount = 200; _chartView.pinchZoomEnabled = YES; ChartLegend *l = _chartView.legend; @@ -93,13 +95,6 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:[@(i) stringValue]]; - } - NSMutableArray *yVals1 = [[NSMutableArray alloc] init]; NSMutableArray *yVals2 = [[NSMutableArray alloc] init]; NSMutableArray *yVals3 = [[NSMutableArray alloc] init]; @@ -107,24 +102,24 @@ - (void)setDataCount:(int)count range:(double)range for (int i = 0; i < count; i++) { double val = (double) (arc4random_uniform(range)) + 3; - [yVals1 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals1 addObject:[[ChartDataEntry alloc] initWithX:(double)i y:val]]; val = (double) (arc4random_uniform(range)) + 3; - [yVals2 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals2 addObject:[[ChartDataEntry alloc] initWithX:(double)i + 0.33 y:val]]; val = (double) (arc4random_uniform(range)) + 3; - [yVals3 addObject:[[ChartDataEntry alloc] initWithValue:val xIndex:i]]; + [yVals3 addObject:[[ChartDataEntry alloc] initWithX:(double)i + 0.66 y:val]]; } - ScatterChartDataSet *set1 = [[ScatterChartDataSet alloc] initWithYVals:yVals1 label:@"DS 1"]; + ScatterChartDataSet *set1 = [[ScatterChartDataSet alloc] initWithValues:yVals1 label:@"DS 1"]; set1.scatterShape = ScatterShapeSquare; [set1 setColor:ChartColorTemplates.colorful[0]]; - ScatterChartDataSet *set2 = [[ScatterChartDataSet alloc] initWithYVals:yVals2 label:@"DS 2"]; + ScatterChartDataSet *set2 = [[ScatterChartDataSet alloc] initWithValues:yVals2 label:@"DS 2"]; set2.scatterShape = ScatterShapeCircle; set2.scatterShapeHoleColor = ChartColorTemplates.colorful[3]; set2.scatterShapeHoleRadius = 3.5f; [set2 setColor:ChartColorTemplates.colorful[1]]; - ScatterChartDataSet *set3 = [[ScatterChartDataSet alloc] initWithYVals:yVals3 label:@"DS 3"]; + ScatterChartDataSet *set3 = [[ScatterChartDataSet alloc] initWithValues:yVals3 label:@"DS 3"]; set3.scatterShape = ScatterShapeCross; [set3 setColor:ChartColorTemplates.colorful[2]]; @@ -137,7 +132,7 @@ - (void)setDataCount:(int)count range:(double)range [dataSets addObject:set2]; [dataSets addObject:set3]; - ScatterChartData *data = [[ScatterChartData alloc] initWithXVals:xVals dataSets:dataSets]; + ScatterChartData *data = [[ScatterChartData alloc] initWithDataSets:dataSets]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]]; _chartView.data = data; diff --git a/ChartsDemo/Classes/Demos/SinusBarChartViewController.m b/ChartsDemo/Classes/Demos/SinusBarChartViewController.m index b9b7275b3b..add0724e04 100644 --- a/ChartsDemo/Classes/Demos/SinusBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/SinusBarChartViewController.m @@ -33,7 +33,6 @@ - (void)viewDidLoad self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -50,7 +49,7 @@ - (void)viewDidLoad _chartView.drawBarShadowEnabled = NO; _chartView.drawValueAboveBarEnabled = YES; - _chartView.maxVisibleValueCount = 60; + _chartView.maxVisibleCount = 60; _chartView.pinchZoomEnabled = NO; _chartView.drawGridBackgroundEnabled = NO; @@ -108,33 +107,31 @@ - (void)updateChartData - (void)setDataCount:(int)count { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; NSMutableArray *entries = [[NSMutableArray alloc] init]; for (int i = 0; i < count; i++) { - [xVals addObject:[@(i) stringValue]]; - [entries addObject:[[BarChartDataEntry alloc] initWithValue:sinf(M_PI * (i % 128) / 64.0) xIndex:i]]; + [entries addObject:[[BarChartDataEntry alloc] initWithX:(double)i y:sinf(M_PI * (i % 128) / 64.0)]]; } BarChartDataSet *set = nil; if (_chartView.data.dataSetCount > 0) { set = (BarChartDataSet *)_chartView.data.dataSets[0]; - set.yVals = entries; - _chartView.data.xValsObjc = xVals; + set.values = entries; [_chartView notifyDataSetChanged]; } else { - set = [[BarChartDataSet alloc] initWithYVals:entries label:@"Sinus Function"]; - set.barSpace = 0.4; + set = [[BarChartDataSet alloc] initWithValues:entries label:@"Sinus Function"]; [set setColor:[UIColor colorWithRed:240/255.f green:120/255.f blue:124/255.f alpha:1.f]]; - BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSet:set]; + BarChartData *data = [[BarChartData alloc] initWithDataSet:set]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:10.f]]; [data setDrawValues:NO]; + data.barWidth = 0.8; + _chartView.data = data; } } diff --git a/ChartsDemo/Classes/Demos/StackedBarChartViewController.m b/ChartsDemo/Classes/Demos/StackedBarChartViewController.m index 4fc2673f36..03fe9308f2 100644 --- a/ChartsDemo/Classes/Demos/StackedBarChartViewController.m +++ b/ChartsDemo/Classes/Demos/StackedBarChartViewController.m @@ -35,7 +35,6 @@ - (void)viewDidLoad self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -51,17 +50,18 @@ - (void)viewDidLoad _chartView.descriptionText = @""; _chartView.noDataTextDescription = @"You need to provide data for the chart."; - _chartView.maxVisibleValueCount = 60; + _chartView.maxVisibleCount = 40; _chartView.pinchZoomEnabled = NO; _chartView.drawGridBackgroundEnabled = NO; _chartView.drawBarShadowEnabled = NO; _chartView.drawValueAboveBarEnabled = NO; + NSNumberFormatter *leftAxisFormatter = [[NSNumberFormatter alloc] init]; + leftAxisFormatter.maximumFractionDigits = 1; + leftAxisFormatter.negativeSuffix = @" $"; + ChartYAxis *leftAxis = _chartView.leftAxis; - leftAxis.valueFormatter = [[NSNumberFormatter alloc] init]; - leftAxis.valueFormatter.maximumFractionDigits = 1; - leftAxis.valueFormatter.negativeSuffix = @" $"; - leftAxis.valueFormatter.positiveSuffix = @" $"; + leftAxis.valueFormatter = [[ChartDefaultAxisValueFormatter alloc] initWithFormatter:leftAxisFormatter]; leftAxis.axisMinValue = 0.0; // this replaces startAtZero = YES _chartView.rightAxis.enabled = NO; @@ -100,13 +100,6 @@ - (void)updateChartData - (void)setDataCount:(int)count range:(double)range { - NSMutableArray *xVals = [[NSMutableArray alloc] init]; - - for (int i = 0; i < count; i++) - { - [xVals addObject:months[i % 12]]; - } - NSMutableArray *yVals = [[NSMutableArray alloc] init]; for (int i = 0; i < count; i++) @@ -116,20 +109,19 @@ - (void)setDataCount:(int)count range:(double)range double val2 = (double) (arc4random_uniform(mult) + mult / 3); double val3 = (double) (arc4random_uniform(mult) + mult / 3); - [yVals addObject:[[BarChartDataEntry alloc] initWithValues:@[@(val1), @(val2), @(val3)] xIndex:i]]; + [yVals addObject:[[BarChartDataEntry alloc] initWithX:i yValues:@[@(val1), @(val2), @(val3)]]]; } BarChartDataSet *set1 = nil; if (_chartView.data.dataSetCount > 0) { set1 = (BarChartDataSet *)_chartView.data.dataSets[0]; - set1.yVals = yVals; - _chartView.data.xValsObjc = xVals; + set1.values = yVals; [_chartView notifyDataSetChanged]; } else { - set1 = [[BarChartDataSet alloc] initWithYVals:yVals label:@"Statistics Vienna 2014"]; + set1 = [[BarChartDataSet alloc] initWithValues:yVals label:@"Statistics Vienna 2014"]; set1.colors = @[ChartColorTemplates.vordiplom[0], ChartColorTemplates.vordiplom[1], ChartColorTemplates.vordiplom[2]]; set1.stackLabels = @[@"Births", @"Divorces", @"Marriages"]; @@ -141,11 +133,16 @@ - (void)setDataCount:(int)count range:(double)range formatter.negativeSuffix = @" $"; formatter.positiveSuffix = @" $"; - BarChartData *data = [[BarChartData alloc] initWithXVals:xVals dataSets:dataSets]; + BarChartData *data = [[BarChartData alloc] initWithDataSets:dataSets]; [data setValueFont:[UIFont fontWithName:@"HelveticaNeue-Light" size:7.f]]; [data setValueFormatter:formatter]; + [data setValueTextColor:UIColor.whiteColor]; + + data.barWidth = 0.8; _chartView.data = data; + + _chartView.fitBars = YES; } } diff --git a/ChartsDemo/Classes/Formatters/DayAxisValueFormatter.h b/ChartsDemo/Classes/Formatters/DayAxisValueFormatter.h new file mode 100644 index 0000000000..6d665c3835 --- /dev/null +++ b/ChartsDemo/Classes/Formatters/DayAxisValueFormatter.h @@ -0,0 +1,16 @@ +// +// DayAxisValueFormatter.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 22/07/2016. +// Copyright © 2016 dcg. All rights reserved. +// + +#import +#import "ChartsDemo-Swift.h" + +@interface DayAxisValueFormatter : NSObject + +- (id)initForChart:(BarLineChartViewBase *)chart; + +@end diff --git a/ChartsDemo/Classes/Formatters/DayAxisValueFormatter.m b/ChartsDemo/Classes/Formatters/DayAxisValueFormatter.m new file mode 100644 index 0000000000..9533723ad6 --- /dev/null +++ b/ChartsDemo/Classes/Formatters/DayAxisValueFormatter.m @@ -0,0 +1,162 @@ +// +// DayAxisValueFormatter.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 22/07/2016. +// Copyright © 2016 dcg. All rights reserved. +// + +#import "DayAxisValueFormatter.h" + +@implementation DayAxisValueFormatter +{ + NSArray *months; + __weak BarLineChartViewBase *_chart; +} + +- (id)initForChart:(BarLineChartViewBase *)chart +{ + self = [super init]; + if (self) + { + self->_chart = chart; + + months = @[ + @"Jan", @"Feb", @"Mar", + @"Apr", @"May", @"Jun", + @"Jul", @"Aug", @"Sep", + @"Oct", @"Nov", @"Dec" + ]; + } + return self; +} + +- (NSString *)stringForValue:(double)value + axis:(ChartAxisBase *)axis +{ + int days = (int)value; + int year = [self determineYearForDays:days]; + int month = [self determineMonthForDayOfYear:days]; + + NSString *monthName = months[month % months.count]; + NSString *yearName = [@(year) stringValue]; + + if (_chart.visibleXRange > 30 * axis.labelCount) + { + return [NSString stringWithFormat:@"%@ %@", monthName, yearName]; + } + else + { + int dayOfMonth = [self determineDayOfMonthForDayOfYear:days month:month + 12 * (year - 2016)]; + + NSString *appendix = @"th"; + + switch (dayOfMonth) + { + case 1: + appendix = @"st"; + break; + case 2: + appendix = @"nd"; + break; + case 3: + appendix = @"rd"; + break; + case 21: + appendix = @"st"; + break; + case 22: + appendix = @"nd"; + break; + case 23: + appendix = @"rd"; + break; + case 31: + appendix = @"st"; + break; + } + + return dayOfMonth == 0 ? @"" : [NSString stringWithFormat:@"%d%@ %@", dayOfMonth, appendix, monthName]; + } +} + +- (int)daysForMonth:(int)month year:(int)year +{ + if (month == 1) + { + if (year == 2016 || year == 2020) + { + return 29; + } + else + { + return 28; + } + } + + if (month == 3 || month == 5 || month == 8 || month == 10) + { + return 30; + } + + return 31; +} + +- (int)determineMonthForDayOfYear:(int)dayOfYear +{ + int month = -1; + int days = 0; + + while (days < dayOfYear) + { + month = month + 1; + + if (month >= 12) + month = 0; + + int year = [self determineYearForDays:days]; + days += [self daysForMonth:month year:year]; + } + + return MAX(month, 0); +} + + +- (int)determineDayOfMonthForDayOfYear:(int)dayOfYear month:(int)month +{ + int count = 0; + int days = 0; + + while (count < month) + { + int year = [self determineYearForDays:days]; + days += [self daysForMonth:count % 12 year:year]; + count++; + } + + return dayOfYear - days; +} + +- (int)determineYearForDays:(int)days +{ + if (days <= 366) + { + return 2016; + } + else if (days <= 730) + { + return 2017; + } + else if (days <= 1094) + { + return 2018; + } + else if (days <= 1458) + { + return 2019; + } + + return 2020; +} + +@end diff --git a/ChartsDemo/Classes/Formatters/IntAxisValueFormatter.h b/ChartsDemo/Classes/Formatters/IntAxisValueFormatter.h new file mode 100644 index 0000000000..514e7e375d --- /dev/null +++ b/ChartsDemo/Classes/Formatters/IntAxisValueFormatter.h @@ -0,0 +1,14 @@ +// +// IntAxisValueFormatter.h +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 22/07/2016. +// Copyright © 2016 dcg. All rights reserved. +// + +#import +#import "ChartsDemo-Swift.h" + +@interface IntAxisValueFormatter : NSObject + +@end diff --git a/ChartsDemo/Classes/Formatters/IntAxisValueFormatter.m b/ChartsDemo/Classes/Formatters/IntAxisValueFormatter.m new file mode 100644 index 0000000000..1c4dfb3755 --- /dev/null +++ b/ChartsDemo/Classes/Formatters/IntAxisValueFormatter.m @@ -0,0 +1,21 @@ +// +// IntAxisValueFormatter.m +// ChartsDemo +// +// Created by Daniel Cohen Gindi on 22/07/2016. +// Copyright © 2016 dcg. All rights reserved. +// + +#import "IntAxisValueFormatter.h" + +@implementation IntAxisValueFormatter +{ +} + +- (NSString *)stringForValue:(double)value + axis:(ChartAxisBase *)axis +{ + return [@((NSInteger)value) stringValue]; +} + +@end diff --git a/ChartsDemo/Classes/RealmBase/RealmDemoBaseViewController.m b/ChartsDemo/Classes/RealmBase/RealmDemoBaseViewController.m index 33e66f7bbf..979ec1f2f2 100644 --- a/ChartsDemo/Classes/RealmBase/RealmDemoBaseViewController.m +++ b/ChartsDemo/Classes/RealmBase/RealmDemoBaseViewController.m @@ -64,7 +64,7 @@ - (void)writeRandomDataToDbWithObjectCount:(NSInteger)objectCount for (int i = 0; i < objectCount; i++) { - RealmDemoData *d = [[RealmDemoData alloc] initWithValue:randomFloatBetween(40.f, 100.f) xIndex:i xValue:[@(i) stringValue]]; + RealmDemoData *d = [[RealmDemoData alloc] initWithXValue:i yValue:randomFloatBetween(40.f, 100.f)]; [realm addObject:d]; } @@ -86,7 +86,7 @@ - (void)writeRandomStackedDataToDbWithObjectCount:(NSInteger)objectCount NSArray *stack = @[@(val1), @(val2), @(100.f - val1 - val2)]; - RealmDemoData *d = [[RealmDemoData alloc] initWithStackValues:stack xIndex:i xValue:[@(i) stringValue]]; + RealmDemoData *d = [[RealmDemoData alloc] initWithXValue:i stackValues:stack]; [realm addObject:d]; } @@ -114,12 +114,11 @@ - (void)writeRandomCandleDataToDbWithObjectCount:(NSInteger)objectCount BOOL even = i % 2 == 0; - RealmDemoData *d = [[RealmDemoData alloc] initWithHigh:val + high - low:val - low - open:even ? val + open : val - open - close:even ? val - close : val + close - xIndex:i - xValue:[@(i) stringValue]]; + RealmDemoData *d = [[RealmDemoData alloc] initWithXValue:i + high:val + high + low:val - low + open:even ? val + open : val - open + close:even ? val - close : val + close]; [realm addObject:d]; } @@ -137,7 +136,9 @@ - (void)writeRandomBubbleDataToDbWithObjectCount:(NSInteger)objectCount for (int i = 0; i < objectCount; i++) { - RealmDemoData *d = [[RealmDemoData alloc] initWithValue:randomFloatBetween(30.f, 130.f) xIndex:i bubbleSize:randomFloatBetween(15.f, 35.f) xValue:[@(i) stringValue]]; + RealmDemoData *d = [[RealmDemoData alloc] initWithXValue:i + yValue:randomFloatBetween(30.f, 130.f) + bubbleSize:randomFloatBetween(15.f, 35.f)]; [realm addObject:d]; } @@ -172,7 +173,8 @@ - (void)writeRandomPieDataToDb for (int i = 0; i < values.count; i++) { - RealmDemoData *d = [[RealmDemoData alloc] initWithValue:randomFloatBetween(values[i].floatValue, 23.f) xIndex:i xValue:xValues[i]]; + RealmDemoData *d = [[RealmDemoData alloc] initWithYValue:randomFloatBetween(values[i].floatValue, 23.f) + label:xValues[i]]; [realm addObject:d]; } @@ -190,7 +192,7 @@ - (void)setupBarLineChartView:(BarLineChartViewBase *)chartView ChartYAxis *leftAxis = chartView.leftAxis; leftAxis.labelFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:8.f]; leftAxis.labelTextColor = UIColor.darkGrayColor; - leftAxis.valueFormatter = percentFormatter; + leftAxis.valueFormatter = [[ChartDefaultAxisValueFormatter alloc] initWithFormatter:percentFormatter]; } - (void)styleData:(ChartData *)data diff --git a/ChartsDemo/Classes/RealmBase/RealmDemoData.h b/ChartsDemo/Classes/RealmBase/RealmDemoData.h index 0518486fec..c5f96b2e20 100644 --- a/ChartsDemo/Classes/RealmBase/RealmDemoData.h +++ b/ChartsDemo/Classes/RealmBase/RealmDemoData.h @@ -11,38 +11,41 @@ @interface RealmDemoData : RLMObject -- (id)initWithValue:(float)value - xIndex:(int)xIndex - xValue:(NSString *)xValue; +- (id)initWithYValue:(double)yValue; -- (id)initWithStackValues:(NSArray *)stackValues - xIndex:(int)xIndex - xValue:(NSString *)xValue; +- (id)initWithXValue:(double)xValue + yValue:(double)yValue; -- (id)initWithHigh:(float)high - low:(float)low - open:(float)open - close:(float)close - xIndex:(int)xIndex xValue:(NSString *)xValue; +- (id)initWithXValue:(double)xValue + stackValues:(NSArray *)stackValues; -- (id)initWithValue:(float)value - xIndex:(int)xIndex - bubbleSize:(float)bubbleSize - xValue:(NSString *)xValue; +- (id)initWithXValue:(double)xValue + high:(double)high + low:(double)low + open:(double)open + close:(double)close; -@property (nonatomic, assign) float value; +- (id)initWithXValue:(double)xValue + yValue:(double)yValue + bubbleSize:(double)bubbleSize; -@property (nonatomic, assign) float open; -@property (nonatomic, assign) float close; -@property (nonatomic, assign) float high; -@property (nonatomic, assign) float low; +/// Constructor for pie chart +- (id)initWithYValue:(double)yValue + label:(NSString *)label; -@property (nonatomic, assign) float bubbleSize; +@property (nonatomic, assign) double xValue; +@property (nonatomic, assign) double yValue; + +@property (nonatomic, assign) double open; +@property (nonatomic, assign) double close; +@property (nonatomic, assign) double high; +@property (nonatomic, assign) double low; + +@property (nonatomic, assign) double bubbleSize; @property (nonatomic, strong) RLMArray *stackValues; -@property (nonatomic, assign) int xIndex; -@property (nonatomic, strong) NSString *xValue; +@property (nonatomic, strong) NSString *label; @property (nonatomic, strong) NSString *someStringField; diff --git a/ChartsDemo/Classes/RealmBase/RealmDemoData.m b/ChartsDemo/Classes/RealmBase/RealmDemoData.m index 9dc3d2c0ec..5f39ba7d24 100644 --- a/ChartsDemo/Classes/RealmBase/RealmDemoData.m +++ b/ChartsDemo/Classes/RealmBase/RealmDemoData.m @@ -10,53 +10,39 @@ @implementation RealmDemoData -- (id)initWithValue:(float)value - xIndex:(int)xIndex - xValue:(NSString *)xValue +- (id)initWithYValue:(double)yValue { self = [super init]; if (self) { - self.value = value; - self.xIndex = xIndex; - self.xValue = xValue; + self.yValue = yValue; } return self; } -- (id)initWithHigh:(float)high - low:(float)low - open:(float)open - close:(float)close - xIndex:(int)xIndex xValue:(NSString *)xValue +- (id)initWithXValue:(double)xValue + yValue:(double)yValue { self = [super init]; if (self) { - self.value = (high + low) / 2.f; - self.high = high; - self.low = low; - self.open = open; - self.close = close; - self.xIndex = xIndex; self.xValue = xValue; + self.yValue = yValue; } return self; } -- (id)initWithStackValues:(NSArray *)stackValues - xIndex:(int)xIndex - xValue:(NSString *)xValue +- (id)initWithXValue:(double)xValue + stackValues:(NSArray *)stackValues { self = [super init]; if (self) { - self.xIndex = xIndex; self.xValue = xValue; self.stackValues = [[RLMArray alloc] initWithObjectClassName:@"RealmFloat"]; @@ -69,19 +55,52 @@ - (id)initWithStackValues:(NSArray *)stackValues return self; } -- (id)initWithValue:(float)value - xIndex:(int)xIndex - bubbleSize:(float)bubbleSize - xValue:(NSString *)xValue; +- (id)initWithXValue:(double)xValue + high:(double)high + low:(double)low + open:(double)open + close:(double)close +{ + self = [super init]; + + if (self) + { + self.xValue = xValue; + self.yValue = (high + low) / 2.f; + self.high = high; + self.low = low; + self.open = open; + self.close = close; + } + + return self; +} + +- (id)initWithXValue:(double)xValue + yValue:(double)yValue + bubbleSize:(double)bubbleSize { self = [super init]; if (self) { - self.value = value; - self.xIndex = xIndex; - self.bubbleSize = bubbleSize; self.xValue = xValue; + self.yValue = yValue; + self.bubbleSize = bubbleSize; + } + + return self; +} + +- (id)initWithYValue:(double)yValue + label:(NSString *)label +{ + self = [super init]; + + if (self) + { + self.yValue = yValue; + self.label = label; } return self; diff --git a/ChartsDemo/Classes/RealmBase/Score.h b/ChartsDemo/Classes/RealmBase/Score.h index 2723816bc9..1f01ed8692 100644 --- a/ChartsDemo/Classes/RealmBase/Score.h +++ b/ChartsDemo/Classes/RealmBase/Score.h @@ -11,11 +11,11 @@ @interface Score : RLMObject @property (nonatomic, assign) float totalScore; -@property (nonatomic, assign) NSInteger scoreNr; +@property (nonatomic, assign) double scoreNr; @property (nonatomic, strong) NSString *playerName; - (id)initWithTotalScore:(float)totalScore - scoreNr:(NSInteger)scoreNr + scoreNr:(double)scoreNr playerName:(NSString *)playerName; @end diff --git a/ChartsDemo/Classes/RealmBase/Score.m b/ChartsDemo/Classes/RealmBase/Score.m index 404f12b475..9dca891d65 100644 --- a/ChartsDemo/Classes/RealmBase/Score.m +++ b/ChartsDemo/Classes/RealmBase/Score.m @@ -11,7 +11,7 @@ @implementation Score - (id)initWithTotalScore:(float)totalScore - scoreNr:(NSInteger)scoreNr + scoreNr:(double)scoreNr playerName:(NSString *)playerName { self = [super init]; diff --git a/ChartsDemo/Classes/RealmDemos/RealmBarChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmBarChartViewController.m index d6ea47450f..f499ac4911 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmBarChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmBarChartViewController.m @@ -30,12 +30,11 @@ - (void)viewDidLoad [self writeRandomDataToDbWithObjectCount:20]; - self.title = @"Realm.io Bar Chart Chart"; + self.title = @"Realm.io Bar Chart"; self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -64,17 +63,18 @@ - (void)setData RLMResults *results = [RealmDemoData allObjectsInRealm:realm]; - RealmBarDataSet *set = [[RealmBarDataSet alloc] initWithResults:results yValueField:@"value" xIndexField:@"xIndex"]; + RealmBarDataSet *set = [[RealmBarDataSet alloc] initWithResults:results xValueField:@"xValue" yValueField:@"yValue"]; set.colors = @[[ChartColorTemplates colorFromString:@"#FF5722"], [ChartColorTemplates colorFromString:@"#03A9F4"]]; set.label = @"Realm BarDataSet"; NSArray> *dataSets = @[set]; - RealmBarData *data = [[RealmBarData alloc] initWithResults:results xValueField:@"xValue" dataSets:dataSets]; + BarChartData *data = [[BarChartData alloc] initWithDataSets:dataSets]; [self styleData:data]; - [_chartView zoom:5.f scaleY:1.f x:0.f y:0.f]; + [_chartView zoomWithScaleX:5.f scaleY:1.f x:0.f y:0.f]; + _chartView.fitBars = YES; _chartView.data = data; [_chartView animateWithYAxisDuration:1.4 easingOption:ChartEasingOptionEaseInOutQuart]; diff --git a/ChartsDemo/Classes/RealmDemos/RealmBubbleChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmBubbleChartViewController.m index ea9c0d4521..882e8253b7 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmBubbleChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmBubbleChartViewController.m @@ -30,7 +30,7 @@ - (void)viewDidLoad [self writeRandomBubbleDataToDbWithObjectCount:10]; - self.title = @"Realm.io Bubble Chart Chart"; + self.title = @"Realm.io Bubble Chart"; self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @@ -67,14 +67,14 @@ - (void)setData RLMResults *results = [RealmDemoData allObjectsInRealm:realm]; - RealmBubbleDataSet *set = [[RealmBubbleDataSet alloc] initWithResults:results yValueField:@"value" xIndexField:@"xIndex" sizeField:@"bubbleSize"]; + RealmBubbleDataSet *set = [[RealmBubbleDataSet alloc] initWithResults:results xValueField:@"xValue" yValueField:@"yValue" sizeField:@"bubbleSize"]; set.label = @"Realm BubbleDataSet"; [set setColors:ChartColorTemplates.colorful alpha:0.43f]; NSArray> *dataSets = @[set]; - RealmBubbleData *data = [[RealmBubbleData alloc] initWithResults:results xValueField:@"xValue" dataSets:dataSets]; + BubbleChartData *data = [[BubbleChartData alloc] initWithDataSets:dataSets]; [self styleData:data]; _chartView.data = data; diff --git a/ChartsDemo/Classes/RealmDemos/RealmCandleChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmCandleChartViewController.m index dea512de95..cdb367eaf5 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmCandleChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmCandleChartViewController.m @@ -30,7 +30,7 @@ - (void)viewDidLoad [self writeRandomCandleDataToDbWithObjectCount:50]; - self.title = @"Realm.io CandleStick Chart Chart"; + self.title = @"Realm.io CandleStick Chart"; self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @@ -68,7 +68,7 @@ - (void)setData RLMResults *results = [RealmDemoData allObjectsInRealm:realm]; - RealmCandleDataSet *set = [[RealmCandleDataSet alloc] initWithResults:results highField:@"high" lowField:@"low" openField:@"open" closeField:@"close" xIndexField:@"xIndex"]; + RealmCandleDataSet *set = [[RealmCandleDataSet alloc] initWithResults:results xValueField:@"xValue" highField:@"high" lowField:@"low" openField:@"open" closeField:@"close"]; set.label = @"Realm CandleDataSet"; set.shadowColor = UIColor.darkGrayColor; @@ -81,10 +81,10 @@ - (void)setData NSArray> *dataSets = @[set]; - RealmCandleData *data = [[RealmCandleData alloc] initWithResults:results xValueField:@"xValue" dataSets:dataSets]; + CandleChartData *data = [[CandleChartData alloc] initWithDataSets:dataSets]; [self styleData:data]; - [_chartView zoom:5.f scaleY:1.f x:0.f y:0.f]; + [_chartView zoomWithScaleX:5.f scaleY:1.f x:0.f y:0.f]; _chartView.data = data; [_chartView animateWithYAxisDuration:1.4 easingOption:ChartEasingOptionEaseInOutQuart]; diff --git a/ChartsDemo/Classes/RealmDemos/RealmHorizontalBarChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmHorizontalBarChartViewController.m index 79b21b9c5b..ad10659ac1 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmHorizontalBarChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmHorizontalBarChartViewController.m @@ -30,12 +30,11 @@ - (void)viewDidLoad [self writeRandomStackedDataToDbWithObjectCount:50]; - self.title = @"Realm.io Horizontal Bar Chart Chart"; + self.title = @"Realm.io Horizontal Bar Chart"; self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @{@"key": @"toggleHighlight", @"label": @"Toggle Highlight"}, - @{@"key": @"toggleHighlightArrow", @"label": @"Toggle Highlight Arrow"}, @{@"key": @"animateX", @"label": @"Animate X"}, @{@"key": @"animateY", @"label": @"Animate Y"}, @{@"key": @"animateXY", @"label": @"Animate XY"}, @@ -67,8 +66,8 @@ - (void)setData RLMResults *results = [RealmDemoData allObjectsInRealm:realm]; - // RealmBarDataSet *set = [[RealmBarDataSet alloc] initWithResults:results yValueField:@"value" xIndexField:@"xIndex"]; - RealmBarDataSet *set = [[RealmBarDataSet alloc] initWithResults:results yValueField:@"stackValues" xIndexField:@"xIndex" stackValueField:@"floatValue"]; // stacked entries + // RealmBarDataSet *set = [[RealmBarDataSet alloc] initWithResults:results yValueField:@@"yValue" xValueField:@"xIndex"]; + RealmBarDataSet *set = [[RealmBarDataSet alloc] initWithResults:results xValueField:@"xValue" yValueField:@"stackValues" stackValueField:@"floatValue"]; // stacked entries set.colors = @[ [ChartColorTemplates colorFromString:@"#8BC34A"], @@ -85,7 +84,7 @@ - (void)setData NSArray> *dataSets = @[set]; - RealmBarData *data = [[RealmBarData alloc] initWithResults:results xValueField:@"xValue" dataSets:dataSets]; + BarChartData *data = [[BarChartData alloc] initWithDataSets:dataSets]; [self styleData:data]; data.valueTextColor = UIColor.whiteColor; diff --git a/ChartsDemo/Classes/RealmDemos/RealmHorizontalBarChartViewController.xib b/ChartsDemo/Classes/RealmDemos/RealmHorizontalBarChartViewController.xib index 3e3f58fccc..25e43884bf 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmHorizontalBarChartViewController.xib +++ b/ChartsDemo/Classes/RealmDemos/RealmHorizontalBarChartViewController.xib @@ -1,8 +1,8 @@ - + - + diff --git a/ChartsDemo/Classes/RealmDemos/RealmLineChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmLineChartViewController.m index 738836c526..f0a56a9a72 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmLineChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmLineChartViewController.m @@ -30,7 +30,7 @@ - (void)viewDidLoad [self writeRandomDataToDbWithObjectCount:40]; - self.title = @"Realm.io Line Chart Chart"; + self.title = @"Realm.io Line Chart"; self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @@ -72,7 +72,7 @@ - (void)setData RLMResults *results = [RealmDemoData allObjectsInRealm:realm]; - RealmLineDataSet *set = [[RealmLineDataSet alloc] initWithResults:results yValueField:@"value" xIndexField:@"xIndex"]; + RealmLineDataSet *set = [[RealmLineDataSet alloc] initWithResults:results xValueField:@"xValue" yValueField:@"yValue"]; set.drawCubicEnabled = NO; set.label = @"Realm LineDataSet"; @@ -84,10 +84,10 @@ - (void)setData NSArray> *dataSets = @[set]; - RealmLineData *data = [[RealmLineData alloc] initWithResults:results xValueField:@"xValue" dataSets:dataSets]; + LineChartData *data = [[LineChartData alloc] initWithDataSets:dataSets]; [self styleData:data]; - [_chartView zoom:5.f scaleY:1.f x:0.f y:0.f]; + [_chartView zoomWithScaleX:5.f scaleY:1.f x:0.f y:0.f]; _chartView.data = data; [_chartView animateWithYAxisDuration:1.4 easingOption:ChartEasingOptionEaseInOutQuart]; diff --git a/ChartsDemo/Classes/RealmDemos/RealmPieChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmPieChartViewController.m index fdc3a78b7b..a5a9d1c938 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmPieChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmPieChartViewController.m @@ -30,7 +30,7 @@ - (void)viewDidLoad [self writeRandomPieDataToDb]; - self.title = @"Realm.io Pie Chart Chart"; + self.title = @"Realm.io Pie Chart"; self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Y-Values"}, @@ -80,7 +80,7 @@ - (void)setData RLMResults *results = [RealmDemoData allObjectsInRealm:realm]; - RealmPieDataSet *set = [[RealmPieDataSet alloc] initWithResults:results yValueField:@"value" xIndexField:@"xIndex"]; + RealmPieDataSet *set = [[RealmPieDataSet alloc] initWithResults:results yValueField:@"yValue" label:@"label"]; set.valueFont = [UIFont systemFontOfSize:9.f]; set.colors = ChartColorTemplates.vordiplom; @@ -89,7 +89,7 @@ - (void)setData NSArray> *dataSets = @[set]; - RealmPieData *data = [[RealmPieData alloc] initWithResults:results xValueField:@"xValue" dataSets:dataSets]; + PieChartData *data = [[PieChartData alloc] initWithDataSets:dataSets]; [self styleData:data]; data.valueTextColor = UIColor.whiteColor; data.valueFont = [UIFont fontWithName:@"HelveticaNeue-Light" size:12.f]; diff --git a/ChartsDemo/Classes/RealmDemos/RealmRadarChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmRadarChartViewController.m index f70a1807e4..862f52aa41 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmRadarChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmRadarChartViewController.m @@ -30,7 +30,7 @@ - (void)viewDidLoad [self writeRandomDataToDbWithObjectCount:7]; - self.title = @"Realm.io Pie Chart Chart"; + self.title = @"Realm.io Pie Chart"; self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @@ -51,6 +51,7 @@ - (void)viewDidLoad [self setupRadarChartView:_chartView]; _chartView.yAxis.enabled = NO; + _chartView.xAxis.enabled = NO; _chartView.webAlpha = 0.7f; _chartView.innerWebColor = UIColor.darkGrayColor; _chartView.webColor = UIColor.grayColor; @@ -71,8 +72,8 @@ - (void)setData RLMResults *results = [RealmDemoData allObjectsInRealm:realm]; - // RealmRadarDataSet *set = [[RealmRadarDataSet alloc] initWithResults:results yValueField:@"stackValues" xIndexField:@"xIndex"]; // normal entries - RealmRadarDataSet *set = [[RealmRadarDataSet alloc] initWithResults:results yValueField:@"value" xIndexField:@"xIndex"]; // stacked entries + // RealmRadarDataSet *set = [[RealmRadarDataSet alloc] initWithResults:results yValueField:@"stackValues" xValueField:@"xIndex"]; // normal entries + RealmRadarDataSet *set = [[RealmRadarDataSet alloc] initWithResults:results xValueField:@"xValue" yValueField:@"yValue"]; // stacked entries set.label = @"Realm RadarDataSet"; set.drawFilledEnabled = YES; @@ -83,7 +84,7 @@ - (void)setData NSArray> *dataSets = @[set]; - RadarChartData *data = [[RadarChartData alloc] initWithXVals:@[@"2013", @"2014", @"2015", @"2016", @"2017", @"2018", @"2019"] dataSets:dataSets]; + RadarChartData *data = [[RadarChartData alloc] initWithDataSets:dataSets]; [self styleData:data]; _chartView.data = data; diff --git a/ChartsDemo/Classes/RealmDemos/RealmScatterChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmScatterChartViewController.m index a2b721061d..1e0ab6a1e4 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmScatterChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmScatterChartViewController.m @@ -30,7 +30,7 @@ - (void)viewDidLoad [self writeRandomDataToDbWithObjectCount:45]; - self.title = @"Realm.io Scatter Chart Chart"; + self.title = @"Realm.io Scatter Chart"; self.options = @[ @{@"key": @"toggleValues", @"label": @"Toggle Values"}, @@ -67,7 +67,7 @@ - (void)setData RLMResults *results = [RealmDemoData allObjectsInRealm:realm]; - RealmScatterDataSet *set = [[RealmScatterDataSet alloc] initWithResults:results yValueField:@"value" xIndexField:@"xIndex"]; + RealmScatterDataSet *set = [[RealmScatterDataSet alloc] initWithResults:results xValueField:@"xValue" yValueField:@"yValue"]; set.label = @"Realm ScatterDataSet"; set.scatterShapeSize = 9.f; @@ -76,10 +76,10 @@ - (void)setData NSArray> *dataSets = @[set]; - RealmScatterData *data = [[RealmScatterData alloc] initWithResults:results xValueField:@"xValue" dataSets:dataSets]; + ScatterChartData *data = [[ScatterChartData alloc] initWithDataSets:dataSets]; [self styleData:data]; - [_chartView zoom:5.f scaleY:1.f x:0.f y:0.f]; + [_chartView zoomWithScaleX:5.f scaleY:1.f x:0.f y:0.f]; _chartView.data = data; [_chartView animateWithYAxisDuration:1.4 easingOption:ChartEasingOptionEaseInOutQuart]; diff --git a/ChartsDemo/Classes/RealmDemos/RealmWikiExampleChartViewController.m b/ChartsDemo/Classes/RealmDemos/RealmWikiExampleChartViewController.m index e8801926a6..ec12436b63 100644 --- a/ChartsDemo/Classes/RealmDemos/RealmWikiExampleChartViewController.m +++ b/ChartsDemo/Classes/RealmDemos/RealmWikiExampleChartViewController.m @@ -16,7 +16,10 @@ #import #import "Score.h" -@interface RealmWikiExampleChartViewController () +@interface RealmWikiExampleChartViewController () +{ + RLMResults *results; +} @property (nonatomic, strong) IBOutlet LineChartView *lineChartView; @property (nonatomic, strong) IBOutlet BarChartView *barChartView; @@ -44,9 +47,13 @@ - (void)viewDidLoad _lineChartView.leftAxis.drawGridLinesEnabled = NO; _lineChartView.xAxis.drawGridLinesEnabled = NO; + _lineChartView.xAxis.labelCount = 5; + _lineChartView.xAxis.granularity = 1.0; _barChartView.leftAxis.drawGridLinesEnabled = NO; _barChartView.xAxis.drawGridLinesEnabled = NO; - + _barChartView.xAxis.labelCount = 5; + _barChartView.xAxis.granularity = 1.0; + // setup realm RLMRealm *realm = [RLMRealm defaultRealm]; @@ -56,19 +63,19 @@ - (void)viewDidLoad [realm deleteObjects:Score.allObjects]; // write some demo-data into the realm.io database - Score *score1 = [[Score alloc] initWithTotalScore:100.f scoreNr:0 playerName:@"Peter"]; + Score *score1 = [[Score alloc] initWithTotalScore:100.f scoreNr:0.0 playerName:@"Peter"]; [realm addObject:score1]; - Score *score2 = [[Score alloc] initWithTotalScore:110.f scoreNr:1 playerName:@"Lisa"]; + Score *score2 = [[Score alloc] initWithTotalScore:110.f scoreNr:1.0 playerName:@"Lisa"]; [realm addObject:score2]; - Score *score3 = [[Score alloc] initWithTotalScore:130.f scoreNr:2 playerName:@"Dennis"]; + Score *score3 = [[Score alloc] initWithTotalScore:130.f scoreNr:2.0 playerName:@"Dennis"]; [realm addObject:score3]; - Score *score4 = [[Score alloc] initWithTotalScore:70.f scoreNr:3 playerName:@"Luke"]; + Score *score4 = [[Score alloc] initWithTotalScore:70.f scoreNr:3.0 playerName:@"Luke"]; [realm addObject:score4]; - Score *score5 = [[Score alloc] initWithTotalScore:80.f scoreNr:4 playerName:@"Sarah"]; + Score *score5 = [[Score alloc] initWithTotalScore:80.f scoreNr:4.0 playerName:@"Sarah"]; [realm addObject:score5]; // commit changes to realm db @@ -88,12 +95,15 @@ - (void)setData { RLMRealm *realm = [RLMRealm defaultRealm]; - // Line chart - RLMResults *results = [Score allObjectsInRealm:realm]; + results = [Score allObjectsInRealm:realm]; + + _lineChartView.xAxis.valueFormatter = self; + _barChartView.xAxis.valueFormatter = self; - RealmLineDataSet *lineDataSet = [[RealmLineDataSet alloc] initWithResults:results yValueField:@"totalScore" xIndexField:@"scoreNr"]; + // Line chart + RealmLineDataSet *lineDataSet = [[RealmLineDataSet alloc] initWithResults:results xValueField:@"scoreNr" yValueField:@"totalScore"]; lineDataSet.drawCubicEnabled = NO; - lineDataSet.label = @"Realm LineDataSet"; + lineDataSet.label = @"Result Scores"; lineDataSet.drawCircleHoleEnabled = NO; [lineDataSet setColor:[ChartColorTemplates colorFromString:@"#FF5722"]]; [lineDataSet setCircleColor:[ChartColorTemplates colorFromString:@"#FF5722"]]; @@ -102,7 +112,7 @@ - (void)setData NSArray> *lineDataSets = @[lineDataSet]; - RealmLineData *lineData = [[RealmLineData alloc] initWithResults:results xValueField:@"playerName" dataSets:lineDataSets]; + LineChartData *lineData = [[LineChartData alloc] initWithDataSets:lineDataSets]; [self styleData:lineData]; // set data @@ -111,7 +121,7 @@ - (void)setData easingOption:ChartEasingOptionEaseInOutQuart]; // Bar chart - RealmBarDataSet *barDataSet = [[RealmBarDataSet alloc] initWithResults:results yValueField:@"totalScore" xIndexField:@"scoreNr"]; + RealmBarDataSet *barDataSet = [[RealmBarDataSet alloc] initWithResults:results xValueField:@"scoreNr" yValueField:@"totalScore"]; barDataSet.colors = @[ [ChartColorTemplates colorFromString:@"#FF5722"], [ChartColorTemplates colorFromString:@"#03A9F4"], @@ -120,9 +130,10 @@ - (void)setData NSArray> *barDataSets = @[barDataSet]; - RealmBarData *barData = [[RealmBarData alloc] initWithResults:results xValueField:@"playerName" dataSets:barDataSets]; + BarChartData *barData = [[BarChartData alloc] initWithDataSets:barDataSets]; [self styleData:barData]; + _barChartView.fitBars = YES; _barChartView.data = barData; [_barChartView animateWithYAxisDuration:1.4 easingOption:ChartEasingOptionEaseInOutQuart]; @@ -154,4 +165,12 @@ - (void)chartValueNothingSelected:(ChartViewBase * __nonnull)chartView } } +#pragma mark - ChartAxisValueFormatter + +- (NSString *)stringForValue:(double)value + axis:(ChartAxisBase *)axis +{ + return ((Score *)results[(int)value]).playerName; +} + @end diff --git a/ChartsRealm/ChartsRealm.xcodeproj/project.pbxproj b/ChartsRealm/ChartsRealm.xcodeproj/project.pbxproj index a6e49c4fec..94b4e542f7 100644 --- a/ChartsRealm/ChartsRealm.xcodeproj/project.pbxproj +++ b/ChartsRealm/ChartsRealm.xcodeproj/project.pbxproj @@ -15,40 +15,26 @@ 5B4AC1821C4C12B80028D1A6 /* RealmChartUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4AC1811C4C12B80028D1A6 /* RealmChartUtils.swift */; }; 5B4AC1831C4C12B80028D1A6 /* RealmChartUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4AC1811C4C12B80028D1A6 /* RealmChartUtils.swift */; }; 5B680D3D1A9D1AD90026A057 /* ChartsRealm.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B680D3C1A9D1AD90026A057 /* ChartsRealm.h */; settings = {ATTRIBUTES = (Public, ); }; }; - 5B88D8851C634EBB00B54CBD /* RealmBarData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8731C634EBB00B54CBD /* RealmBarData.swift */; }; - 5B88D8861C634EBB00B54CBD /* RealmBarData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8731C634EBB00B54CBD /* RealmBarData.swift */; }; 5B88D8871C634EBB00B54CBD /* RealmBarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8741C634EBB00B54CBD /* RealmBarDataSet.swift */; }; 5B88D8881C634EBB00B54CBD /* RealmBarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8741C634EBB00B54CBD /* RealmBarDataSet.swift */; }; 5B88D8891C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8751C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift */; }; 5B88D88A1C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8751C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift */; }; 5B88D88B1C634EBB00B54CBD /* RealmBaseDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8761C634EBB00B54CBD /* RealmBaseDataSet.swift */; }; 5B88D88C1C634EBB00B54CBD /* RealmBaseDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8761C634EBB00B54CBD /* RealmBaseDataSet.swift */; }; - 5B88D88D1C634EBB00B54CBD /* RealmBubbleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8771C634EBB00B54CBD /* RealmBubbleData.swift */; }; - 5B88D88E1C634EBB00B54CBD /* RealmBubbleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8771C634EBB00B54CBD /* RealmBubbleData.swift */; }; 5B88D88F1C634EBB00B54CBD /* RealmBubbleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8781C634EBB00B54CBD /* RealmBubbleDataSet.swift */; }; 5B88D8901C634EBB00B54CBD /* RealmBubbleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8781C634EBB00B54CBD /* RealmBubbleDataSet.swift */; }; - 5B88D8911C634EBB00B54CBD /* RealmCandleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8791C634EBB00B54CBD /* RealmCandleData.swift */; }; - 5B88D8921C634EBB00B54CBD /* RealmCandleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8791C634EBB00B54CBD /* RealmCandleData.swift */; }; 5B88D8931C634EBB00B54CBD /* RealmCandleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87A1C634EBB00B54CBD /* RealmCandleDataSet.swift */; }; 5B88D8941C634EBB00B54CBD /* RealmCandleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87A1C634EBB00B54CBD /* RealmCandleDataSet.swift */; }; - 5B88D8951C634EBB00B54CBD /* RealmLineData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87B1C634EBB00B54CBD /* RealmLineData.swift */; }; - 5B88D8961C634EBB00B54CBD /* RealmLineData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87B1C634EBB00B54CBD /* RealmLineData.swift */; }; 5B88D8971C634EBB00B54CBD /* RealmLineDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87C1C634EBB00B54CBD /* RealmLineDataSet.swift */; }; 5B88D8981C634EBB00B54CBD /* RealmLineDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87C1C634EBB00B54CBD /* RealmLineDataSet.swift */; }; 5B88D8991C634EBB00B54CBD /* RealmLineRadarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87D1C634EBB00B54CBD /* RealmLineRadarDataSet.swift */; }; 5B88D89A1C634EBB00B54CBD /* RealmLineRadarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87D1C634EBB00B54CBD /* RealmLineRadarDataSet.swift */; }; 5B88D89B1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87E1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift */; }; 5B88D89C1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87E1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift */; }; - 5B88D89D1C634EBB00B54CBD /* RealmPieData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87F1C634EBB00B54CBD /* RealmPieData.swift */; }; - 5B88D89E1C634EBB00B54CBD /* RealmPieData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87F1C634EBB00B54CBD /* RealmPieData.swift */; }; 5B88D89F1C634EBB00B54CBD /* RealmPieDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8801C634EBB00B54CBD /* RealmPieDataSet.swift */; }; 5B88D8A01C634EBB00B54CBD /* RealmPieDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8801C634EBB00B54CBD /* RealmPieDataSet.swift */; }; - 5B88D8A11C634EBB00B54CBD /* RealmRadarData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8811C634EBB00B54CBD /* RealmRadarData.swift */; }; - 5B88D8A21C634EBB00B54CBD /* RealmRadarData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8811C634EBB00B54CBD /* RealmRadarData.swift */; }; 5B88D8A31C634EBB00B54CBD /* RealmRadarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8821C634EBB00B54CBD /* RealmRadarDataSet.swift */; }; 5B88D8A41C634EBB00B54CBD /* RealmRadarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8821C634EBB00B54CBD /* RealmRadarDataSet.swift */; }; - 5B88D8A51C634EBB00B54CBD /* RealmScatterData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8831C634EBB00B54CBD /* RealmScatterData.swift */; }; - 5B88D8A61C634EBB00B54CBD /* RealmScatterData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8831C634EBB00B54CBD /* RealmScatterData.swift */; }; 5B88D8A71C634EBB00B54CBD /* RealmScatterDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8841C634EBB00B54CBD /* RealmScatterDataSet.swift */; }; 5B88D8A81C634EBB00B54CBD /* RealmScatterDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8841C634EBB00B54CBD /* RealmScatterDataSet.swift */; }; 5B88D8B51C63504200B54CBD /* Charts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B88D8B01C63502500B54CBD /* Charts.framework */; settings = {ATTRIBUTES = (Required, ); }; }; @@ -57,20 +43,13 @@ 5BA899281CADB7F20012ED64 /* RealmLineDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87C1C634EBB00B54CBD /* RealmLineDataSet.swift */; }; 5BA899291CADB7F20012ED64 /* RealmChartUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B4AC1811C4C12B80028D1A6 /* RealmChartUtils.swift */; }; 5BA8992A1CADB7F20012ED64 /* RealmScatterDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8841C634EBB00B54CBD /* RealmScatterDataSet.swift */; }; - 5BA8992B1CADB7F20012ED64 /* RealmBarData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8731C634EBB00B54CBD /* RealmBarData.swift */; }; 5BA8992C1CADB7F20012ED64 /* RealmBarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8741C634EBB00B54CBD /* RealmBarDataSet.swift */; }; 5BA8992D1CADB7F20012ED64 /* RealmBaseDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8761C634EBB00B54CBD /* RealmBaseDataSet.swift */; }; 5BA8992E1CADB7F20012ED64 /* RealmCandleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87A1C634EBB00B54CBD /* RealmCandleDataSet.swift */; }; - 5BA8992F1CADB7F20012ED64 /* RealmCandleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8791C634EBB00B54CBD /* RealmCandleData.swift */; }; - 5BA899301CADB7F20012ED64 /* RealmBubbleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8771C634EBB00B54CBD /* RealmBubbleData.swift */; }; 5BA899311CADB7F20012ED64 /* RealmBarLineScatterCandleBubbleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8751C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift */; }; - 5BA899321CADB7F20012ED64 /* RealmScatterData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8831C634EBB00B54CBD /* RealmScatterData.swift */; }; 5BA899331CADB7F20012ED64 /* RealmBubbleDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8781C634EBB00B54CBD /* RealmBubbleDataSet.swift */; }; 5BA899341CADB7F20012ED64 /* RealmLineScatterCandleRadarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87E1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift */; }; 5BA899351CADB7F20012ED64 /* RealmRadarDataSet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8821C634EBB00B54CBD /* RealmRadarDataSet.swift */; }; - 5BA899361CADB7F20012ED64 /* RealmLineData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87B1C634EBB00B54CBD /* RealmLineData.swift */; }; - 5BA899371CADB7F20012ED64 /* RealmPieData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D87F1C634EBB00B54CBD /* RealmPieData.swift */; }; - 5BA899381CADB7F20012ED64 /* RealmRadarData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B88D8811C634EBB00B54CBD /* RealmRadarData.swift */; }; 5BA8993A1CADB7F20012ED64 /* Charts.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5B88D8B01C63502500B54CBD /* Charts.framework */; settings = {ATTRIBUTES = (Required, ); }; }; 5BA8993E1CADB7F20012ED64 /* ChartsRealm.h in Headers */ = {isa = PBXBuildFile; fileRef = 5B680D3C1A9D1AD90026A057 /* ChartsRealm.h */; settings = {ATTRIBUTES = (Public, ); }; }; 5BA8994C1CADB8EC0012ED64 /* Realm.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BA8994A1CADB8EC0012ED64 /* Realm.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; @@ -116,23 +95,16 @@ 5B4AC1321C44684E0028D1A6 /* RealmSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = RealmSwift.framework; sourceTree = ""; }; 5B4AC1811C4C12B80028D1A6 /* RealmChartUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmChartUtils.swift; sourceTree = ""; }; 5B680D3C1A9D1AD90026A057 /* ChartsRealm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChartsRealm.h; sourceTree = ""; }; - 5B88D8731C634EBB00B54CBD /* RealmBarData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmBarData.swift; sourceTree = ""; }; 5B88D8741C634EBB00B54CBD /* RealmBarDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmBarDataSet.swift; sourceTree = ""; }; 5B88D8751C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmBarLineScatterCandleBubbleDataSet.swift; sourceTree = ""; }; 5B88D8761C634EBB00B54CBD /* RealmBaseDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmBaseDataSet.swift; sourceTree = ""; }; - 5B88D8771C634EBB00B54CBD /* RealmBubbleData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmBubbleData.swift; sourceTree = ""; }; 5B88D8781C634EBB00B54CBD /* RealmBubbleDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmBubbleDataSet.swift; sourceTree = ""; }; - 5B88D8791C634EBB00B54CBD /* RealmCandleData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmCandleData.swift; sourceTree = ""; }; 5B88D87A1C634EBB00B54CBD /* RealmCandleDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmCandleDataSet.swift; sourceTree = ""; }; - 5B88D87B1C634EBB00B54CBD /* RealmLineData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmLineData.swift; sourceTree = ""; }; 5B88D87C1C634EBB00B54CBD /* RealmLineDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmLineDataSet.swift; sourceTree = ""; }; 5B88D87D1C634EBB00B54CBD /* RealmLineRadarDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmLineRadarDataSet.swift; sourceTree = ""; }; 5B88D87E1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmLineScatterCandleRadarDataSet.swift; sourceTree = ""; }; - 5B88D87F1C634EBB00B54CBD /* RealmPieData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmPieData.swift; sourceTree = ""; }; 5B88D8801C634EBB00B54CBD /* RealmPieDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmPieDataSet.swift; sourceTree = ""; }; - 5B88D8811C634EBB00B54CBD /* RealmRadarData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmRadarData.swift; sourceTree = ""; }; 5B88D8821C634EBB00B54CBD /* RealmRadarDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmRadarDataSet.swift; sourceTree = ""; }; - 5B88D8831C634EBB00B54CBD /* RealmScatterData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmScatterData.swift; sourceTree = ""; }; 5B88D8841C634EBB00B54CBD /* RealmScatterDataSet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealmScatterDataSet.swift; sourceTree = ""; }; 5B88D8A91C63502400B54CBD /* Charts.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = Charts.xcodeproj; path = ../Charts/Charts.xcodeproj; sourceTree = ""; }; 5BA899431CADB7F20012ED64 /* ChartsRealm.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = ChartsRealm.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -276,23 +248,16 @@ 5BA8EC6F1A9D151C00CE82E1 /* Data */ = { isa = PBXGroup; children = ( - 5B88D8731C634EBB00B54CBD /* RealmBarData.swift */, 5B88D8741C634EBB00B54CBD /* RealmBarDataSet.swift */, 5B88D8751C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift */, 5B88D8761C634EBB00B54CBD /* RealmBaseDataSet.swift */, - 5B88D8771C634EBB00B54CBD /* RealmBubbleData.swift */, 5B88D8781C634EBB00B54CBD /* RealmBubbleDataSet.swift */, - 5B88D8791C634EBB00B54CBD /* RealmCandleData.swift */, 5B88D87A1C634EBB00B54CBD /* RealmCandleDataSet.swift */, - 5B88D87B1C634EBB00B54CBD /* RealmLineData.swift */, 5B88D87C1C634EBB00B54CBD /* RealmLineDataSet.swift */, 5B88D87D1C634EBB00B54CBD /* RealmLineRadarDataSet.swift */, 5B88D87E1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift */, - 5B88D87F1C634EBB00B54CBD /* RealmPieData.swift */, 5B88D8801C634EBB00B54CBD /* RealmPieDataSet.swift */, - 5B88D8811C634EBB00B54CBD /* RealmRadarData.swift */, 5B88D8821C634EBB00B54CBD /* RealmRadarDataSet.swift */, - 5B88D8831C634EBB00B54CBD /* RealmScatterData.swift */, 5B88D8841C634EBB00B54CBD /* RealmScatterDataSet.swift */, ); path = Data; @@ -501,20 +466,13 @@ 5BA899281CADB7F20012ED64 /* RealmLineDataSet.swift in Sources */, 5BA899291CADB7F20012ED64 /* RealmChartUtils.swift in Sources */, 5BA8992A1CADB7F20012ED64 /* RealmScatterDataSet.swift in Sources */, - 5BA8992B1CADB7F20012ED64 /* RealmBarData.swift in Sources */, 5BA8992C1CADB7F20012ED64 /* RealmBarDataSet.swift in Sources */, 5BA8992D1CADB7F20012ED64 /* RealmBaseDataSet.swift in Sources */, 5BA8992E1CADB7F20012ED64 /* RealmCandleDataSet.swift in Sources */, - 5BA8992F1CADB7F20012ED64 /* RealmCandleData.swift in Sources */, - 5BA899301CADB7F20012ED64 /* RealmBubbleData.swift in Sources */, 5BA899311CADB7F20012ED64 /* RealmBarLineScatterCandleBubbleDataSet.swift in Sources */, - 5BA899321CADB7F20012ED64 /* RealmScatterData.swift in Sources */, 5BA899331CADB7F20012ED64 /* RealmBubbleDataSet.swift in Sources */, 5BA899341CADB7F20012ED64 /* RealmLineScatterCandleRadarDataSet.swift in Sources */, 5BA899351CADB7F20012ED64 /* RealmRadarDataSet.swift in Sources */, - 5BA899361CADB7F20012ED64 /* RealmLineData.swift in Sources */, - 5BA899371CADB7F20012ED64 /* RealmPieData.swift in Sources */, - 5BA899381CADB7F20012ED64 /* RealmRadarData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -527,20 +485,13 @@ 5B88D8971C634EBB00B54CBD /* RealmLineDataSet.swift in Sources */, 5B4AC1821C4C12B80028D1A6 /* RealmChartUtils.swift in Sources */, 5B88D8A71C634EBB00B54CBD /* RealmScatterDataSet.swift in Sources */, - 5B88D8851C634EBB00B54CBD /* RealmBarData.swift in Sources */, 5B88D8871C634EBB00B54CBD /* RealmBarDataSet.swift in Sources */, 5B88D88B1C634EBB00B54CBD /* RealmBaseDataSet.swift in Sources */, 5B88D8931C634EBB00B54CBD /* RealmCandleDataSet.swift in Sources */, - 5B88D8911C634EBB00B54CBD /* RealmCandleData.swift in Sources */, - 5B88D88D1C634EBB00B54CBD /* RealmBubbleData.swift in Sources */, 5B88D8891C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift in Sources */, - 5B88D8A51C634EBB00B54CBD /* RealmScatterData.swift in Sources */, 5B88D88F1C634EBB00B54CBD /* RealmBubbleDataSet.swift in Sources */, 5B88D89B1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift in Sources */, 5B88D8A31C634EBB00B54CBD /* RealmRadarDataSet.swift in Sources */, - 5B88D8951C634EBB00B54CBD /* RealmLineData.swift in Sources */, - 5B88D89D1C634EBB00B54CBD /* RealmPieData.swift in Sources */, - 5B88D8A11C634EBB00B54CBD /* RealmRadarData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -553,20 +504,13 @@ 5B88D8981C634EBB00B54CBD /* RealmLineDataSet.swift in Sources */, 5B4AC1831C4C12B80028D1A6 /* RealmChartUtils.swift in Sources */, 5B88D8A81C634EBB00B54CBD /* RealmScatterDataSet.swift in Sources */, - 5B88D8861C634EBB00B54CBD /* RealmBarData.swift in Sources */, 5B88D8881C634EBB00B54CBD /* RealmBarDataSet.swift in Sources */, 5B88D88C1C634EBB00B54CBD /* RealmBaseDataSet.swift in Sources */, 5B88D8941C634EBB00B54CBD /* RealmCandleDataSet.swift in Sources */, - 5B88D8921C634EBB00B54CBD /* RealmCandleData.swift in Sources */, - 5B88D88E1C634EBB00B54CBD /* RealmBubbleData.swift in Sources */, 5B88D88A1C634EBB00B54CBD /* RealmBarLineScatterCandleBubbleDataSet.swift in Sources */, - 5B88D8A61C634EBB00B54CBD /* RealmScatterData.swift in Sources */, 5B88D8901C634EBB00B54CBD /* RealmBubbleDataSet.swift in Sources */, 5B88D89C1C634EBB00B54CBD /* RealmLineScatterCandleRadarDataSet.swift in Sources */, 5B88D8A41C634EBB00B54CBD /* RealmRadarDataSet.swift in Sources */, - 5B88D8961C634EBB00B54CBD /* RealmLineData.swift in Sources */, - 5B88D89E1C634EBB00B54CBD /* RealmPieData.swift in Sources */, - 5B88D8A21C634EBB00B54CBD /* RealmRadarData.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ChartsRealm/Classes/Data/RealmBarData.swift b/ChartsRealm/Classes/Data/RealmBarData.swift deleted file mode 100644 index 60e0f97716..0000000000 --- a/ChartsRealm/Classes/Data/RealmBarData.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RealmBarData.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -import Charts -import Realm -import Realm.Dynamic - -public class RealmBarData: BarChartData -{ - public init(results: RLMResults?, xValueField: String, dataSets: [IChartDataSet]?) - { - if results == nil - { - super.init(xVals: [String](), dataSets: dataSets) - } - else - { - super.init(xVals: RealmChartUtils.toXVals(results: results!, xValueField: xValueField), dataSets: dataSets) - } - } -} \ No newline at end of file diff --git a/ChartsRealm/Classes/Data/RealmBarDataSet.swift b/ChartsRealm/Classes/Data/RealmBarDataSet.swift index a8dfb60452..8fb095c463 100644 --- a/ChartsRealm/Classes/Data/RealmBarDataSet.swift +++ b/ChartsRealm/Classes/Data/RealmBarDataSet.swift @@ -31,67 +31,64 @@ public class RealmBarDataSet: RealmBarLineScatterCandleBubbleDataSet, IBarChartD super.init() } - public override init(results: RLMResults?, yValueField: String, xIndexField: String?, label: String?) + public override init(results: RLMResults?, xValueField: String?, yValueField: String, label: String?) { - super.init(results: results, yValueField: yValueField, xIndexField: xIndexField, label: label) + super.init(results: results, xValueField: xValueField, yValueField: yValueField, label: label) } - public init(results: RLMResults?, yValueField: String, xIndexField: String?, stackValueField: String, label: String?) + public init(results: RLMResults?, xValueField: String?, yValueField: String, stackValueField: String, label: String?) { _stackValueField = stackValueField - super.init(results: results, yValueField: yValueField, xIndexField: xIndexField, label: label) + super.init(results: results, xValueField: xValueField, yValueField: yValueField, label: label) } - public convenience init(results: RLMResults?, yValueField: String, xIndexField: String?, stackValueField: String) + public convenience init(results: RLMResults?, xValueField: String?, yValueField: String, stackValueField: String) { - self.init(results: results, yValueField: yValueField, xIndexField: xIndexField, stackValueField: stackValueField, label: "DataSet") + self.init(results: results, xValueField: xValueField, yValueField: yValueField, stackValueField: stackValueField, label: "DataSet") } public convenience init(results: RLMResults?, yValueField: String, stackValueField: String, label: String) { - self.init(results: results, yValueField: yValueField, xIndexField: nil, stackValueField: stackValueField, label: label) + self.init(results: results, xValueField: nil, yValueField: yValueField, stackValueField: stackValueField, label: label) } public convenience init(results: RLMResults?, yValueField: String, stackValueField: String) { - self.init(results: results, yValueField: yValueField, xIndexField: nil, stackValueField: stackValueField) + self.init(results: results, xValueField: nil, yValueField: yValueField, stackValueField: stackValueField) } - public override init(realm: RLMRealm?, modelName: String, resultsWhere: String, yValueField: String, xIndexField: String?, label: String?) + public override init(realm: RLMRealm?, modelName: String, resultsWhere: String, xValueField: String?, yValueField: String, label: String?) { - super.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, yValueField: yValueField, xIndexField: xIndexField, label: label) + super.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, xValueField: xValueField, yValueField: yValueField, label: label) } - public init(realm: RLMRealm?, modelName: String, resultsWhere: String, yValueField: String, xIndexField: String?, stackValueField: String, label: String?) + public init(realm: RLMRealm?, modelName: String, resultsWhere: String, xValueField: String?, yValueField: String, stackValueField: String, label: String?) { _stackValueField = stackValueField - super.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, yValueField: yValueField, xIndexField: xIndexField, label: label) + super.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, xValueField: xValueField, yValueField: yValueField, label: label) } - public convenience init(realm: RLMRealm?, modelName: String, resultsWhere: String, yValueField: String, xIndexField: String?, stackValueField: String) + public convenience init(realm: RLMRealm?, modelName: String, resultsWhere: String, xValueField: String?, yValueField: String, stackValueField: String) { - self.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, yValueField: yValueField, xIndexField: nil, stackValueField: stackValueField) + self.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, xValueField: nil, yValueField: yValueField, stackValueField: stackValueField) } public convenience init(realm: RLMRealm?, modelName: String, resultsWhere: String, yValueField: String, stackValueField: String, label: String?) { - self.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, yValueField: yValueField, xIndexField: nil, stackValueField: stackValueField, label: label) + self.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, xValueField: nil, yValueField: yValueField, stackValueField: stackValueField, label: label) } public convenience init(realm: RLMRealm?, modelName: String, resultsWhere: String, yValueField: String, stackValueField: String) { - self.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, yValueField: yValueField, xIndexField: nil, stackValueField: stackValueField, label: nil) + self.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, xValueField: nil, yValueField: yValueField, stackValueField: stackValueField, label: nil) } public override func notifyDataSetChanged() { - _cache.removeAll() - ensureCache(start: 0, end: entryCount - 1) - self.calcStackSize(_cache as! [BarChartDataEntry]) - super.notifyDataSetChanged() + self.calcStackSize(_cache as! [BarChartDataEntry]) } // MARK: - Data functions and accessors @@ -102,7 +99,7 @@ public class RealmBarDataSet: RealmBarLineScatterCandleBubbleDataSet, IBarChartD /// is calculated from the Entries that are added to the DataSet private var _stackSize = 1 - internal override func buildEntryFromResultObject(object: RLMObject, atIndex: UInt) -> ChartDataEntry + internal override func buildEntryFromResultObject(object: RLMObject, x: Double) -> ChartDataEntry { let value = object[_yValueField!] let entry: BarChartDataEntry @@ -114,11 +111,11 @@ public class RealmBarDataSet: RealmBarLineScatterCandleBubbleDataSet, IBarChartD { values.append((val as! RLMObject)[_stackValueField!] as! Double) } - entry = BarChartDataEntry(values: values, xIndex: _xIndexField == nil ? Int(atIndex) : object[_xIndexField!] as! Int) + entry = BarChartDataEntry(x: _xValueField == nil ? x : object[_xValueField!] as! Double, yValues: values) } else { - entry = BarChartDataEntry(value: value as! Double, xIndex: _xIndexField == nil ? Int(atIndex) : object[_xIndexField!] as! Int) + entry = BarChartDataEntry(x: _xValueField == nil ? x : object[_xValueField!] as! Double, y: value as! Double) } return entry @@ -129,7 +126,7 @@ public class RealmBarDataSet: RealmBarLineScatterCandleBubbleDataSet, IBarChartD { for i in 0 ..< yVals.count { - if let vals = yVals[i].values + if let vals = yVals[i].yValues { if vals.count > _stackSize { @@ -139,78 +136,57 @@ public class RealmBarDataSet: RealmBarLineScatterCandleBubbleDataSet, IBarChartD } } - public override func calcMinMax(start start : Int, end: Int) + public override func calcMinMax() { - let yValCount = self.entryCount - - if yValCount == 0 - { - return - } - - var endValue : Int - - if end == 0 || end >= yValCount - { - endValue = yValCount - 1 - } - else - { - endValue = end - } - - ensureCache(start: start, end: endValue) - if _cache.count == 0 { return } - _lastStart = start - _lastEnd = endValue - - _yMin = DBL_MAX _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - for i in start.stride(through: endValue, by: 1) + for e in _cache as! [BarChartDataEntry] { - if let e = _cache[i - _cacheFirst] as? BarChartDataEntry + if !e.y.isNaN { - if !e.value.isNaN + if e.yValues == nil + { + if e.y < _yMin + { + _yMin = e.y + } + + if e.y > _yMax + { + _yMax = e.y + } + } + else { - if e.values == nil + if -e.negativeSum < _yMin { - if e.value < _yMin - { - _yMin = e.value - } - - if e.value > _yMax - { - _yMax = e.value - } + _yMin = -e.negativeSum } - else + + if e.positiveSum > _yMax { - if -e.negativeSum < _yMin - { - _yMin = -e.negativeSum - } - - if e.positiveSum > _yMax - { - _yMax = e.positiveSum - } + _yMax = e.positiveSum } } + + if e.x < _xMin + { + _xMin = e.x + } + if e.x > _xMax + { + _xMax = e.x + } } } - - if (_yMin == DBL_MAX) - { - _yMin = 0.0 - _yMax = 0.0 - } } /// - returns: the maximum number of bars that can be stacked upon another in this DataSet. @@ -230,9 +206,6 @@ public class RealmBarDataSet: RealmBarLineScatterCandleBubbleDataSet, IBarChartD // MARK: - Styling functions and accessors - /// space indicator between the bars in percentage of the whole width of one value (0.15 == 15% of bar width) - public var barSpace: CGFloat = 0.15 - /// the color used for drawing the bar-shadows. The bar shadows is a surface behind the bar that indicates the maximum value public var barShadowColor = NSUIColor(red: 215.0/255.0, green: 215.0/255.0, blue: 215.0/255.0, alpha: 1.0) @@ -252,7 +225,6 @@ public class RealmBarDataSet: RealmBarLineScatterCandleBubbleDataSet, IBarChartD let copy = super.copyWithZone(zone) as! RealmBarDataSet copy._stackSize = _stackSize copy.stackLabels = stackLabels - copy.barSpace = barSpace copy.barShadowColor = barShadowColor copy.highlightAlpha = highlightAlpha return copy diff --git a/ChartsRealm/Classes/Data/RealmBaseDataSet.swift b/ChartsRealm/Classes/Data/RealmBaseDataSet.swift index 2357392365..6bb46b908d 100644 --- a/ChartsRealm/Classes/Data/RealmBaseDataSet.swift +++ b/ChartsRealm/Classes/Data/RealmBaseDataSet.swift @@ -47,7 +47,7 @@ public class RealmBaseDataSet: ChartBaseDataSet initialize() } - public init(results: RLMResults?, yValueField: String, xIndexField: String?, label: String?) + public init(results: RLMResults?, xValueField: String?, yValueField: String, label: String?) { super.init() @@ -58,8 +58,8 @@ public class RealmBaseDataSet: ChartBaseDataSet _results = results _yValueField = yValueField - _xIndexField = xIndexField - _results = _results?.sortedResultsUsingProperty(_xIndexField!, ascending: true) + _xValueField = xValueField + _results = _results?.sortedResultsUsingProperty(_xValueField!, ascending: true) notifyDataSetChanged() @@ -68,12 +68,12 @@ public class RealmBaseDataSet: ChartBaseDataSet public convenience init(results: RLMResults?, yValueField: String, label: String?) { - self.init(results: results, yValueField: yValueField, xIndexField: nil, label: label) + self.init(results: results, xValueField: nil, yValueField: yValueField, label: label) } - public convenience init(results: RLMResults?, yValueField: String, xIndexField: String?) + public convenience init(results: RLMResults?, xValueField: String?, yValueField: String) { - self.init(results: results, yValueField: yValueField, xIndexField: xIndexField, label: "DataSet") + self.init(results: results, xValueField: xValueField, yValueField: yValueField, label: "DataSet") } public convenience init(results: RLMResults?, yValueField: String) @@ -81,7 +81,7 @@ public class RealmBaseDataSet: ChartBaseDataSet self.init(results: results, yValueField: yValueField) } - public init(realm: RLMRealm?, modelName: String, resultsWhere: String, yValueField: String, xIndexField: String?, label: String?) + public init(realm: RLMRealm?, modelName: String, resultsWhere: String, xValueField: String?, yValueField: String, label: String?) { super.init() @@ -91,7 +91,7 @@ public class RealmBaseDataSet: ChartBaseDataSet self.label = label _yValueField = yValueField - _xIndexField = xIndexField + _xValueField = xValueField if realm != nil { @@ -103,7 +103,7 @@ public class RealmBaseDataSet: ChartBaseDataSet public convenience init(realm: RLMRealm?, modelName: String, resultsWhere: String, yValueField: String, label: String?) { - self.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, yValueField: yValueField, xIndexField: nil, label: label) + self.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, xValueField: nil, yValueField: yValueField, label: label) } public func loadResults(realm realm: RLMRealm, modelName: String) @@ -122,9 +122,9 @@ public class RealmBaseDataSet: ChartBaseDataSet _results = realm.objects(modelName, withPredicate: predicate) } - if _xIndexField != nil + if _xValueField != nil { - _results = _results?.sortedResultsUsingProperty(_xIndexField!, ascending: true) + _results = _results?.sortedResultsUsingProperty(_xValueField!, ascending: true) } notifyDataSetChanged() @@ -134,73 +134,34 @@ public class RealmBaseDataSet: ChartBaseDataSet internal var _results: RLMResults? internal var _yValueField: String? - internal var _xIndexField: String? + internal var _xValueField: String? internal var _cache = [ChartDataEntry]() - internal var _cacheFirst: Int = -1 - internal var _cacheLast: Int = -1 - internal var _yMax = Double(0.0) - internal var _yMin = Double(0.0) + internal var _yMax: Double = -DBL_MAX + internal var _yMin: Double = DBL_MAX - /// the last start value used for calcMinMax - internal var _lastStart: Int = 0 - - /// the last end value used for calcMinMax - internal var _lastEnd: Int = 0 + internal var _xMax: Double = -DBL_MAX + internal var _xMin: Double = DBL_MAX /// Makes sure that the cache is populated for the specified range - internal func ensureCache(start start: Int, end: Int) + internal func buildCache() { - if start <= _cacheLast && end >= _cacheFirst - { - return - } - guard let results = _results else { return } - if _cacheFirst == -1 || _cacheLast == -1 - { - _cache.removeAll() - _cache.reserveCapacity(end - start + 1) - - for i in UInt(start) ..< UInt(end + 1) - { - _cache.append(buildEntryFromResultObject(results.objectAtIndex(i), atIndex: i)) - } - - _cacheFirst = start - _cacheLast = end - } - - if start < _cacheFirst - { - var newEntries = [ChartDataEntry]() - newEntries.reserveCapacity(start - _cacheFirst) - - for i in UInt(start) ..< UInt(_cacheFirst) - { - newEntries.append(buildEntryFromResultObject(results.objectAtIndex(i), atIndex: i)) - } - - _cache.insertContentsOf(newEntries, at: 0) - - _cacheFirst = start - } + _cache.removeAll() + _cache.reserveCapacity(Int(results.count)) - if end > _cacheLast + var xValue: Double = 0.0 + for e in results { - for i in UInt(_cacheLast + 1) ..< UInt(end + 1) - { - _cache.append(buildEntryFromResultObject(results.objectAtIndex(i), atIndex: i)) - } - - _cacheLast = end + _cache.append(buildEntryFromResultObject(e as! RLMObject, x: xValue)) + xValue += 1.0 } } - internal func buildEntryFromResultObject(object: RLMObject, atIndex: UInt) -> ChartDataEntry + internal func buildEntryFromResultObject(object: RLMObject, x: Double) -> ChartDataEntry { - let entry = ChartDataEntry(value: object[_yValueField!] as! Double, xIndex: _xIndexField == nil ? Int(atIndex) : object[_xIndexField!] as! Int) + let entry = ChartDataEntry(x: _xValueField == nil ? x : object[_xValueField!] as! Double, y: object[_yValueField!] as! Double) return entry } @@ -209,123 +170,114 @@ public class RealmBaseDataSet: ChartBaseDataSet internal func clearCache() { _cache.removeAll() - _cacheFirst = -1 - _cacheLast = -1 } /// Use this method to tell the data set that the underlying data has changed public override func notifyDataSetChanged() { - calcMinMax(start: _lastStart, end: _lastEnd) + buildCache() + calcMinMax() } - public override func calcMinMax(start start: Int, end: Int) + public override func calcMinMax() { - let yValCount = self.entryCount - - if yValCount == 0 + if _cache.count == 0 { return } - var endValue : Int + _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - if end == 0 || end >= yValCount + for e in _cache { - endValue = yValCount - 1 + calcMinMax(entry: e) } - else + } + + /// Updates the min and max x and y value of this DataSet based on the given Entry. + /// + /// - parameter e: + internal func calcMinMax(entry e: ChartDataEntry) + { + if e.y < _yMin { - endValue = end + _yMin = e.y } - - ensureCache(start: start, end: endValue) - - if _cache.count == 0 + if e.y > _yMax { - return + _yMax = e.y } - - _lastStart = start - _lastEnd = endValue - - _yMin = DBL_MAX - _yMax = -DBL_MAX - - for i in start.stride(through: endValue, by: 1) + if e.x < _xMin { - let e = _cache[i - _cacheFirst] - - if (!e.value.isNaN) - { - if (e.value < _yMin) - { - _yMin = e.value - } - if (e.value > _yMax) - { - _yMax = e.value - } - } + _xMin = e.x } - - if (_yMin == DBL_MAX) + if e.x > _xMax { - _yMin = 0.0 - _yMax = 0.0 + _xMax = e.x } } - + /// - returns: the minimum y-value this DataSet holds public override var yMin: Double { return _yMin } /// - returns: the maximum y-value this DataSet holds public override var yMax: Double { return _yMax } + /// - returns: the minimum x-value this DataSet holds + public override var xMin: Double { return _xMin } + + /// - returns: the maximum x-value this DataSet holds + public override var xMax: Double { return _xMax } + /// - returns: the number of y-values this DataSet represents public override var entryCount: Int { return Int(_results?.count ?? 0) } - /// - returns: the value of the Entry object at the given xIndex. Returns NaN if no value is at the given x-index. - public override func yValForXIndex(x: Int) -> Double + /// - returns: the value of the Entry object at the given x-pos. Returns NaN if no value is at the given x-pos. + public override func yValueForXValue(x: Double) -> Double { - let e = self.entryForXIndex(x) + let e = self.entryForXPos(x) - if (e !== nil && e!.xIndex == x) { return e!.value } + if (e !== nil && e!.x == x) { return e!.y } else { return Double.NaN } } - /// - returns: all of the y values of the Entry objects at the given xIndex. Returns NaN if no value is at the given x-index. - public override func yValsForXIndex(x: Int) -> [Double] + /// - returns: all of the y values of the Entry objects at the given x-pos. Returns NaN if no value is at the given x-pos. + public override func yValuesForXValue(x: Double) -> [Double] { - let entries = self.entriesForXIndex(x) + let entries = self.entriesForXPos(x) var yVals = [Double]() for e in entries { - yVals.append(e.value) + yVals.append(e.y) } return yVals } - /// - returns: the entry object found at the given index (not x-index!) + /// - returns: the entry object found at the given index (not x-value!) /// - throws: out of bounds /// if `i` is out of bounds, it may throw an out-of-bounds exception public override func entryForIndex(i: Int) -> ChartDataEntry? { - if i < _lastStart || i > _lastEnd + if _cache.count == 0 { - ensureCache(start: i, end: i) + buildCache() } - return _cache[i - _lastStart] + return _cache[i] } - /// - returns: the first Entry object found at the given xIndex with binary search. - /// If the no Entry at the specifed x-index is found, this method returns the Entry at the closest x-index. - /// nil if no Entry object at that index. - public override func entryForXIndex(x: Int, rounding: ChartDataSetRounding) -> ChartDataEntry? + /// - returns: the first Entry object found at the given x-pos with binary search. + /// If the no Entry at the specifed x-pos is found, this method returns the Entry at the closest x-pox. + /// nil if no Entry object at that x-pos. + /// - parameter x: the x-pos + /// - parameter rounding: determine whether to round up/down/closest if there is no Entry matching the provided x-pos + public override func entryForXPos(x: Double, rounding: ChartDataSetRounding) -> ChartDataEntry? { - let index = self.entryIndex(xIndex: x, rounding: rounding) + let index = self.entryIndex(x: x, rounding: rounding) if (index > -1) { return entryForIndex(index) @@ -333,38 +285,31 @@ public class RealmBaseDataSet: ChartBaseDataSet return nil } - /// - returns: the first Entry object found at the given xIndex with binary search. - /// If the no Entry at the specifed x-index is found, this method returns the Entry at the closest x-index. - /// nil if no Entry object at that index. - public override func entryForXIndex(x: Int) -> ChartDataEntry? + /// - returns: the first Entry object found at the given x-pos with binary search. + /// If the no Entry at the specifed x-pos is found, this method returns the Entry at the closest x-pos. + /// nil if no Entry object at that x-pos. + public override func entryForXPos(x: Double) -> ChartDataEntry? { - return entryForXIndex(x, rounding: .Closest) + return entryForXPos(x, rounding: .Closest) } - /// - returns: all Entry objects found at the given xIndex with binary search. - /// An empty array if no Entry object at that index. - public override func entriesForXIndex(x: Int) -> [ChartDataEntry] + /// - returns: all Entry objects found at the given x-pos with binary search. + /// An empty array if no Entry object at that x-pos. + public override func entriesForXPos(x: Double) -> [ChartDataEntry] { var entries = [ChartDataEntry]() guard let results = _results else { return entries } - if _xIndexField == nil - { - if results.count > UInt(x) - { - entries.append(buildEntryFromResultObject(results.objectAtIndex(UInt(x)), atIndex: UInt(x))) - } - } - else + if _xValueField != nil { let foundObjects = results.objectsWithPredicate( - NSPredicate(format: "%K == %d", _xIndexField!, x) + NSPredicate(format: "%K == %@", _xValueField!, x) ) for e in foundObjects { - entries.append(buildEntryFromResultObject(e as! RLMObject, atIndex: UInt(x))) + entries.append(buildEntryFromResultObject(e as! RLMObject, x: x)) } } @@ -373,13 +318,13 @@ public class RealmBaseDataSet: ChartBaseDataSet /// - returns: the array-index of the specified entry /// - /// - parameter x: x-index of the entry to search for - public override func entryIndex(xIndex x: Int, rounding: ChartDataSetRounding) -> Int + /// - parameter x: x-pos of the entry to search for + public override func entryIndex(x x: Double, rounding: ChartDataSetRounding) -> Int { guard let results = _results else { return -1 } let foundIndex = results.indexOfObjectWithPredicate( - NSPredicate(format: "%K == %d", _xIndexField!, x) + NSPredicate(format: "%K == %@", _xValueField!, x) ) // TODO: Figure out a way to quickly find the closest index @@ -396,7 +341,7 @@ public class RealmBaseDataSet: ChartBaseDataSet { if (_cache[i] === e || _cache[i].isEqual(e)) { - return _cacheFirst + i + return i } } @@ -445,12 +390,12 @@ public class RealmBaseDataSet: ChartBaseDataSet } } - /// Returns the fieldname that represents the "x-index" in the realm-data. - public var xIndexField: String? + /// Returns the fieldname that represents the "x-values" in the realm-data. + public var xValueField: String? { get { - return _xIndexField + return _xValueField } } @@ -462,11 +407,11 @@ public class RealmBaseDataSet: ChartBaseDataSet copy._results = _results copy._yValueField = _yValueField - copy._xIndexField = _xIndexField + copy._xValueField = _xValueField copy._yMax = _yMax copy._yMin = _yMin - copy._lastStart = _lastStart - copy._lastEnd = _lastEnd + copy._xMax = _xMax + copy._xMin = _xMin return copy } diff --git a/ChartsRealm/Classes/Data/RealmBubbleData.swift b/ChartsRealm/Classes/Data/RealmBubbleData.swift deleted file mode 100644 index 703f5f656f..0000000000 --- a/ChartsRealm/Classes/Data/RealmBubbleData.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RealmBubbleData.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -import Charts -import Realm -import Realm.Dynamic - -public class RealmBubbleData: BubbleChartData -{ - public init(results: RLMResults?, xValueField: String, dataSets: [IChartDataSet]?) - { - if results == nil - { - super.init(xVals: [String](), dataSets: dataSets) - } - else - { - super.init(xVals: RealmChartUtils.toXVals(results: results!, xValueField: xValueField), dataSets: dataSets) - } - } -} \ No newline at end of file diff --git a/ChartsRealm/Classes/Data/RealmBubbleDataSet.swift b/ChartsRealm/Classes/Data/RealmBubbleDataSet.swift index 6bde4a72e9..b591b572d4 100644 --- a/ChartsRealm/Classes/Data/RealmBubbleDataSet.swift +++ b/ChartsRealm/Classes/Data/RealmBubbleDataSet.swift @@ -30,143 +30,67 @@ public class RealmBubbleDataSet: RealmBarLineScatterCandleBubbleDataSet, IBubble super.init() } - public init(results: RLMResults?, yValueField: String, xIndexField: String, sizeField: String, label: String?) + public init(results: RLMResults?, xValueField: String, yValueField: String, sizeField: String, label: String?) { _sizeField = sizeField - super.init(results: results, yValueField: yValueField, xIndexField: xIndexField, label: label) + super.init(results: results, xValueField: xValueField, yValueField: yValueField, label: label) } - public convenience init(results: RLMResults?, yValueField: String, xIndexField: String, sizeField: String) + public convenience init(results: RLMResults?, xValueField: String, yValueField: String, sizeField: String) { - self.init(results: results, yValueField: yValueField, xIndexField: xIndexField, sizeField: sizeField, label: "DataSet") + self.init(results: results, xValueField: xValueField, yValueField: yValueField, sizeField: sizeField, label: "DataSet") } - public init(realm: RLMRealm?, modelName: String, resultsWhere: String, yValueField: String, xIndexField: String, sizeField: String, label: String?) + public init(realm: RLMRealm?, modelName: String, resultsWhere: String, xValueField: String, yValueField: String, sizeField: String, label: String?) { _sizeField = sizeField - super.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, yValueField: yValueField, xIndexField: xIndexField, label: label) + super.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, xValueField: xValueField, yValueField: yValueField, label: label) } // MARK: - Data functions and accessors internal var _sizeField: String? - internal var _xMax = Double(0.0) - internal var _xMin = Double(0.0) internal var _maxSize = CGFloat(0.0) - public var xMin: Double { return _xMin } - public var xMax: Double { return _xMax } public var maxSize: CGFloat { return _maxSize } public var normalizeSizeEnabled: Bool = true public var isNormalizeSizeEnabled: Bool { return normalizeSizeEnabled } - internal override func buildEntryFromResultObject(object: RLMObject, atIndex: UInt) -> ChartDataEntry + internal override func buildEntryFromResultObject(object: RLMObject, x: Double) -> ChartDataEntry { - let entry = BubbleChartDataEntry(xIndex: _xIndexField == nil ? Int(atIndex) : object[_xIndexField!] as! Int, value: object[_yValueField!] as! Double, size: object[_sizeField!] as! CGFloat) + let entry = BubbleChartDataEntry(x: _xValueField == nil ? x : object[_xValueField!] as! Double, y: object[_yValueField!] as! Double, size: object[_sizeField!] as! CGFloat) return entry } - public override func calcMinMax(start start: Int, end: Int) + public override func calcMinMax() { - let yValCount = self.entryCount - - if yValCount == 0 - { - return - } - - var endValue : Int - - if end == 0 || end >= yValCount - { - endValue = yValCount - 1 - } - else - { - endValue = end - } - - ensureCache(start: start, end: endValue) - if _cache.count == 0 { return } - _lastStart = start - _lastEnd = end - - _yMin = yMin(_cache[start - _cacheFirst] as! BubbleChartDataEntry) - _yMax = yMax(_cache[start - _cacheFirst] as! BubbleChartDataEntry) + _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - for i in start.stride(through: endValue, by: 1) + for e in _cache as! [BubbleChartDataEntry] { - let entry = _cache[i - _cacheFirst] as! BubbleChartDataEntry - - let ymin = yMin(entry) - let ymax = yMax(entry) - - if (ymin < _yMin) - { - _yMin = ymin - } - - if (ymax > _yMax) - { - _yMax = ymax - } - - let xmin = xMin(entry) - let xmax = xMax(entry) + calcMinMax(entry: e) - if (xmin < _xMin) - { - _xMin = xmin - } - - if (xmax > _xMax) - { - _xMax = xmax - } - - let size = largestSize(entry) + let size = e.size - if (size > _maxSize) + if size > _maxSize { _maxSize = size } } } - private func yMin(entry: BubbleChartDataEntry) -> Double - { - return entry.value - } - - private func yMax(entry: BubbleChartDataEntry) -> Double - { - return entry.value - } - - private func xMin(entry: BubbleChartDataEntry) -> Double - { - return Double(entry.xIndex) - } - - private func xMax(entry: BubbleChartDataEntry) -> Double - { - return Double(entry.xIndex) - } - - private func largestSize(entry: BubbleChartDataEntry) -> CGFloat - { - return entry.size - } - // MARK: - Styling functions and accessors /// Sets/gets the width of the circle that surrounds the bubble when highlighted diff --git a/ChartsRealm/Classes/Data/RealmCandleData.swift b/ChartsRealm/Classes/Data/RealmCandleData.swift deleted file mode 100644 index 4a3190a5b5..0000000000 --- a/ChartsRealm/Classes/Data/RealmCandleData.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RealmCandleData.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -import Charts -import Realm -import Realm.Dynamic - -public class RealmCandleData: CandleChartData -{ - public init(results: RLMResults?, xValueField: String, dataSets: [IChartDataSet]?) - { - if results == nil - { - super.init(xVals: [String](), dataSets: dataSets) - } - else - { - super.init(xVals: RealmChartUtils.toXVals(results: results!, xValueField: xValueField), dataSets: dataSets) - } - } -} \ No newline at end of file diff --git a/ChartsRealm/Classes/Data/RealmCandleDataSet.swift b/ChartsRealm/Classes/Data/RealmCandleDataSet.swift index aa9ed13361..80709ab994 100644 --- a/ChartsRealm/Classes/Data/RealmCandleDataSet.swift +++ b/ChartsRealm/Classes/Data/RealmCandleDataSet.swift @@ -31,29 +31,29 @@ public class RealmCandleDataSet: RealmLineScatterCandleRadarDataSet, ICandleChar super.init() } - public init(results: RLMResults?, highField: String, lowField: String, openField: String, closeField: String, xIndexField: String, label: String?) + public init(results: RLMResults?, xValueField: String, highField: String, lowField: String, openField: String, closeField: String, label: String?) { _highField = highField _lowField = lowField _openField = openField _closeField = closeField - super.init(results: results, yValueField: "", xIndexField: xIndexField, label: label) + super.init(results: results, xValueField: xValueField, yValueField: "", label: label) } - public convenience init(results: RLMResults?, highField: String, lowField: String, openField: String, closeField: String, xIndexField: String) + public convenience init(results: RLMResults?, xValueField: String, highField: String, lowField: String, openField: String, closeField: String) { - self.init(results: results, highField: highField, lowField: lowField, openField: openField, closeField: closeField, xIndexField: xIndexField, label: "DataSet") + self.init(results: results, xValueField: xValueField, highField: highField, lowField: lowField, openField: openField, closeField: closeField, label: "DataSet") } - public init(realm: RLMRealm?, modelName: String, resultsWhere: String, highField: String, lowField: String, openField: String, closeField: String, xIndexField: String, label: String?) + public init(realm: RLMRealm?, modelName: String, resultsWhere: String, xValueField: String, highField: String, lowField: String, openField: String, closeField: String, label: String?) { _highField = highField _lowField = lowField _openField = openField _closeField = closeField - super.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, yValueField: "", xIndexField: xIndexField, label: label) + super.init(realm: realm, modelName: modelName, resultsWhere: resultsWhere, xValueField: xValueField, yValueField: "", label: label) } // MARK: - Data functions and accessors @@ -63,10 +63,10 @@ public class RealmCandleDataSet: RealmLineScatterCandleRadarDataSet, ICandleChar internal var _openField: String? internal var _closeField: String? - internal override func buildEntryFromResultObject(object: RLMObject, atIndex: UInt) -> ChartDataEntry + internal override func buildEntryFromResultObject(object: RLMObject, x: Double) -> ChartDataEntry { let entry = CandleChartDataEntry( - xIndex: _xIndexField == nil ? Int(atIndex) : object[_xIndexField!] as! Int, + x: _xValueField == nil ? x : object[_xValueField!] as! Double, shadowH: object[_highField!] as! Double, shadowL: object[_lowField!] as! Double, open: object[_openField!] as! Double, @@ -75,43 +75,20 @@ public class RealmCandleDataSet: RealmLineScatterCandleRadarDataSet, ICandleChar return entry } - public override func calcMinMax(start start: Int, end: Int) + public override func calcMinMax() { - let yValCount = self.entryCount - - if yValCount == 0 - { - return - } - - var endValue : Int - - if end == 0 || end >= yValCount - { - endValue = yValCount - 1 - } - else - { - endValue = end - } - - ensureCache(start: start, end: endValue) - if _cache.count == 0 { return } - _lastStart = start - _lastEnd = end - - _yMin = DBL_MAX _yMax = -DBL_MAX + _yMin = DBL_MAX + _xMax = -DBL_MAX + _xMin = DBL_MAX - for i in start.stride(through: endValue, by: 1) + for e in _cache as! [CandleChartDataEntry] { - let e = _cache[i - _cacheFirst] as! CandleChartDataEntry - if (e.low < _yMin) { _yMin = e.low @@ -121,6 +98,15 @@ public class RealmCandleDataSet: RealmLineScatterCandleRadarDataSet, ICandleChar { _yMax = e.high } + + if e.x < _xMin + { + _xMin = e.x + } + if e.x > _xMax + { + _xMax = e.x + } } } diff --git a/ChartsRealm/Classes/Data/RealmLineData.swift b/ChartsRealm/Classes/Data/RealmLineData.swift deleted file mode 100644 index 4e36ee096e..0000000000 --- a/ChartsRealm/Classes/Data/RealmLineData.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RealmLineData.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -import Charts -import Realm -import Realm.Dynamic - -public class RealmLineData: LineChartData -{ - public init(results: RLMResults?, xValueField: String, dataSets: [IChartDataSet]?) - { - if results == nil - { - super.init(xVals: [String](), dataSets: dataSets) - } - else - { - super.init(xVals: RealmChartUtils.toXVals(results: results!, xValueField: xValueField), dataSets: dataSets) - } - } -} \ No newline at end of file diff --git a/ChartsRealm/Classes/Data/RealmPieData.swift b/ChartsRealm/Classes/Data/RealmPieData.swift deleted file mode 100644 index 2cfb40dbd0..0000000000 --- a/ChartsRealm/Classes/Data/RealmPieData.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RealmPieData.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -import Charts -import Realm -import Realm.Dynamic - -public class RealmPieData: PieChartData -{ - public init(results: RLMResults?, xValueField: String, dataSets: [IChartDataSet]?) - { - if results == nil - { - super.init(xVals: [String](), dataSets: dataSets) - } - else - { - super.init(xVals: RealmChartUtils.toXVals(results: results!, xValueField: xValueField), dataSets: dataSets) - } - } -} \ No newline at end of file diff --git a/ChartsRealm/Classes/Data/RealmPieDataSet.swift b/ChartsRealm/Classes/Data/RealmPieDataSet.swift index 5a359c21fe..dd6269d51a 100644 --- a/ChartsRealm/Classes/Data/RealmPieDataSet.swift +++ b/ChartsRealm/Classes/Data/RealmPieDataSet.swift @@ -27,6 +27,34 @@ public class RealmPieDataSet: RealmBaseDataSet, IPieChartDataSet self.valueFont = NSUIFont.systemFontOfSize(13.0) } + public required init() + { + super.init() + } + + public init(results: RLMResults?, yValueField: String, labelField: String?) + { + _labelField = labelField + + super.init(results: results, xValueField: nil, yValueField: yValueField, label: nil) + } + + // MARK: - Data functions and accessors + + internal var _labelField: String? + + internal override func buildEntryFromResultObject(object: RLMObject, x: Double) -> ChartDataEntry + { + if _labelField == nil + { + return PieChartDataEntry(value: object[_yValueField!] as! Double); + } + else + { + return PieChartDataEntry(value: object[_yValueField!] as! Double, label: object[_labelField!] as? String); + } + } + // MARK: - Styling functions and accessors private var _sliceSpace = CGFloat(0.0) diff --git a/ChartsRealm/Classes/Data/RealmRadarData.swift b/ChartsRealm/Classes/Data/RealmRadarData.swift deleted file mode 100644 index 7a0a352fbd..0000000000 --- a/ChartsRealm/Classes/Data/RealmRadarData.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RealmRadarData.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation -import Charts - -import Realm -import Realm.Dynamic - -public class RealmRadarData: RadarChartData -{ - public init(results: RLMResults?, xValueField: String, dataSets: [IChartDataSet]?) - { - if results == nil - { - super.init(xVals: [String](), dataSets: dataSets) - } - else - { - super.init(xVals: RealmChartUtils.toXVals(results: results!, xValueField: xValueField), dataSets: dataSets) - } - } -} \ No newline at end of file diff --git a/ChartsRealm/Classes/Data/RealmScatterData.swift b/ChartsRealm/Classes/Data/RealmScatterData.swift deleted file mode 100644 index 6a0f5b5d82..0000000000 --- a/ChartsRealm/Classes/Data/RealmScatterData.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// RealmScatterData.swift -// Charts -// -// Created by Daniel Cohen Gindi on 23/2/15. - -// -// Copyright 2015 Daniel Cohen Gindi & Philipp Jahoda -// A port of MPAndroidChart for iOS -// Licensed under Apache License 2.0 -// -// https://github.com/danielgindi/Charts -// - -import Foundation - -import Charts -import Realm -import Realm.Dynamic - -public class RealmScatterData: ScatterChartData -{ - public init(results: RLMResults?, xValueField: String, dataSets: [IChartDataSet]?) - { - if results == nil - { - super.init(xVals: [String](), dataSets: dataSets) - } - else - { - super.init(xVals: RealmChartUtils.toXVals(results: results!, xValueField: xValueField), dataSets: dataSets) - } - } -} \ No newline at end of file diff --git a/ChartsRealm/Classes/Utils/RealmChartUtils.swift b/ChartsRealm/Classes/Utils/RealmChartUtils.swift index fea543abb5..d1b0c00052 100644 --- a/ChartsRealm/Classes/Utils/RealmChartUtils.swift +++ b/ChartsRealm/Classes/Utils/RealmChartUtils.swift @@ -14,28 +14,6 @@ import Foundation import Realm -public class RealmChartUtils: NSObject -{ - /// Transforms the given Realm-ResultSet into an xValue array, using the specified xValueField - public static func toXVals(results results: RLMResults, xValueField: String) -> [String] - { - let addedValues = NSMutableSet() - var xVals = [String]() - - for object in results - { - let xVal = (object as! RLMObject)[xValueField] as! String! - if !addedValues.containsObject(xVal) - { - addedValues.addObject(xVal) - xVals.append(xVal) - } - } - - return xVals - } -} - extension RLMResults: SequenceType { public func generate() -> NSFastGenerator