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 19struct LineChartRenderer::DataSourceInfo { 20public: 21 ChartDataSource* source; 22 double* samples; 23 int32 sampleCount; 24 bool samplesValid; 25 LineChartRendererDataSourceConfig config; 26 27public: 28 DataSourceInfo(ChartDataSource* source, 29 LineChartRendererDataSourceConfig* config); 30 ~DataSourceInfo(); 31 32 bool UpdateSamples(const ChartDataRange& domain, 33 int32 sampleCount); 34}; 35 36 37LineChartRenderer::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 50LineChartRenderer::DataSourceInfo::~DataSourceInfo() 51{ 52 delete[] samples; 53} 54 55 56bool 57LineChartRenderer::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 86LineChartRenderer::LineChartRenderer() 87 : 88 fFrame(), 89 fDomain(), 90 fRange() 91{ 92} 93 94 95LineChartRenderer::~LineChartRenderer() 96{ 97} 98 99 100bool 101LineChartRenderer::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 118void 119LineChartRenderer::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 131void 132LineChartRenderer::SetFrame(const BRect& frame) 133{ 134 fFrame = frame; 135 _InvalidateSamples(); 136} 137 138 139void 140LineChartRenderer::SetDomain(const ChartDataRange& domain) 141{ 142 if (domain != fDomain) { 143 fDomain = domain; 144 _InvalidateSamples(); 145 } 146} 147 148 149void 150LineChartRenderer::SetRange(const ChartDataRange& range) 151{ 152 if (range != fRange) { 153 fRange = range; 154 _InvalidateSamples(); 155 } 156} 157 158 159void 160LineChartRenderer::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 212void 213LineChartRenderer::_InvalidateSamples() 214{ 215 for (int32 i = 0; DataSourceInfo* info = fDataSources.ItemAt(i); i++) 216 info->samplesValid = false; 217} 218 219 220bool 221LineChartRenderer::_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 239LineChartRendererDataSourceConfig::LineChartRendererDataSourceConfig() 240{ 241 fColor.red = 0; 242 fColor.green = 0; 243 fColor.blue = 0; 244 fColor.alpha = 255; 245} 246 247 248LineChartRendererDataSourceConfig::LineChartRendererDataSourceConfig( 249 const rgb_color& color) 250{ 251 fColor = color; 252} 253 254 255LineChartRendererDataSourceConfig::~LineChartRendererDataSourceConfig() 256{ 257} 258 259 260const rgb_color& 261LineChartRendererDataSourceConfig::Color() const 262{ 263 return fColor; 264} 265 266 267void 268LineChartRendererDataSourceConfig::SetColor(const rgb_color& color) 269{ 270 fColor = color; 271} 272