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