KSquare Utilities
Chart.cpp
Go to the documentation of this file.
1 /* Chart.cpp -- Used to build Charts from data-series.
2  * Copyright (C) 1994-2011 Kurt Kramer
3  * For conditions of distribution and use, see copyright notice in KKB.h
4  */
5 #include "FirstIncludes.h"
6 
7 #include <float.h>
8 #include <math.h>
9 #include <stdio.h>
10 
11 #include <string>
12 #include <iostream>
13 #include <fstream>
14 #include <vector>
15 #include <algorithm>
16 
17 #include "MemoryDebug.h"
18 
19 
20 using namespace std;
21 
22 
23 #include "Chart.h"
24 #include "ImageIO.h"
25 
26 
27 namespace KKB
28 {
30  {
31  public:
32  PlotPoint (double _xVal,
33  double _yVal
34  ):
35  xVal (_xVal),
36  yVal (_yVal)
37  {}
38 
39 
41  kkint32 _yVal
42  ):
43  xVal (double (_xVal)),
44  yVal (double (_yVal))
45  {}
46 
47 
48  double XVal () const {return xVal;}
49  double YVal () const {return yVal;}
50 
51 
52  private:
53  double xVal;
54  double yVal;
55  };
56 
57 
58 
59  class Chart::Series
60  {
61  public:
62  Series (KKStr _name,
63  PixelValue _color
64  );
65 
66 
67  void AddAValue (float _xVal,
68  float _yVal
69  );
70 
71  kkint32 Size () {return (kkint32)points.size ();}
72 
73 
74  private:
75  KKStr name;
76  PixelValue color;
77  vector<PlotPoint> points;
78 
79  friend class Chart;
80  };
81 
82 
83  typedef Chart::SeriesPtr SeriesPtr;
84 
86 
87 
88  class Chart::XLabel
89  {
90  public:
91  XLabel (double _xVal,
92  KKStr _name
93  ):
94  name (_name),
95  xVal (_xVal)
96  {}
97 
98  const
99  KKStr& Name () {return name;}
100 
101  double XVal () {return xVal;}
102 
103  bool operator< (const XLabel& right) const
104  {
105  return xVal < right.xVal;
106  }
107 
108  private:
109  KKStr name;
110  double xVal;
111  };
112 } /* KKB */
113 
114 
115 
116 using namespace KKB;
117 
118 
119 
121  PixelValue _color
122  ):
123  name (_name),
124  color (_color)
125 {
126 }
127 
128 
129 
130 void Chart::Series::AddAValue (float _xVal,
131  float _yVal
132  )
133 {
134  points.push_back (PlotPoint (_xVal, _yVal));
135 } /* AddAValue */
136 
137 
138 
139 
140 
141 
142 KKB::Chart::Chart (KKStr _title):
143  title (_title),
144  series (),
145  xLabels (),
146  minXValue (FloatMax),
147  maxXValue (FloatMin),
148  minYValue (FloatMax),
149  maxYValue (FloatMin),
150 
151  maxPointsPlotedInASeries (0),
152  numOfDefaultColorsUsed (0),
153 
154  chartWidth (0),
155  chartHeight (0),
156  xOffset (0),
157  yOffset (0),
158  yRange (0.0),
159  xRange (0.0),
160  yScale (0.0),
161  xScale (0.0),
162  xMin (0.0),
163  xMax (0.0),
164  yMin (0.0),
165  yMax (0.0),
166  yIncrement (0.0)
167 
168 {
169 }
170 
171 
173 {
174  for (kkuint32 x = 0; x < series.size (); x++)
175  {
176  delete series[x];
177  series[x] = NULL;
178  }
179 }
180 
181 
182 
183 
184 void KKB::Chart::AddAValue (kkuint32 _seriesIDX,
185  float _xVal,
186  float _yVal
187  )
188 {
189  if ((_seriesIDX < 0) || (_seriesIDX >= series.size ()))
190  {
191  cerr << std::endl
192  << "*** ERROR *** Chart::AddAValue" << std::endl
193  << " Invalid SeriesIDX[" << _seriesIDX << "]" << std::endl
194  << " Only [" << series.size () << "] series defined." << std::endl
195  << std::endl;
196  exit (-1);
197  }
198 
199  series[_seriesIDX]->AddAValue (_xVal, _yVal);
200 
201  if (series[_seriesIDX]->Size () > maxPointsPlotedInASeries)
202  maxPointsPlotedInASeries = series[_seriesIDX]->Size ();
203 
204 
205  if (_xVal < minXValue)
206  minXValue = _xVal;
207 
208  if (_xVal > maxXValue)
209  maxXValue = _xVal;
210 
211  if (_yVal < minYValue)
212  minYValue = _yVal;
213 
214  if (_yVal > maxYValue)
215  maxYValue = _yVal;
216 
217 } /* AddAValue */
218 
219 
220 
221 
222 static
224  PixelValue ( 0, 255, 0), // Green
225  PixelValue ( 0, 0, 255), // Blue
226  PixelValue ( 0, 0, 0), // Black
227  PixelValue (255, 255, 0), // red-Green
228  PixelValue (255, 0, 255), // red-Blue
229  PixelValue ( 0, 255, 255), // Green-Blue
230  PixelValue ( 70, 70, 70), // Gray
231  PixelValue (120, 0, 0), // Lite-Blue
232  PixelValue ( 0, 120, 0), // Lite-Green
233  PixelValue ( 0, 0, 120), // Lite-Blue
234  PixelValue (120, 120, 0), // Lite-Red-Green
235  PixelValue (120, 0, 120), // Lite-Red-Blue
236  PixelValue ( 0, 120, 120) // Lite-Green-Blue
237  };
238 
239 static
240 const
242 
243 
244 
245 void KKB::Chart::DefineASeries (KKStr _name)
246 {
247  series.push_back (new Series (_name, defaultColors[numOfDefaultColorsUsed % numOfDefinedDefaultColors]));
248  numOfDefaultColorsUsed++;
249 }
250 
251 
252 
253 void KKB::Chart::DefineASeries (KKStr _name,
254  PixelValue _color
255  )
256 {
257  series.push_back (new Series (_name, _color));
258 }
259 
260 
261 
262 
263 void KKB::Chart::DefineAXLabel (float _xVal,
264  KKStr _name
265  )
266 {
267  xLabels.push_back (XLabel (_xVal, _name));
268 }
269 
270 
271 
272 
273 
274 
275 Point Chart::RasterCoordinates (const PlotPoint& plotPoint)
276 {
277  kkint32 x = (kkint32)(((double)plotPoint.XVal () - xMin) * xScale + (double)xOffset + 0.5);
278  kkint32 y = (kkint32)(((double)plotPoint.YVal () - yMin) * yScale + (double)yOffset + 0.5);
279  return Point (y, x);
280 } /* ConvertToRasterCoordinates */
281 
282 
283 
284 
285 
286 
287 RasterPtr Chart::CreateRaster ()
288 {
289  PixelValue axisColor ( 0, 0, 0);
290  PixelValue scaleColor (190, 190, 190);
291  PixelValue zeroAxisColor (240, 132, 240);
292 
293  chartWidth = maxPointsPlotedInASeries * 10;
294 
295  if (chartWidth > 1000)
296  chartWidth = 1000;
297 
298  chartHeight = chartWidth;
299 
300  if (chartHeight > 500)
301  chartHeight = 500;
302 
303 
304  xOffset = 10;
305  yOffset = 10;
306 
307 
308  yRange = maxYValue - minYValue;
309  xRange = maxXValue - minXValue;
310 
311  yScale = (double)chartHeight / yRange;
312  xScale = (double)chartWidth / xRange;
313 
314  yMin = minYValue;
315  yMax = maxYValue;
316 
317  xMin = minXValue;
318  xMax = maxXValue;
319 
320 
321  {
322  // Lets refine the scaling
323  double sigTensPlace = floor (log10 (yRange));
324 
325  sigTensPlace = pow (10.0, sigTensPlace);
326 
327  yIncrement = floor (yRange / sigTensPlace) * sigTensPlace;
328 
329  kkint32 numOfYIncremets = (kkint32)((yRange / yIncrement) + 0.5);
330 
331  if (numOfYIncremets < 2)
332  {
333  yIncrement = yIncrement / 4.0;
334  }
335  else if (numOfYIncremets < 5)
336  {
337  yIncrement = yIncrement / 2.0;
338  }
339  else if (numOfYIncremets > 7)
340  {
341  yIncrement = yIncrement * 2.0;
342  }
343  else if (numOfYIncremets > 13)
344  {
345  yIncrement = yIncrement * 4.0;
346  }
347 
348  yMin = floor (minYValue / yIncrement) * yIncrement;
349  yMax = ceil (maxYValue / yIncrement) * yIncrement;
350 
351  yRange = yMax - yMin;
352  yScale = (double)chartHeight / yRange;
353  }
354 
355 
356  kkint32 height = chartHeight + 2 * yOffset;
357  kkint32 width = chartWidth + 2 * xOffset;
358 
359  RasterPtr chart = new Raster (height, width, true);
360 
361  {
362  // Draw Main Axis
363  chart->DrawLine (yOffset, xOffset, yOffset + chartHeight, xOffset, axisColor);
364  chart->DrawLine (yOffset, xOffset, yOffset , xOffset + chartWidth, axisColor);
365  }
366 
367 
368  {
369  // Draw y scales
370 
371  double y = yMin + yIncrement;
372 
373  while (y < yMax)
374  {
375  chart->DrawLine (RasterCoordinates (PlotPoint (0.0, (double)y)),
376  RasterCoordinates (PlotPoint (xMax, (double)y)),
377  scaleColor
378  );
379  y += yIncrement;
380  }
381  }
382 
383 
384  if ((yMin < 0.0) && (yMax > 0.0))
385  {
386  // Draw a line for y=0.0
387  chart->DrawLine (RasterCoordinates (PlotPoint (double ((kkint32)(xMin + 0.5)), 0.0)),
388  RasterCoordinates (PlotPoint (double ((kkint32)(xMax + 0.5)), 0.0)),
389  zeroAxisColor
390  );
391 
392  }
393 
394 
395 
396  if ((xMin < 0.0) && (xMax > 0.0))
397  {
398  // Draw a line for x=0.0
399  chart->DrawLine (RasterCoordinates (PlotPoint (0.0, double ((kkint32)(yMin + 0.5)))),
400  RasterCoordinates (PlotPoint (0.0, double ((kkint32)(yMax + 0.5)))),
401  zeroAxisColor
402  );
403 
404  }
405 
406 
407 
408  {
409  // Lets plot the individual series
410 
411  for (kkuint32 seriesIDX = 0; seriesIDX < series.size (); seriesIDX++)
412  {
413  SeriesPtr s = series[seriesIDX];
414 
415  if (s->points.size () == 0)
416  continue;
417 
418  PlotPoint lastPoint (s->points[0]);
419 
420  chart->DrawDot (RasterCoordinates (lastPoint), s->color, 3);
421 
422  for (kkuint32 plotIDX = 1; plotIDX < s->points.size (); plotIDX++)
423  {
424  PlotPoint point (s->points[plotIDX]);
425 
426  chart->DrawDot (RasterCoordinates (point), s->color, 3);
427 
428  chart->DrawLine (RasterCoordinates (lastPoint),
429  RasterCoordinates (point),
430  s->color
431  );
432 
433  lastPoint = point;
434  }
435  }
436  }
437 
438 
439  return chart;
440 } /* CreateRaster */
441 
442 
443 
444 
445 void Chart::SaveAsImage (KKStr _fileName)
446 {
447  RasterPtr raster = CreateRaster ();
448  SaveImage (*raster, _fileName);
449 } /* SaveAsImage */
450 
451 
452 
453 
454 kkint32 Chart::LookUpXLableIDX (double xVal)
455 {
456  for (kkuint32 x = 0; x < xLabels.size (); x++)
457  {
458  if (xLabels[x].XVal () == xVal)
459  return x;
460  }
461 
462  return -1;
463 } /* LookUpXLableIDX */
464 
465 
466 
467 
468 Chart::PlotPointPtr Chart::LookUpPoint (kkint32 seriesIDX,
469  double xVal
470  )
471 {
472  Series& s = *(series[seriesIDX]);
473 
474  kkuint32 pIDX;
475  for (pIDX = 0; pIDX <= s.points.size (); pIDX++)
476  {
477  if (s.points[pIDX].XVal () == xVal)
478  return &s.points[pIDX];
479  }
480 
481  return NULL;
482 } /* LookUpPoint */
483 
484 
485 
486 
487 void Chart::Save (KKStr _fileName)
488 {
489  ofstream o (_fileName.Str ());
490  if (!o.is_open ())
491  {
492  cerr << std::endl
493  << "*** ERROR *** Chart::Save, Error Opening file[" << _fileName << "]." << std::endl
494  << std::endl;
495  return;
496  }
497 
498 
499  {
500  // Make sure we have a XLabel for all Points Plotted
501  for (kkuint32 seriesIDX = 0; seriesIDX < series.size (); seriesIDX++)
502  {
503  SeriesPtr s = series[seriesIDX];
504  for (kkuint32 plotIDX = 0; plotIDX < s->points.size (); plotIDX++)
505  {
506  PlotPoint& p = s->points[plotIDX];
507  if (LookUpXLableIDX (p.XVal ()) < 0)
508  {
509  char buff[80];
510 
511 # ifdef USE_SECURE_FUNCS
512  sprintf_s (buff, sizeof (buff), "%g", p.XVal ());
513 # else
514  sprintf (buff, "%g", p.XVal ());
515 # endif
516 
517  xLabels.push_back (XLabel (p.XVal (), buff));
518  }
519  }
520  }
521  }
522 
523 
524  std::sort (xLabels.begin (), xLabels.end ());
525 
526 
527  {
528  // Output Header Info
529  o << "Title" << "\t" << title << std::endl;
530  o << "MinAndMaxXValues:" << "\t" << minXValue << "\t" << maxXValue << std::endl;
531  o << "MinAndMaxYValues:" << "\t" << minYValue << "\t" << maxYValue << std::endl;
532 
533  o << "MaxPointsPlottedInASeries" << "\t" << maxPointsPlotedInASeries << std::endl;
534  o << std::endl;
535  }
536 
537 
538  o << std::endl;
539  o << std::endl;
540 
541 
542  {
543  // Column Headers
544  o << "XLabel" << "\t" << "XValue";
545  for (kkint32 seriesIDX = 0; seriesIDX < NumOfSeries (); seriesIDX++)
546  {
547  o << "\t" << series[seriesIDX]->name;
548  }
549  o << std::endl;
550  }
551 
552 
553 
554  {
555  kkuint32 x;
556 
557  for (x = 0; x < xLabels.size (); x++)
558  {
559  XLabel& xLabel = xLabels[x];
560 
561  double xVal = xLabel.XVal ();
562 
563 
564  o << xLabel.Name () << "\t" << xVal;
565 
566  kkint32 seriesIDX = 0;
567 
568  for (seriesIDX = 0; seriesIDX < NumOfSeries (); seriesIDX++)
569  {
570  o << "\t";
571 
572  PlotPointPtr p = LookUpPoint (seriesIDX, xVal);
573  if (p)
574  {
575  o << p->YVal ();
576  }
577  }
578 
579  o << std::endl;
580  }
581  }
582 
583 
584  o.close ();
585 } /* Save */
PixelValue(const PixelValue &pixelValue)
Definition: PixelValue.cpp:52
Series * SeriesPtr
Definition: Chart.h:29
void AddAValue(float _xVal, float _yVal)
Definition: Chart.cpp:130
PlotPoint * PlotPointPtr
Definition: Chart.h:33
void DrawLine(kkint32 bpRow, kkint32 bpCol, kkint32 epRow, kkint32 epCol, PixelValue pixelVal)
Definition: Raster.cpp:6771
__int32 kkint32
Definition: KKBaseTypes.h:88
float FloatMin
Definition: KKBaseTypes.cpp:21
void DefineASeries(KKStr _name, PixelValue _color)
Definition: Chart.cpp:253
void DefineAXLabel(float _xVal, KKStr _name)
Definition: Chart.cpp:263
void Save(KKStr _fileName)
Definition: Chart.cpp:487
kkint32 Size()
Definition: Chart.cpp:71
PlotPoint(kkint32 _xVal, kkint32 _yVal)
Definition: Chart.cpp:40
float FloatMax
Definition: KKBaseTypes.cpp:20
static PixelValue defaultColors[]
Definition: Chart.cpp:223
A class that is used by to represent a single image in memory.
Definition: Raster.h:108
unsigned __int32 kkuint32
Definition: KKBaseTypes.h:89
void AddAValue(kkuint32 _seriesIDX, float _xVal, float _yVal)
Definition: Chart.cpp:184
double YVal() const
Definition: Chart.cpp:49
void DefineASeries(KKStr _name)
Definition: Chart.cpp:245
Used by Raster class and MorphOp derived classes to denote a single pixel location in Raster image...
Definition: Point.h:20
void DrawDot(const Point &point, const PixelValue &color, kkint32 size)
Definition: Raster.cpp:6948
KKTHread * KKTHreadPtr
const KKStr & Name()
Definition: Chart.cpp:99
Series(KKStr _name, PixelValue _color)
Definition: Chart.cpp:120
KKStr(const KKStr &str)
Copy Constructor.
Definition: KKStr.cpp:561
PixelValue(uchar _r, uchar _g, uchar _b)
Constructs a &#39;PixelValue&#39; instance using the provided values for the color components.
Definition: PixelValue.cpp:60
void SaveImage(const Raster &image, const KKStr &imageFileName)
Definition: ImageIO.cpp:617
static KKStr Concat(const std::vector< std::string > &values)
Concatenates the list of &#39;std::string&#39; strings.
Definition: KKStr.cpp:1082
Chart(KKStr _title)
Definition: Chart.cpp:142
XLabel(double _xVal, KKStr _name)
Definition: Chart.cpp:91
kkint32 NumOfSeries()
Definition: Chart.h:40
double XVal() const
Definition: Chart.cpp:48
Raster(kkint32 _height, kkint32 _width, bool _color)
Constructs a blank image with given dimensions.
Definition: Raster.cpp:356
bool operator<(const XLabel &right) const
Definition: Chart.cpp:103
Point(kkint32 _row, kkint32 _col)
Definition: Point.cpp:43
friend std::ostream & operator<<(std::ostream &os, const Matrix &matrix)
RasterPtr CreateRaster()
Definition: Chart.cpp:287
double XVal()
Definition: Chart.cpp:101
void DrawLine(const Point &beginPoint, const Point &endPoint, const PixelValue &pixelVal)
Definition: Raster.cpp:6807
static const kkint32 numOfDefinedDefaultColors
Definition: Chart.cpp:241
vector< SeriesPtr > SeriesList
Definition: Chart.cpp:85
PlotPoint(double _xVal, double _yVal)
Definition: Chart.cpp:32
Used to Create chart&#39;s from defined series of data.
Definition: Chart.h:24
Used by the Raster Class to represent the contents of one pixel.
Definition: PixelValue.h:22
void SaveAsImage(KKStr _fileName)
Definition: Chart.cpp:445