1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "chart/LineChartRenderer.h" 7 8 #include <stdio.h> 9 10 #include <Shape.h> 11 #include <View.h> 12 13 #include "chart/ChartDataSource.h" 14 15 16 // #pragma mark - DataSourceInfo 17 18 19 struct LineChartRenderer::DataSourceInfo { 20 public: 21 ChartDataSource* source; 22 double* samples; 23 int32 sampleCount; 24 bool samplesValid; 25 LineChartRendererDataSourceConfig config; 26 27 public: 28 DataSourceInfo(ChartDataSource* source, 29 LineChartRendererDataSourceConfig* config); 30 ~DataSourceInfo(); 31 32 bool UpdateSamples(const ChartDataRange& domain, 33 int32 sampleCount); 34 }; 35 36 37 LineChartRenderer::DataSourceInfo::DataSourceInfo(ChartDataSource* source, 38 LineChartRendererDataSourceConfig* config) 39 : 40 source(source), 41 samples(NULL), 42 sampleCount(0), 43 samplesValid(false) 44 { 45 if (config != NULL) 46 this->config = *config; 47 } 48 49 50 LineChartRenderer::DataSourceInfo::~DataSourceInfo() 51 { 52 delete[] samples; 53 } 54 55 56 bool 57 LineChartRenderer::DataSourceInfo::UpdateSamples(const ChartDataRange& domain, 58 int32 sampleCount) 59 { 60 if (samplesValid) 61 return true; 62 63 // check the sample count -- we might need to realloc the sample array 64 if (sampleCount != this->sampleCount) { 65 delete[] samples; 66 this->sampleCount = 0; 67 68 samples = new(std::nothrow) double[sampleCount]; 69 if (samples == NULL) 70 return false; 71 72 this->sampleCount = sampleCount; 73 } 74 75 // get the new samples 76 source->GetSamples(domain.min, domain.max, samples, sampleCount); 77 78 samplesValid = true; 79 return true; 80 } 81 82 83 // #pragma mark - LineChartRenderer 84 85 86 LineChartRenderer::LineChartRenderer() 87 : 88 fFrame(), 89 fDomain(), 90 fRange() 91 { 92 } 93 94 95 LineChartRenderer::~LineChartRenderer() 96 { 97 } 98 99 100 bool 101 LineChartRenderer::AddDataSource(ChartDataSource* dataSource, int32 index, 102 ChartRendererDataSourceConfig* config) 103 { 104 DataSourceInfo* info = new(std::nothrow) DataSourceInfo(dataSource, 105 dynamic_cast<LineChartRendererDataSourceConfig*>(config)); 106 if (info == NULL) 107 return false; 108 109 if (!fDataSources.AddItem(info, index)) { 110 delete info; 111 return false; 112 } 113 114 return true; 115 } 116 117 118 void 119 LineChartRenderer::RemoveDataSource(ChartDataSource* dataSource) 120 { 121 for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) { 122 info->samplesValid = false; 123 if (info->source == dataSource) { 124 delete fDataSources.RemoveItemAt(i); 125 return; 126 } 127 } 128 } 129 130 131 void 132 LineChartRenderer::SetFrame(const BRect& frame) 133 { 134 fFrame = frame; 135 _InvalidateSamples(); 136 } 137 138 139 void 140 LineChartRenderer::SetDomain(const ChartDataRange& domain) 141 { 142 if (domain != fDomain) { 143 fDomain = domain; 144 _InvalidateSamples(); 145 } 146 } 147 148 149 void 150 LineChartRenderer::SetRange(const ChartDataRange& range) 151 { 152 if (range != fRange) { 153 fRange = range; 154 _InvalidateSamples(); 155 } 156 } 157 158 159 void 160 LineChartRenderer::Render(BView* view, BRect updateRect) 161 { 162 if (!updateRect.IsValid() || updateRect.left > fFrame.right 163 || fFrame.left > updateRect.right) { 164 return; 165 } 166 167 if (fDomain.min >= fDomain.max || fRange.min >= fRange.max) 168 return; 169 170 if (!_UpdateSamples()) 171 return; 172 173 // get the range to draw (draw one more sample on each side) 174 int32 left = (int32)fFrame.left; 175 int32 first = (int32)updateRect.left - left - 1; 176 int32 last = (int32)updateRect.right - left + 1; 177 if (first < 0) 178 first = 0; 179 if (last > fFrame.IntegerWidth()) 180 last = fFrame.IntegerWidth(); 181 if (first > last) 182 return; 183 184 double minRange = fRange.min; 185 double sampleRange = fRange.max - minRange; 186 if (sampleRange == 0) { 187 minRange = fRange.min - 0.5; 188 sampleRange = 1; 189 } 190 double scale = (double)fFrame.IntegerHeight() / sampleRange; 191 192 // draw 193 view->SetLineMode(B_ROUND_CAP, B_ROUND_JOIN); 194 for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) { 195 196 float bottom = fFrame.bottom; 197 BShape shape; 198 shape.MoveTo(BPoint(left + first, 199 bottom - ((info->samples[first] - minRange) * scale))); 200 201 for (int32 i = first; i <= last; i++) { 202 shape.LineTo(BPoint(float(left + i), 203 float(bottom - ((info->samples[i] - minRange) * scale)))); 204 } 205 view->SetHighColor(info->config.Color()); 206 view->MovePenTo(B_ORIGIN); 207 view->StrokeShape(&shape); 208 } 209 } 210 211 212 void 213 LineChartRenderer::_InvalidateSamples() 214 { 215 for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) 216 info->samplesValid = false; 217 } 218 219 220 bool 221 LineChartRenderer::_UpdateSamples() 222 { 223 int32 width = fFrame.IntegerWidth() + 1; 224 if (width <= 0) 225 return false; 226 227 for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) { 228 if (!info->UpdateSamples(fDomain, width)) 229 return false; 230 } 231 232 return true; 233 } 234 235 236 // #pragma mark - LineChartRendererDataSourceConfig 237 238 239 LineChartRendererDataSourceConfig::LineChartRendererDataSourceConfig() 240 { 241 fColor.red = 0; 242 fColor.green = 0; 243 fColor.blue = 0; 244 fColor.alpha = 255; 245 } 246 247 248 LineChartRendererDataSourceConfig::LineChartRendererDataSourceConfig( 249 const rgb_color& color) 250 { 251 fColor = color; 252 } 253 254 255 LineChartRendererDataSourceConfig::~LineChartRendererDataSourceConfig() 256 { 257 } 258 259 260 const rgb_color& 261 LineChartRendererDataSourceConfig::Color() const 262 { 263 return fColor; 264 } 265 266 267 void 268 LineChartRendererDataSourceConfig::SetColor(const rgb_color& color) 269 { 270 fColor = color; 271 } 272