1 /*=========================================================================
2 
3   Program:   Visualization Toolkit
4   Module:    vtkPlotBag.cxx
5 
6   Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7   All rights reserved.
8   See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9 
10      This software is distributed WITHOUT ANY WARRANTY; without even
11      the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12      PURPOSE.  See the above copyright notice for more information.
13 
14 =========================================================================*/
15 
16 #include "vtkBrush.h"
17 #include "vtkContext2D.h"
18 #include "vtkContextMapper2D.h"
19 #include "vtkDataArray.h"
20 #include "vtkDoubleArray.h"
21 #include "vtkObjectFactory.h"
22 #include "vtkPen.h"
23 #include "vtkPlotBag.h"
24 #include "vtkPoints.h"
25 #include "vtkPoints2D.h"
26 #include "vtkPointsProjectedHull.h"
27 #include "vtkStringArray.h"
28 #include "vtkTable.h"
29 #include "vtkTimeStamp.h"
30 #include "vtkMath.h"
31 
32 #include <algorithm>
33 #include <sstream>
34 
35 //-----------------------------------------------------------------------------
36 vtkStandardNewMacro(vtkPlotBag);
37 
38 vtkSetObjectImplementationMacro(vtkPlotBag, LinePen, vtkPen);
39 
40 //-----------------------------------------------------------------------------
vtkPlotBag()41 vtkPlotBag::vtkPlotBag()
42 {
43   this->MedianPoints = vtkPoints2D::New();
44   this->Q3Points = vtkPoints2D::New();
45   this->TooltipDefaultLabelFormat = "%C, %l (%x, %y): %z";
46   this->Brush->SetColor(255, 0, 0);
47   this->Brush->SetOpacity(255);
48   this->Pen->SetColor(0, 0, 0);
49   this->Pen->SetWidth(5.f);
50   this->LinePen = vtkPen::New();
51   this->LinePen->SetColor(0, 0, 0);
52   this->LinePen->SetWidth(1.f);
53 }
54 
55 //-----------------------------------------------------------------------------
~vtkPlotBag()56 vtkPlotBag::~vtkPlotBag()
57 {
58   if (this->MedianPoints)
59     {
60     this->MedianPoints->Delete();
61     this->MedianPoints = 0;
62     }
63   if (this->Q3Points)
64     {
65     this->Q3Points->Delete();
66     this->Q3Points = 0;
67     }
68   if (this->LinePen)
69     {
70     this->LinePen->Delete();
71     this->LinePen = 0;
72     }
73 }
74 
75 //-----------------------------------------------------------------------------
Update()76 void vtkPlotBag::Update()
77 {
78   if (!this->Visible)
79     {
80     return;
81     }
82 
83   // Check if we have an input
84   vtkTable *table = this->Data->GetInput();
85   vtkDataArray *density = vtkDataArray::SafeDownCast(
86     this->Data->GetInputAbstractArrayToProcess(2, this->GetInput()));
87   if (!table || !density)
88     {
89     vtkDebugMacro(<< "Update event called with no input table or density column set.");
90     return;
91     }
92   bool update = (this->Data->GetMTime() > this->BuildTime ||
93     table->GetMTime() > this->BuildTime ||
94     this->MTime > this->BuildTime);
95 
96   this->Superclass::Update();
97 
98   if (update)
99     {
100     vtkDebugMacro(<< "Updating cached values.");
101     this->UpdateTableCache(density);
102     }
103 }
104 
105 //-----------------------------------------------------------------------------
106 class DensityVal
107 {
108 public:
DensityVal(double d,vtkIdType cid)109   DensityVal(double d, vtkIdType cid) : Density(d), Id(cid) {}
operator <(const DensityVal & b) const110   bool operator<(const DensityVal& b) const
111   {
112     return this->Density > b.Density;
113   }
114   double Density;
115   vtkIdType Id;
116 };
117 
118 //-----------------------------------------------------------------------------
UpdateTableCache(vtkDataArray * density)119 void vtkPlotBag::UpdateTableCache(vtkDataArray* density)
120 {
121   this->MedianPoints->Reset();
122   this->Q3Points->Reset();
123 
124   if (!this->Points)
125     {
126     return;
127     }
128   vtkDataArray* d = density;
129   vtkPoints2D* points = this->Points;
130 
131   vtkIdType nbPoints = d->GetNumberOfTuples();
132 
133   // Fetch and sort arrays according their density
134   std::vector<DensityVal> ids;
135   ids.reserve(nbPoints);
136   for (int i = 0; i < nbPoints; i++)
137     {
138     ids.push_back(DensityVal(d->GetTuple1(i), i));
139     }
140   std::sort(ids.begin(), ids.end());
141 
142   vtkNew<vtkPointsProjectedHull> q3Points;
143   q3Points->Allocate(nbPoints);
144   vtkNew<vtkPointsProjectedHull> medianPoints;
145   medianPoints->Allocate(nbPoints);
146 
147   // Compute total density sum
148   double densitySum = 0.0;
149   for (vtkIdType i = 0; i < nbPoints; i++)
150     {
151     densitySum += d->GetTuple1(i);
152     }
153 
154   double sum = 0.0;
155   for (vtkIdType i = 0; i < nbPoints; i++)
156     {
157     double x[3];
158     points->GetPoint(ids[i].Id, x);
159     sum += ids[i].Density;
160     if (sum < 0.5 * densitySum)
161       {
162       medianPoints->InsertNextPoint(x);
163       }
164     if (sum < 0.99 * densitySum)
165       {
166       q3Points->InsertNextPoint(x);
167       }
168     else
169       {
170       break;
171       }
172     }
173 
174   // Compute the convex hull for the median points
175   vtkIdType nbMedPoints = medianPoints->GetNumberOfPoints();
176   if (nbMedPoints > 2)
177     {
178     int size = medianPoints->GetSizeCCWHullZ();
179     this->MedianPoints->SetDataTypeToFloat();
180     this->MedianPoints->SetNumberOfPoints(size+1);
181     medianPoints->GetCCWHullZ(
182       static_cast<float*>(this->MedianPoints->GetData()->GetVoidPointer(0)), size);
183     double x[3];
184     this->MedianPoints->GetPoint(0, x);
185     this->MedianPoints->SetPoint(size, x);
186     }
187   else if (nbMedPoints > 0)
188     {
189     this->MedianPoints->SetNumberOfPoints(nbMedPoints);
190     for (int j = 0; j < nbMedPoints; j++)
191       {
192       double x[3];
193       medianPoints->GetPoint(j, x);
194       this->MedianPoints->SetPoint(j, x);
195       }
196     }
197 
198   // Compute the convex hull for the first quartile points
199   vtkIdType nbQ3Points = q3Points->GetNumberOfPoints();
200   if (nbQ3Points > 2)
201     {
202     int size = q3Points->GetSizeCCWHullZ();
203     this->Q3Points->SetDataTypeToFloat();
204     this->Q3Points->SetNumberOfPoints(size+1);
205     q3Points->GetCCWHullZ(
206       static_cast<float*>(this->Q3Points->GetData()->GetVoidPointer(0)), size);
207     double x[3];
208     this->Q3Points->GetPoint(0, x);
209     this->Q3Points->SetPoint(size, x);
210     }
211   else if (nbQ3Points > 0)
212     {
213     this->Q3Points->SetNumberOfPoints(nbQ3Points);
214     for (int j = 0; j < nbQ3Points; j++)
215       {
216       double x[3];
217       q3Points->GetPoint(j, x);
218       this->Q3Points->SetPoint(j, x);
219       }
220     }
221 
222   this->BuildTime.Modified();
223 }
224 
225 //-----------------------------------------------------------------------------
Paint(vtkContext2D * painter)226 bool vtkPlotBag::Paint(vtkContext2D *painter)
227 {
228   vtkDebugMacro(<< "Paint event called in vtkPlotBag.");
229 
230   vtkTable *table = this->Data->GetInput();
231 
232   if (!this->Visible || !this->Points || !table)
233     {
234     return false;
235     }
236 
237   unsigned char bcolor[4];
238   this->Brush->GetColor(bcolor);
239 
240   // Draw the 2 bags
241   this->Brush->SetOpacity(255);
242   this->Brush->SetColor(bcolor[0] / 2, bcolor[1] / 2, bcolor[2] / 2);
243   painter->ApplyPen(this->LinePen);
244   painter->ApplyBrush(this->Brush);
245   if (this->Q3Points->GetNumberOfPoints() > 2)
246     {
247     painter->DrawPolygon(this->Q3Points);
248     }
249   else if (this->Q3Points->GetNumberOfPoints() == 2)
250     {
251     painter->DrawLine(this->Q3Points);
252     }
253 
254   this->Brush->SetColor(bcolor);
255   this->Brush->SetOpacity(128);
256   painter->ApplyBrush(this->Brush);
257 
258   if (this->MedianPoints->GetNumberOfPoints() > 2)
259     {
260     painter->DrawPolygon(this->MedianPoints);
261     }
262   else if (this->MedianPoints->GetNumberOfPoints() == 2)
263     {
264     painter->DrawLine(this->MedianPoints);
265     }
266 
267   painter->ApplyPen(this->Pen);
268 
269   // Let PlotPoints draw the points as usual
270   return this->Superclass::Paint(painter);
271 }
272 
273 //-----------------------------------------------------------------------------
PaintLegend(vtkContext2D * painter,const vtkRectf & rect,int)274 bool vtkPlotBag::PaintLegend(vtkContext2D *painter, const vtkRectf& rect, int)
275 {
276   painter->ApplyPen(this->LinePen);
277   unsigned char bcolor[4];
278   this->Brush->GetColor(bcolor);
279   unsigned char opacity = this->Brush->GetOpacity();
280 
281   this->Brush->SetOpacity(255);
282   this->Brush->SetColor(bcolor[0] / 2, bcolor[1] / 2, bcolor[2] / 2);
283   painter->ApplyBrush(this->Brush);
284   painter->DrawRect(rect[0], rect[1], rect[2], rect[3]);
285 
286   this->Brush->SetColor(bcolor);
287   this->Brush->SetOpacity(128);
288   painter->ApplyBrush(this->Brush);
289   painter->DrawRect(rect[0] + rect[2] / 2.f, rect[1], rect[2]/2, rect[3]);
290   this->Brush->SetOpacity(opacity);
291 
292   return true;
293 }
294 
295 //-----------------------------------------------------------------------------
GetLabels()296 vtkStringArray* vtkPlotBag::GetLabels()
297 {
298   // If the label string is empty, return the y column name
299   if (this->Labels)
300     {
301     return this->Labels;
302     }
303   else if (this->AutoLabels)
304     {
305     return this->AutoLabels;
306     }
307   else if (this->Data->GetInput())
308     {
309     this->AutoLabels = vtkSmartPointer<vtkStringArray>::New();
310     vtkDataArray *density = vtkDataArray::SafeDownCast(
311       this->Data->GetInputAbstractArrayToProcess(2, this->GetInput()));
312     if (density)
313       {
314       this->AutoLabels->InsertNextValue(density->GetName());
315       }
316     return this->AutoLabels;
317     }
318   return NULL;
319 }
320 
321 //-----------------------------------------------------------------------------
GetTooltipLabel(const vtkVector2d & plotPos,vtkIdType seriesIndex,vtkIdType)322 vtkStdString vtkPlotBag::GetTooltipLabel(const vtkVector2d &plotPos,
323                                          vtkIdType seriesIndex,
324                                          vtkIdType)
325 {
326   vtkStdString tooltipLabel;
327   vtkStdString &format = this->TooltipLabelFormat.empty() ?
328         this->TooltipDefaultLabelFormat : this->TooltipLabelFormat;
329   // Parse TooltipLabelFormat and build tooltipLabel
330   bool escapeNext = false;
331   vtkDataArray *density = vtkDataArray::SafeDownCast(
332     this->Data->GetInputAbstractArrayToProcess(2, this->GetInput()));
333   for (size_t i = 0; i < format.length(); ++i)
334     {
335     if (escapeNext)
336       {
337       switch (format[i])
338         {
339         case 'x':
340           tooltipLabel += this->GetNumber(plotPos.GetX(), this->XAxis);
341           break;
342         case 'y':
343           tooltipLabel += this->GetNumber(plotPos.GetY(), this->YAxis);
344           break;
345         case 'z':
346           tooltipLabel += density ?
347             density->GetVariantValue(seriesIndex).ToString() :
348             vtkStdString("?");
349           break;
350         case 'i':
351           if (this->IndexedLabels &&
352               seriesIndex >= 0 &&
353               seriesIndex < this->IndexedLabels->GetNumberOfTuples())
354             {
355             tooltipLabel += this->IndexedLabels->GetValue(seriesIndex);
356             }
357           break;
358         case 'l':
359           // GetLabel() is GetLabel(0) in this implementation
360           tooltipLabel += this->GetLabel();
361           break;
362         case 'c':
363           {
364           std::stringstream ss;
365           ss << seriesIndex;
366           tooltipLabel += ss.str();
367           }
368           break;
369         case 'C':
370           {
371           vtkAbstractArray *colName = vtkAbstractArray::SafeDownCast(
372             this->GetInput()->GetColumnByName("ColName"));
373           std::stringstream ss;
374           if (colName)
375             {
376             ss << colName->GetVariantValue(seriesIndex).ToString();
377             }
378           else
379             {
380             ss << "?";
381             }
382           tooltipLabel += ss.str();
383           }
384           break;
385         default: // If no match, insert the entire format tag
386           tooltipLabel += "%";
387           tooltipLabel += format[i];
388           break;
389         }
390       escapeNext = false;
391       }
392     else
393       {
394       if (format[i] == '%')
395         {
396         escapeNext = true;
397         }
398       else
399         {
400         tooltipLabel += format[i];
401         }
402       }
403     }
404   return tooltipLabel;
405 }
406 
407 //-----------------------------------------------------------------------------
SetInputData(vtkTable * table)408 void vtkPlotBag::SetInputData(vtkTable *table)
409 {
410   this->Data->SetInputData(table);
411   this->Modified();
412 }
413 
414 //-----------------------------------------------------------------------------
SetInputData(vtkTable * table,const vtkStdString & yColumn,const vtkStdString & densityColumn)415 void vtkPlotBag::SetInputData(vtkTable *table, const vtkStdString &yColumn,
416                               const vtkStdString &densityColumn)
417 {
418   vtkDebugMacro(<< "Setting input, Y column = \"" << yColumn.c_str() << "\", "
419                 << "Density column = \"" << densityColumn.c_str() << "\"");
420 
421   if (table->GetColumnByName(densityColumn.c_str())->GetNumberOfTuples()
422     != table->GetColumnByName(yColumn.c_str())->GetNumberOfTuples())
423     {
424     vtkErrorMacro(<< "Input table not correctly initialized!");
425     return;
426     }
427 
428   this->SetInputData(table, yColumn, yColumn, densityColumn);
429   this->UseIndexForXSeries = true;
430 }
431 
432 //-----------------------------------------------------------------------------
SetInputData(vtkTable * table,const vtkStdString & xColumn,const vtkStdString & yColumn,const vtkStdString & densityColumn)433 void vtkPlotBag::SetInputData(vtkTable *table, const vtkStdString &xColumn,
434                               const vtkStdString &yColumn,
435                               const vtkStdString &densityColumn)
436 {
437   vtkDebugMacro(<< "Setting input, X column = \"" << xColumn.c_str()
438                 << "\", " << "Y column = \""
439                 << yColumn.c_str() << "\""
440                 << "\", " << "Density column = \""
441                 << densityColumn.c_str() << "\"");
442 
443   this->Data->SetInputData(table);
444   this->Data->SetInputArrayToProcess(0, 0, 0,
445     vtkDataObject::FIELD_ASSOCIATION_ROWS, xColumn.c_str());
446   this->Data->SetInputArrayToProcess(1, 0, 0,
447     vtkDataObject::FIELD_ASSOCIATION_ROWS, yColumn.c_str());
448   this->Data->SetInputArrayToProcess(2, 0, 0,
449     vtkDataObject::FIELD_ASSOCIATION_ROWS, densityColumn.c_str());
450   if (this->AutoLabels)
451     {
452     this->AutoLabels = 0;
453     }
454 }
455 
456 //-----------------------------------------------------------------------------
SetInputData(vtkTable * table,vtkIdType xColumn,vtkIdType yColumn,vtkIdType densityColumn)457 void vtkPlotBag::SetInputData(vtkTable *table, vtkIdType xColumn,
458                               vtkIdType yColumn,
459                               vtkIdType densityColumn)
460 {
461   this->SetInputData(table,
462     table->GetColumnName(xColumn),
463     table->GetColumnName(yColumn),
464     table->GetColumnName(densityColumn));
465 }
466 
467 //-----------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)468 void vtkPlotBag::PrintSelf(ostream &os, vtkIndent indent)
469 {
470   this->Superclass::PrintSelf(os, indent);
471 }
472