vtkPlotLine.cxx 18.6 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkPlotLine.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/

#include "vtkPlotLine.h"

#include "vtkContext2D.h"
19
#include "vtkAxis.h"
20
#include "vtkPen.h"
21
22
23
#include "vtkFloatArray.h"
#include "vtkVector.h"
#include "vtkTransform2D.h"
24
25
26
27
#include "vtkContextDevice2D.h"
#include "vtkContextMapper2D.h"
#include "vtkPoints2D.h"
#include "vtkTable.h"
28
#include "vtkDataArray.h"
29
#include "vtkIdTypeArray.h"
30
#include "vtkImageData.h"
31
32
33
#include "vtkExecutive.h"
#include "vtkTimeStamp.h"
#include "vtkInformation.h"
34
#include "vtkMath.h"
35
36
37

#include "vtkObjectFactory.h"

38
39
40
#include "vtkstd/vector"
#include "vtkstd/algorithm"

David Partyka's avatar
David Partyka committed
41

42
43
44
45
46
47
48
49
50
// PIMPL for STL vector...
class vtkPlotLine::VectorPIMPL : public vtkstd::vector<vtkVector2f>
{
public:
  VectorPIMPL(vtkVector2f* start, vtkVector2f* end)
    : vtkstd::vector<vtkVector2f>::vector(start, end)
  {
  }
};
51
52
53
54
55
56
57

//-----------------------------------------------------------------------------
vtkStandardNewMacro(vtkPlotLine);

//-----------------------------------------------------------------------------
vtkPlotLine::vtkPlotLine()
{
58
59
  this->Points = NULL;
  this->Sorted = NULL;
60
  this->BadPoints = NULL;
61
  this->MarkerStyle = vtkPlotLine::NONE;
62
63
  this->LogX = false;
  this->LogY = false;
64
  this->Marker = NULL;
65
  this->HighlightMarker = NULL;
66
67
68
69
70
}

//-----------------------------------------------------------------------------
vtkPlotLine::~vtkPlotLine()
{
71
72
73
74
75
  if (this->Points)
    {
    this->Points->Delete();
    this->Points = NULL;
    }
76
  delete this->Sorted;
Marcus Hanwell's avatar
Marcus Hanwell committed
77
78
79
80
81
  if (this->BadPoints)
    {
    this->BadPoints->Delete();
    this->BadPoints = NULL;
    }
82
83
84
85
  if (this->Marker)
    {
    this->Marker->Delete();
    }
86
87
88
89
90
91
92
93
  if (this->HighlightMarker)
    {
    this->HighlightMarker->Delete();
    }
  if (this->Selection)
    {
    this->Selection->Delete();
    }
94
95
96
}

//-----------------------------------------------------------------------------
97
void vtkPlotLine::Update()
98
{
99
100
  if (!this->Visible)
    {
101
    return;
102
    }
103
  // Check if we have an input
104
105
106
  vtkTable *table = this->Data->GetInput();
  if (!table)
    {
107
108
    vtkDebugMacro(<< "Update event called with no input table set.");
    return;
109
    }
110
111
112
  else if(this->Data->GetMTime() > this->BuildTime ||
          table->GetMTime() > this->BuildTime ||
          this->MTime > this->BuildTime)
113
    {
114
    vtkDebugMacro(<< "Updating cached values.");
115
116
    this->UpdateTableCache(table);
    }
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
  else if ((this->XAxis && this->XAxis->GetMTime() > this->BuildTime) ||
           (this->YAxis && this->YAxis->GetMaximum() > this->BuildTime))
    {
    if (this->LogX != this->XAxis->GetLogScale() ||
        this->LogY != this->YAxis->GetLogScale())
      {
      this->UpdateTableCache(table);
      }
    }
}

//-----------------------------------------------------------------------------
bool vtkPlotLine::Paint(vtkContext2D *painter)
{
  // This is where everything should be drawn, or dispatched to other methods.
  vtkDebugMacro(<< "Paint event called in vtkPlotLine.");

  if (!this->Visible)
    {
    return false;
    }
138

139
140
141
142
143
144
  float width = this->Pen->GetWidth() * 2.3;
  if (width < 8.0)
    {
    width = 8.0;
    }

145
146
147
148
149
150
  // Now add some decorations for our selected points...
  if (this->Selection)
    {
    vtkDebugMacro(<<"Selection set " << this->Selection->GetNumberOfTuples());
    for (int i = 0; i < this->Selection->GetNumberOfTuples(); ++i)
      {
151
152
153
154
155
      this->GeneraterMarker(static_cast<int>(width+2.7), true);

      painter->GetPen()->SetColor(255, 50, 0, 255);
      painter->GetPen()->SetWidth(width+2.7);

156
157
158
159
160
      vtkIdType id = 0;
      this->Selection->GetTupleValue(i, &id);
      if (id < this->Points->GetNumberOfPoints())
        {
        double *point = this->Points->GetPoint(id);
161
162
        float p[] = { point[0], point[1] };
        painter->DrawPointSprites(this->HighlightMarker, p, 1);
163
164
165
166
167
        }
      }
    }
  else
    {
168
    vtkDebugMacro("No selection set.");
169
170
171
172
173
    }

  // Now to plot the points
  if (this->Points)
    {
174
    painter->ApplyPen(this->Pen);
175
    painter->DrawPoly(this->Points);
176
    painter->GetPen()->SetLineType(vtkPen::SOLID_LINE);
177
    }
178
179
180
  // If there is a marker style, then draw the marker for each point too
  if (this->MarkerStyle)
    {
181
    this->GeneraterMarker(static_cast<int>(width));
182
    painter->ApplyBrush(this->Brush);
183
    painter->GetPen()->SetWidth(width);
184
    painter->DrawPointSprites(this->Marker, this->Points);
185
186
187
188
189
    }

  return true;
}

190
void vtkPlotLine::GeneraterMarker(int width, bool highlight)
191
{
192
193
194
195
  // Set up the image data, if highlight then the mark shape is different
  vtkImageData *data = 0;

  if (!highlight)
196
    {
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
    if (!this->Marker)
      {
      this->Marker = vtkImageData::New();
      this->Marker->SetScalarTypeToUnsignedChar();
      this->Marker->SetNumberOfScalarComponents(4);
      }
    else
      {
      if (this->Marker->GetMTime() >= this->GetMTime() &&
          this->Marker->GetMTime() >= this->Pen->GetMTime())
        {
        // Marker already generated, no need to do this again.
        return;
        }
      }
    data = this->Marker;
213
214
215
    }
  else
    {
216
217
218
219
220
221
222
223
    if (!this->HighlightMarker)
      {
      this->HighlightMarker = vtkImageData::New();
      this->HighlightMarker->SetScalarTypeToUnsignedChar();
      this->HighlightMarker->SetNumberOfScalarComponents(4);
      data = this->HighlightMarker;
      }
    else
224
      {
225
226
227
228
229
230
      if (this->HighlightMarker->GetMTime() >= this->GetMTime() &&
          this->HighlightMarker->GetMTime() >= this->Pen->GetMTime())
        {
        // Marker already generated, no need to do this again.
        return;
        }
231
      }
232
    data = this->HighlightMarker;
233
    }
234
235
236

  data->SetExtent(0, width-1, 0, width-1, 0, 0);
  data->AllocateScalars();
237
  unsigned char* image =
238
      static_cast<unsigned char*>(data->GetScalarPointer());
239
240
241
242
243

  // Generate the marker image at the required size
  switch (this->MarkerStyle)
    {
    case vtkPlotLine::CROSS:
244
      {
245
      for (int i = 0; i < width; ++i)
246
        {
247
        for (int j = 0; j < width; ++j)
248
          {
249
          unsigned char color = 0;
250
251

          if (highlight)
252
            {
253
254
255
256
257
258
259
260
261
262
263
            if ((i >= j-1 && i <= j+1) || (i >= width-j-1 && i <= width-j+1))
              {
              color = 255;
              }
            }
          else
            {
            if (i == j || i == width-j)
              {
              color = 255;
              }
264
265
266
267
            }
          image[4*width*i + 4*j] = image[4*width*i + 4*j + 1] =
                                   image[4*width*i + 4*j + 2] = color;
          image[4*width*i + 4*j + 3] = color;
268
269
          }
        }
270
271
272
273
274
275
276
      break;
      }
    case vtkPlotLine::PLUS:
      {
      int x = width / 2;
      int y = width / 2;
      for (int i = 0; i < width; ++i)
277
        {
278
        for (int j = 0; j < width; ++j)
279
          {
280
281
282
283
284
          unsigned char color = 0;
          if (i == x || j == y)
            {
            color = 255;
            }
285
286
287
288
289
290
291
          if (highlight)
            {
            if (i == x-1 || i == x+1 || j == y-1 || j == y+1)
              {
              color = 255;
              }
            }
292
293
294
          image[4*width*i + 4*j] = image[4*width*i + 4*j + 1] =
                                   image[4*width*i + 4*j + 2] = color;
          image[4*width*i + 4*j + 3] = color;
295
296
          }
        }
297
298
299
300
301
      break;
      }
    case vtkPlotLine::SQUARE:
      {
      for (int i = 0; i < width; ++i)
302
        {
303
        for (int j = 0; j < width; ++j)
304
          {
305
306
307
308
          unsigned char color = 255;
          image[4*width*i + 4*j] = image[4*width*i + 4*j + 1] =
                                   image[4*width*i + 4*j + 2] = color;
          image[4*width*i + 4*j + 3] = color;
309
310
          }
        }
311
312
313
314
315
316
      break;
      }
    case vtkPlotLine::CIRCLE:
      {
      double c = width/2.0;
      for (int i = 0; i < width; ++i)
317
        {
318
        double dx2 = (i - c)*(i-c);
319
320
        for (int j = 0; j < width; ++j)
          {
321
          double dy2 = (j - c)*(j - c);
322
323
324
325
326
327
328
329
330
          unsigned char color = 0;
          if (sqrt(dx2 + dy2) < c)
            {
            color = 255;
            }
          image[4*width*i + 4*j] = image[4*width*i + 4*j + 1] =
                                   image[4*width*i + 4*j + 2] = color;
          image[4*width*i + 4*j + 3] = color;
          }
331
        }
332
333
334
335
336
337
      break;
      }
    case vtkPlotLine::DIAMOND:
      {
      int c = width/2;
      for (int i = 0; i < width; ++i)
338
        {
339
340
        int dx = i-c > 0 ? i-c : c-i;
        for (int j = 0; j < width; ++j)
341
          {
342
343
344
345
346
347
348
349
350
          int dy = j-c > 0 ? j-c : c-j;
          unsigned char color = 0;
          if (c-dx >= dy)
            {
            color = 255;
            }
          image[4*width*i + 4*j] = image[4*width*i + 4*j + 1] =
                                   image[4*width*i + 4*j + 2] = color;
          image[4*width*i + 4*j + 3] = color;
351
352
          }
        }
353
      break;
354
      }
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
    default:
      {
      int x = width / 2;
      int y = width / 2;
      for (int i = 0; i < width; ++i)
        {
        for (int j = 0; j < width; ++j)
          {
          unsigned char color = 0;
          if (i == x || j == y)
            {
            color = 255;
            }
          image[4*width*i + 4*j] = image[4*width*i + 4*j + 1] =
                                   image[4*width*i + 4*j + 2] = color;
          image[4*width*i + 4*j + 3] = color;
          }
        }
      }
374
    }
375
376
}

377
378
379
380
381
382
383
384
385
//-----------------------------------------------------------------------------
bool vtkPlotLine::PaintLegend(vtkContext2D *painter, float rect[4])
{
  painter->ApplyPen(this->Pen);
  painter->DrawLine(rect[0], rect[1]+0.5*rect[3],
                    rect[0]+rect[2], rect[1]+0.5*rect[3]);
  return true;
}

386
387
388
//-----------------------------------------------------------------------------
void vtkPlotLine::GetBounds(double bounds[4])
{
389
390
  if (this->Points)
    {
391
392
393
394
395
396
397
398
399
    if (!this->BadPoints)
      {
      this->Points->GetBounds(bounds);
      }
    else
      {
      // There are bad points in the series - need to do this ourselves.
      this->CalculateBounds(bounds);
      }
400
    }
401
402
403
404
  vtkDebugMacro(<< "Bounds: " << bounds[0] << "\t" << bounds[1] << "\t"
                << bounds[2] << "\t" << bounds[3]);
}

405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
namespace
{

// Compare the two vectors, in X component only
bool compVector2fX(const vtkVector2f& v1, const vtkVector2f& v2)
{
  if (v1.X() < v2.X())
    {
    return true;
    }
  else
    {
    return false;
    }
}

// See if the point is within tolerance.
bool inRange(const vtkVector2f& point, const vtkVector2f& tol,
             const vtkVector2f& current)
{
  if (current.X() > point.X() - tol.X() && current.X() < point.X() + tol.X() &&
      current.Y() > point.Y() - tol.Y() && current.Y() < point.Y() + tol.Y())
    {
    return true;
    }
  else
    {
    return false;
    }
}

}

//-----------------------------------------------------------------------------
bool vtkPlotLine::GetNearestPoint(const vtkVector2f& point,
                                  const vtkVector2f& tol,
                                  vtkVector2f* location)
{
  // Right now doing a simple bisector search of the array. This should be
  // revisited. Assumes the x axis is sorted, which should always be true for
  // line plots.
  if (!this->Points)
    {
    return false;
    }
  vtkIdType n = this->Points->GetNumberOfPoints();
  if (n < 2)
    {
    return false;
    }

456
  // Sort the data if it has not been done already...
457
458
459
  if (!this->Sorted)
    {
    vtkVector2f* data =
460
461
462
        static_cast<vtkVector2f*>(this->Points->GetVoidPointer(0));
    this->Sorted = new VectorPIMPL(data, data+n);
    vtkstd::sort(this->Sorted->begin(), this->Sorted->end(), compVector2fX);
463
464
    }

465
  // Set up our search array, use the STL lower_bound algorithm
466
467
468
  VectorPIMPL::iterator low;
  VectorPIMPL &v = *this->Sorted;

469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
  // Get the lowest point we might hit within the supplied tolerance
  vtkVector2f lowPoint(point.X()-tol.X(), 0.0f);
  low = vtkstd::lower_bound(v.begin(), v.end(), lowPoint, compVector2fX);

  // Now consider the y axis
  float highX = point.X() + tol.X();
  while (low != v.end())
    {
    if (inRange(point, tol, *low))
      {
      *location = *low;
      return true;
      }
    else if (low->X() > highX)
      {
      break;
      }
    ++low;
    }
  return false;
}

491
//-----------------------------------------------------------------------------
492
493
494
495
496
497
498
499
500
501
502
503
504
505
namespace {

// Copy the two arrays into the points array
template<class A>
void CopyToPointsSwitch(vtkPoints2D *points, A *a, vtkDataArray *b, int n)
{
  switch(b->GetDataType())
    {
    vtkTemplateMacro(
        CopyToPoints(points, a, static_cast<VTK_TT*>(b->GetVoidPointer(0)), n));
    }
}

// Copy the two arrays into the points array
506
507
508
509
template<class A, class B>
void CopyToPoints(vtkPoints2D *points, A *a, B *b, int n)
{
  points->SetNumberOfPoints(n);
510
  float* data = static_cast<float*>(points->GetVoidPointer(0));
511
512
  for (int i = 0; i < n; ++i)
    {
513
514
    data[2*i] = a[i];
    data[2*i+1] = b[i];
515
516
517
    }
}

518
519
520
521
522
// Copy one array into the points array, use the index of that array as x
template<class A>
void CopyToPoints(vtkPoints2D *points, A *a, int n)
{
  points->SetNumberOfPoints(n);
523
  float* data = static_cast<float*>(points->GetVoidPointer(0));
524
525
  for (int i = 0; i < n; ++i)
    {
526
527
    data[2*i] = static_cast<float>(i);
    data[2*i+1] = a[i];
528
529
530
    }
}

531
532
}

533
534
535
536
//-----------------------------------------------------------------------------
bool vtkPlotLine::UpdateTableCache(vtkTable *table)
{
  // Get the x and y arrays (index 0 and 1 respectively)
537
538
  vtkDataArray* x = this->UseIndexForXSeries ?
                    0 : this->Data->GetInputArrayToProcess(0, table);
539
  vtkDataArray* y = this->Data->GetInputArrayToProcess(1, table);
540
  if (!x && !this->UseIndexForXSeries)
541
542
543
544
545
546
547
548
549
    {
    vtkErrorMacro(<< "No X column is set (index 0).");
    return false;
    }
  else if (!y)
    {
    vtkErrorMacro(<< "No Y column is set (index 1).");
    return false;
    }
550
551
  else if (!this->UseIndexForXSeries &&
           x->GetNumberOfTuples() != y->GetNumberOfTuples())
552
    {
553
554
    vtkErrorMacro("The x and y columns must have the same number of elements. "
                  << x->GetNumberOfTuples() << ", " << y->GetNumberOfTuples());
555
556
557
558
559
560
561
562
    return false;
    }

  if (!this->Points)
    {
    this->Points = vtkPoints2D::New();
    }

563
  // Now copy the components into their new columns
564
  if (this->UseIndexForXSeries)
565
    {
566
567
568
569
570
    switch(y->GetDataType())
      {
        vtkTemplateMacro(
            CopyToPoints(this->Points,
                         static_cast<VTK_TT*>(y->GetVoidPointer(0)),
571
                         y->GetNumberOfTuples()));
572
573
574
575
576
577
      }
    }
  else
    {
    switch(x->GetDataType())
      {
578
579
580
      vtkTemplateMacro(
          CopyToPointsSwitch(this->Points,
                             static_cast<VTK_TT*>(x->GetVoidPointer(0)),
581
                             y, x->GetNumberOfTuples()));
582
      }
583
    }
584
  this->CalculateLogSeries();
585
  this->FindBadPoints();
586
  this->Points->Modified();
587
588
  if (this->Sorted)
    {
589
590
    delete this->Sorted;
    this->Sorted = 0;
591
    }
592
  this->BuildTime.Modified();
593
  return true;
594
595
}

596
//-----------------------------------------------------------------------------
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
inline void vtkPlotLine::CalculateLogSeries()
{
  if (!this->XAxis || !this->YAxis)
    {
    return;
    }
  this->LogX = this->XAxis->GetLogScale();
  this->LogY = this->YAxis->GetLogScale();
  float* data = static_cast<float*>(this->Points->GetVoidPointer(0));
  vtkIdType n = this->Points->GetNumberOfPoints();
  if (this->LogX)
    {
    for (vtkIdType i = 0; i < n; ++i)
      {
      data[2*i] = log10(data[2*i]);
      }
    }
  if (this->LogY)
615
616
    {
    for (vtkIdType i = 0; i < n; ++i)
617
618
619
620
621
622
    {
    data[2*i+1] = log10(data[2*i+1]);
    }
  }
}

623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
//-----------------------------------------------------------------------------
inline void vtkPlotLine::FindBadPoints()
{
  // This should be run after CalculateLogSeries as a final step.
  float* data = static_cast<float*>(this->Points->GetVoidPointer(0));
  vtkIdType n = this->Points->GetNumberOfPoints();
  if (!this->BadPoints)
    {
    this->BadPoints = vtkIdTypeArray::New();
    }
  else
    {
    this->BadPoints->SetNumberOfTuples(0);
    }

  // Scan through and find any bad points.
  for (vtkIdType i = 0; i < n; ++i)
    {
    vtkIdType p = 2*i;
    if (vtkMath::IsInf(data[p]) || vtkMath::IsInf(data[p+1]) ||
        vtkMath::IsNan(data[p]) || vtkMath::IsNan(data[p+1]))
      {
      this->BadPoints->InsertNextValue(i);
      }
    }

  if (this->BadPoints->GetNumberOfTuples() == 0)
    {
    this->BadPoints->Delete();
    this->BadPoints = NULL;
    }
}

//-----------------------------------------------------------------------------
inline void vtkPlotLine::CalculateBounds(double bounds[4])
{
  // We can use the BadPoints array to skip the bad points
  if (!this->Points || !this->BadPoints)
    {
    return;
    }
  vtkIdType start = 0;
  vtkIdType end = 0;
  vtkIdType i = 0;
  vtkIdType nBad = this->BadPoints->GetNumberOfTuples();
  vtkIdType nPoints = this->Points->GetNumberOfPoints();
  if (this->BadPoints->GetValue(i) == 0)
    {
    while (i < nBad && i == this->BadPoints->GetValue(i))
      {
      start = this->BadPoints->GetValue(i++) + 1;
      }
    if (start < nPoints)
      {
      end = nPoints;
      }
    else
      {
      // They are all bad points
      return;
      }
    }
  if (i < nBad)
    {
    end = this->BadPoints->GetValue(i++);
    }
  else
    {
    end = nPoints;
    }
  vtkVector2f* pts = static_cast<vtkVector2f*>(this->Points->GetVoidPointer(0));

  // Initialize our min/max
  bounds[0] = bounds[1] = pts[start].X();
  bounds[2] = bounds[3] = pts[start++].Y();

  while (start < nPoints)
    {
    // Calculate the min/max in this range
    while (start < end)
      {
      float x = pts[start].X();
      float y = pts[start++].Y();
      if (x < bounds[0])
        {
        bounds[0] = x;
        }
      else if (x > bounds[1])
        {
        bounds[1] = x;
        }
      if (y < bounds[2])
        {
        bounds[2] = y;
        }
      else if (y > bounds[3])
        {
        bounds[3] = y;
        }
      }
    // Now figure out the next range
    start = end + 1;
    if (++i < nBad)
      {
      end = this->BadPoints->GetValue(i);
      }
    else
      {
      end = nPoints;
      }
    }
}

736
737
738
739
740
//-----------------------------------------------------------------------------
void vtkPlotLine::PrintSelf(ostream &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}