1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkColorTransferFunction.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 #include "vtkColorTransferFunction.h"
16
17 #include "vtkMath.h"
18 #include "vtkObjectFactory.h"
19
20 #include <algorithm>
21 #include <iterator>
22 #include <math.h>
23 #include <set>
24 #include <vector>
25
26 vtkStandardNewMacro(vtkColorTransferFunction);
27
28 #define MY_MAX(x, y) ((x) > (y) ? (x) : (y))
29
30 //=============================================================================
31 class vtkCTFNode
32 {
33 public:
34 double X;
35 double R;
36 double G;
37 double B;
38 double Sharpness;
39 double Midpoint;
40 };
41
42 class vtkCTFCompareNodes
43 {
44 public:
operator ()(const vtkCTFNode * node1,const vtkCTFNode * node2)45 bool operator () ( const vtkCTFNode *node1,
46 const vtkCTFNode *node2 )
47 {
48 return node1->X < node2->X;
49 }
50 };
51
52 class vtkCTFFindNodeEqual
53 {
54 public:
55 double X;
operator ()(const vtkCTFNode * node)56 bool operator () ( const vtkCTFNode *node )
57 {
58 return node->X == this->X;
59 }
60 };
61
62 class vtkCTFFindNodeInRange
63 {
64 public:
65 double X1;
66 double X2;
operator ()(const vtkCTFNode * node)67 bool operator () (const vtkCTFNode *node )
68 {
69 return ( node->X >= this->X1 &&
70 node->X <= this->X2 );
71 }
72 };
73
74 class vtkCTFFindNodeOutOfRange
75 {
76 public:
77 double X1;
78 double X2;
operator ()(const vtkCTFNode * node)79 bool operator () (const vtkCTFNode *node )
80 {
81 return ( node->X < this->X1 ||
82 node->X > this->X2 );
83 }
84 };
85
86 class vtkColorTransferFunctionInternals
87 {
88 public:
89 std::vector<vtkCTFNode*> Nodes;
90 vtkCTFCompareNodes CompareNodes;
91 vtkCTFFindNodeEqual FindNodeEqual;
92 vtkCTFFindNodeInRange FindNodeInRange;
93 vtkCTFFindNodeOutOfRange FindNodeOutOfRange;
94 };
95
96 //=============================================================================
97 // Convert to and from a special polar version of CIELAB (useful for creating
98 // continuous diverging color maps).
vtkColorTransferFunctionLabToMsh(const double lab[3],double msh[3])99 inline void vtkColorTransferFunctionLabToMsh(const double lab[3], double msh[3])
100 {
101 const double &L = lab[0];
102 const double &a = lab[1];
103 const double &b = lab[2];
104 double &M = msh[0];
105 double &s = msh[1];
106 double &h = msh[2];
107
108 M = sqrt(L*L + a*a + b*b);
109 s = (M > 0.001) ? acos(L/M) : 0.0;
110 h = (s > 0.001) ? atan2(b,a) : 0.0;
111 }
112
vtkColorTransferFunctionMshToLab(const double msh[3],double lab[3])113 inline void vtkColorTransferFunctionMshToLab(const double msh[3], double lab[3])
114 {
115 const double &M = msh[0];
116 const double &s = msh[1];
117 const double &h = msh[2];
118 double &L = lab[0];
119 double &a = lab[1];
120 double &b = lab[2];
121
122 L = M*cos(s);
123 a = M*sin(s)*cos(h);
124 b = M*sin(s)*sin(h);
125 }
126
127 // Given two angular orientations, returns the smallest angle between the two.
vtkColorTransferFunctionAngleDiff(double a1,double a2)128 inline double vtkColorTransferFunctionAngleDiff(double a1, double a2)
129 {
130 double adiff = a1 - a2;
131 if (adiff < 0.0) adiff = -adiff;
132 while (adiff >= 2.0 * vtkMath::Pi()) adiff -= (2.0 * vtkMath::Pi());
133 if (adiff > vtkMath::Pi()) adiff = (2.0 * vtkMath::Pi()) - adiff;
134 return adiff;
135 }
136
137 // For the case when interpolating from a saturated color to an unsaturated
138 // color, find a hue for the unsaturated color that makes sense.
vtkColorTransferFunctionAdjustHue(const double msh[3],double unsatM)139 inline double vtkColorTransferFunctionAdjustHue(const double msh[3],
140 double unsatM)
141 {
142 if (msh[0] >= unsatM - 0.1)
143 {
144 // The best we can do is hold hue constant.
145 return msh[2];
146 }
147 else
148 {
149 // This equation is designed to make the perceptual change of the
150 // interpolation to be close to constant.
151 double hueSpin = ( msh[1]*sqrt(unsatM*unsatM - msh[0]*msh[0])
152 / (msh[0]*sin(msh[1])) );
153 // Spin hue away from 0 except in purple hues.
154 if (msh[2] > -0.3*vtkMath::Pi())
155 {
156 return msh[2] + hueSpin;
157 }
158 else
159 {
160 return msh[2] - hueSpin;
161 }
162 }
163 }
164
165 // Interpolate a diverging color map.
vtkColorTransferFunctionInterpolateDiverging(double s,const double rgb1[3],const double rgb2[3],double result[3])166 inline void vtkColorTransferFunctionInterpolateDiverging(double s,
167 const double rgb1[3],
168 const double rgb2[3],
169 double result[3])
170 {
171 double lab1[3], lab2[3];
172 vtkMath::RGBToLab(rgb1, lab1);
173 vtkMath::RGBToLab(rgb2, lab2);
174
175 double msh1[3], msh2[3];
176 vtkColorTransferFunctionLabToMsh(lab1, msh1);
177 vtkColorTransferFunctionLabToMsh(lab2, msh2);
178
179 // If the endpoints are distinct saturated colors, then place white in between
180 // them.
181 if ( (msh1[1] > 0.05) && (msh2[1] > 0.05)
182 && (vtkColorTransferFunctionAngleDiff(msh1[2], msh2[2]) > 0.33*vtkMath::Pi()) )
183 {
184 // Insert the white midpoint by setting one end to white and adjusting the
185 // scalar value.
186 double Mmid = MY_MAX(msh1[0], msh2[0]);
187 Mmid = MY_MAX(88.0, Mmid);
188 if (s < 0.5)
189 {
190 msh2[0] = Mmid; msh2[1] = 0.0; msh2[2] = 0.0;
191 s = 2.0*s;
192 }
193 else
194 {
195 msh1[0] = Mmid; msh1[1] = 0.0; msh1[2] = 0.0;
196 s = 2.0*s - 1.0;
197 }
198 }
199
200 // If one color has no saturation, then its hue value is invalid. In this
201 // case, we want to set it to something logical so that the interpolation of
202 // hue makes sense.
203 if ((msh1[1] < 0.05) && (msh2[1] > 0.05))
204 {
205 msh1[2] = vtkColorTransferFunctionAdjustHue(msh2, msh1[0]);
206 }
207 else if ((msh2[1] < 0.05) && (msh1[1] > 0.05))
208 {
209 msh2[2] = vtkColorTransferFunctionAdjustHue(msh1, msh2[0]);
210 }
211
212 double mshTmp[3];
213 mshTmp[0] = (1-s)*msh1[0] + s*msh2[0];
214 mshTmp[1] = (1-s)*msh1[1] + s*msh2[1];
215 mshTmp[2] = (1-s)*msh1[2] + s*msh2[2];
216
217 // Now convert back to RGB
218 double labTmp[3];
219 vtkColorTransferFunctionMshToLab(mshTmp, labTmp);
220 vtkMath::LabToRGB(labTmp, result);
221 }
222
223 //----------------------------------------------------------------------------
224 // Construct a new vtkColorTransferFunction with default values
vtkColorTransferFunction()225 vtkColorTransferFunction::vtkColorTransferFunction()
226 {
227 this->UnsignedCharRGBAValue[0] = 0;
228 this->UnsignedCharRGBAValue[1] = 0;
229 this->UnsignedCharRGBAValue[2] = 0;
230 this->UnsignedCharRGBAValue[3] = 0;
231
232 this->Range[0] = 0;
233 this->Range[1] = 0;
234
235 this->Clamping = 1;
236 this->ColorSpace = VTK_CTF_RGB;
237 this->HSVWrap = 1; //By default HSV will be wrap
238
239 this->Scale = VTK_CTF_LINEAR;
240
241 this->NanColor[0] = 0.5;
242 this->NanColor[1] = 0.0;
243 this->NanColor[2] = 0.0;
244
245 this->BelowRangeColor[0] = 0.0;
246 this->BelowRangeColor[1] = 0.0;
247 this->BelowRangeColor[2] = 0.0;
248
249 this->UseBelowRangeColor = 0;
250
251 this->AboveRangeColor[0] = 1.0;
252 this->AboveRangeColor[1] = 1.0;
253 this->AboveRangeColor[2] = 1.0;
254
255 this->UseAboveRangeColor = 0;
256
257 this->Function = NULL;
258
259 this->Table = NULL;
260 this->TableSize = 0;
261
262 this->AllowDuplicateScalars = 0;
263
264 this->Internal = new vtkColorTransferFunctionInternals;
265 }
266
267 //----------------------------------------------------------------------------
268 // Destruct a vtkColorTransferFunction
~vtkColorTransferFunction()269 vtkColorTransferFunction::~vtkColorTransferFunction()
270 {
271 delete [] this->Table;
272
273 delete [] this->Function;
274 this->Function = NULL;
275
276 for(unsigned int i=0;i<this->Internal->Nodes.size();i++)
277 {
278 delete this->Internal->Nodes[i];
279 }
280 this->Internal->Nodes.clear();
281 delete this->Internal;
282 }
283
284 // Return the number of points which specify this function
GetSize()285 int vtkColorTransferFunction::GetSize()
286 {
287 return static_cast<int>(this->Internal->Nodes.size());
288 }
289
290 // Since we no longer store the data in an array, we must
291 // copy out of the vector into an array. No modified check -
292 // could be added if performance is a problem
GetDataPointer()293 double *vtkColorTransferFunction::GetDataPointer()
294 {
295 int size = static_cast<int>(this->Internal->Nodes.size());
296
297 delete [] this->Function;
298 this->Function = NULL;
299
300 if ( size > 0 )
301 {
302 this->Function = new double[size*4];
303 for ( int i = 0; i < size; i++ )
304 {
305 this->Function[4*i ] = this->Internal->Nodes[i]->X;
306 this->Function[4*i+1] = this->Internal->Nodes[i]->R;
307 this->Function[4*i+2] = this->Internal->Nodes[i]->G;
308 this->Function[4*i+3] = this->Internal->Nodes[i]->B;
309 }
310 }
311
312 return this->Function;
313 }
314
315 //----------------------------------------------------------------------------
316 // Add a point defined in RGB
AddRGBPoint(double x,double r,double g,double b)317 int vtkColorTransferFunction::AddRGBPoint( double x, double r,
318 double g, double b )
319 {
320 return this->AddRGBPoint( x, r, g, b, 0.5, 0.0 );
321 }
322
323 //----------------------------------------------------------------------------
324 // Add a point defined in RGB
AddRGBPoint(double x,double r,double g,double b,double midpoint,double sharpness)325 int vtkColorTransferFunction::AddRGBPoint( double x, double r,
326 double g, double b,
327 double midpoint,
328 double sharpness )
329 {
330 // Error check
331 if ( midpoint < 0.0 || midpoint > 1.0 )
332 {
333 vtkErrorMacro("Midpoint outside range [0.0, 1.0]");
334 return -1;
335 }
336
337 if ( sharpness < 0.0 || sharpness > 1.0 )
338 {
339 vtkErrorMacro("Sharpness outside range [0.0, 1.0]");
340 return -1;
341 }
342
343 // remove any node already at this X location
344 if (!this->AllowDuplicateScalars)
345 {
346 this->RemovePoint( x );
347 }
348
349 // Create the new node
350 vtkCTFNode *node = new vtkCTFNode;
351 node->X = x;
352 node->R = r;
353 node->G = g;
354 node->B = b;
355 node->Midpoint = midpoint;
356 node->Sharpness = sharpness;
357
358 // Add it, then sort to get everything in order
359 this->Internal->Nodes.push_back(node);
360 this->SortAndUpdateRange();
361
362 // We need to find the index of the node we just added in order
363 // to return this value
364 unsigned int i;
365 for ( i = 0; i < this->Internal->Nodes.size(); i++ )
366 {
367 if ( this->Internal->Nodes[i]->X == x )
368 {
369 break;
370 }
371 }
372
373 int retVal;
374
375 // If we didn't find it, something went horribly wrong so
376 // return -1
377 if ( i < this->Internal->Nodes.size() )
378 {
379 retVal = i;
380 }
381 else
382 {
383 retVal = -1;
384 }
385
386 return retVal;
387 }
388
389 //----------------------------------------------------------------------------
390 // Add a point defined in HSV
AddHSVPoint(double x,double h,double s,double v)391 int vtkColorTransferFunction::AddHSVPoint( double x, double h,
392 double s, double v )
393 {
394 double r, b, g;
395
396 vtkMath::HSVToRGB(h, s, v, &r, &g, &b);
397 return this->AddRGBPoint( x, r, g, b );
398 }
399
400 //----------------------------------------------------------------------------
401 // Add a point defined in HSV
AddHSVPoint(double x,double h,double s,double v,double midpoint,double sharpness)402 int vtkColorTransferFunction::AddHSVPoint( double x, double h,
403 double s, double v,
404 double midpoint,
405 double sharpness )
406 {
407 double r, b, g;
408
409 vtkMath::HSVToRGB(h, s, v, &r, &g, &b);
410 return this->AddRGBPoint( x, r, g, b, midpoint, sharpness );
411 }
412
413 //----------------------------------------------------------------------------
414 // Sort the vector in increasing order, then fill in
415 // the Range
SortAndUpdateRange()416 void vtkColorTransferFunction::SortAndUpdateRange()
417 {
418 std::sort( this->Internal->Nodes.begin(),
419 this->Internal->Nodes.end(),
420 this->Internal->CompareNodes );
421 bool modifiedInvoked = this->UpdateRange();
422 // If range is updated, Modified() has been called, don't call it again.
423 if (!modifiedInvoked)
424 {
425 this->Modified();
426 }
427 }
428
429 //----------------------------------------------------------------------------
UpdateRange()430 bool vtkColorTransferFunction::UpdateRange()
431 {
432 double oldRange[2];
433 oldRange[0] = this->Range[0];
434 oldRange[1] = this->Range[1];
435
436 int size = static_cast<int>(this->Internal->Nodes.size());
437 if ( size )
438 {
439 this->Range[0] = this->Internal->Nodes[0]->X;
440 this->Range[1] = this->Internal->Nodes[size-1]->X;
441 }
442 else
443 {
444 this->Range[0] = 0;
445 this->Range[1] = 0;
446 }
447
448 // If the range is the same, then no need to call Modified()
449 if (oldRange[0] == this->Range[0] && oldRange[1] == this->Range[1])
450 {
451 return false;
452 }
453
454 this->Modified();
455 return true;
456 }
457
458 //----------------------------------------------------------------------------
459 // Remove a point
RemovePoint(double x)460 int vtkColorTransferFunction::RemovePoint( double x )
461 {
462 // First find the node since we need to know its
463 // index as our return value
464 unsigned int i;
465 for ( i = 0; i < this->Internal->Nodes.size(); i++ )
466 {
467 if ( this->Internal->Nodes[i]->X == x )
468 {
469 break;
470 }
471 }
472
473 int retVal;
474
475 // If the node doesn't exist, we return -1
476 if ( i < this->Internal->Nodes.size() )
477 {
478 retVal = i;
479 }
480 else
481 {
482 return -1;
483 }
484
485 // Now use STL to find it, so that we can remove it
486 this->Internal->FindNodeEqual.X = x;
487
488 std::vector<vtkCTFNode*>::iterator iter =
489 std::find_if(this->Internal->Nodes.begin(),
490 this->Internal->Nodes.end(),
491 this->Internal->FindNodeEqual );
492
493 // Actually delete it
494 if ( iter != this->Internal->Nodes.end() )
495 {
496 delete *iter;
497 this->Internal->Nodes.erase(iter);
498 // If the first or last point has been removed, then we update the range
499 // No need to sort here as the order of points hasn't changed.
500 bool modifiedInvoked = false;
501 if (i == 0 || i == this->Internal->Nodes.size())
502 {
503 modifiedInvoked = this->UpdateRange();
504 }
505 if (!modifiedInvoked)
506 {
507 this->Modified();
508 }
509 }
510 else
511 {
512 // This should never happen - we already returned if the node
513 // didn't exist...
514 return -1;
515 }
516
517
518 return retVal;
519 }
520
521
522 //----------------------------------------------------------------------------
MovePoint(double oldX,double newX)523 void vtkColorTransferFunction::MovePoint(double oldX, double newX)
524 {
525 if (oldX == newX)
526 {
527 // Nothing to do.
528 return;
529 }
530
531 this->RemovePoint(newX);
532 for (unsigned int i = 0; i < this->Internal->Nodes.size(); i++ )
533 {
534 if ( this->Internal->Nodes[i]->X == oldX )
535 {
536 this->Internal->Nodes[i]->X = newX;
537 this->SortAndUpdateRange();
538 break;
539 }
540 }
541 }
542
543 //----------------------------------------------------------------------------
544 // Remove all points
RemoveAllPoints()545 void vtkColorTransferFunction::RemoveAllPoints()
546 {
547 for(unsigned int i=0;i<this->Internal->Nodes.size();i++)
548 {
549 delete this->Internal->Nodes[i];
550 }
551 this->Internal->Nodes.clear();
552
553 this->SortAndUpdateRange();
554 }
555
556 //----------------------------------------------------------------------------
557 // Add a line defined in RGB
AddRGBSegment(double x1,double r1,double g1,double b1,double x2,double r2,double g2,double b2)558 void vtkColorTransferFunction::AddRGBSegment( double x1, double r1,
559 double g1, double b1,
560 double x2, double r2,
561 double g2, double b2 )
562 {
563 int done;
564
565 // First, find all points in this range and remove them
566 done = 0;
567 while ( !done )
568 {
569 done = 1;
570
571 this->Internal->FindNodeInRange.X1 = x1;
572 this->Internal->FindNodeInRange.X2 = x2;
573
574 std::vector<vtkCTFNode*>::iterator iter =
575 std::find_if(this->Internal->Nodes.begin(),
576 this->Internal->Nodes.end(),
577 this->Internal->FindNodeInRange );
578
579 if ( iter != this->Internal->Nodes.end() )
580 {
581 delete *iter;
582 this->Internal->Nodes.erase(iter);
583 this->Modified();
584 done = 0;
585 }
586 }
587
588 // Now add the points
589 this->AddRGBPoint( x1, r1, g1, b1, 0.5, 0.0 );
590 this->AddRGBPoint( x2, r2, g2, b2, 0.5, 0.0 );
591 }
592
593 //----------------------------------------------------------------------------
594 // Add a line defined in HSV
AddHSVSegment(double x1,double h1,double s1,double v1,double x2,double h2,double s2,double v2)595 void vtkColorTransferFunction::AddHSVSegment( double x1, double h1,
596 double s1, double v1,
597 double x2, double h2,
598 double s2, double v2 )
599 {
600 double r1, r2, b1, b2, g1, g2;
601
602 vtkMath::HSVToRGB(h1, s1, v1, &r1, &g1, &b1);
603 vtkMath::HSVToRGB(h2, s2, v2, &r2, &g2, &b2);
604 this->AddRGBSegment( x1, r1, g1, b1, x2, r2, g2, b2 );
605 }
606
607 //----------------------------------------------------------------------------
608 // Returns the RGBA color evaluated at the specified location
MapValue(double x)609 unsigned char *vtkColorTransferFunction::MapValue( double x )
610 {
611 double rgb[3];
612 this->GetColor( x, rgb );
613
614 this->UnsignedCharRGBAValue[0] =
615 static_cast<unsigned char>(255.0*rgb[0] + 0.5);
616 this->UnsignedCharRGBAValue[1] =
617 static_cast<unsigned char>(255.0*rgb[1] + 0.5);
618 this->UnsignedCharRGBAValue[2] =
619 static_cast<unsigned char>(255.0*rgb[2] + 0.5);
620 this->UnsignedCharRGBAValue[3] = 255;
621 return this->UnsignedCharRGBAValue;
622 }
623
624 //----------------------------------------------------------------------------
625 // Returns the RGB color evaluated at the specified location
GetColor(double x,double rgb[3])626 void vtkColorTransferFunction::GetColor(double x, double rgb[3])
627 {
628 if ( this->IndexedLookup )
629 {
630 int numNodes = this->GetSize();
631 vtkVariant xv( x );
632 vtkIdType idx = this->GetAnnotatedValueIndexInternal( xv );
633 if ( idx < 0 || numNodes == 0 )
634 {
635 this->GetNanColor( rgb );
636 }
637 else
638 {
639 double nodeVal[6];
640 this->GetNodeValue( idx % numNodes, nodeVal );
641 for ( int i = 0; i < 3; ++ i )
642 rgb[i] = nodeVal[i + 1];
643 }
644 return;
645 }
646 this->GetTable( x, x, 1, rgb );
647 }
648
649 //----------------------------------------------------------------------------
650 // Returns the red color evaluated at the specified location
GetRedValue(double x)651 double vtkColorTransferFunction::GetRedValue( double x )
652 {
653 double rgb[3];
654 this->GetColor( x, rgb );
655
656 return rgb[0];
657 }
658
659 //----------------------------------------------------------------------------
660 // Returns the green color evaluated at the specified location
GetGreenValue(double x)661 double vtkColorTransferFunction::GetGreenValue( double x )
662 {
663 double rgb[3];
664 this->GetColor( x, rgb );
665
666 return rgb[1];
667 }
668
669 //----------------------------------------------------------------------------
670 // Returns the blue color evaluated at the specified location
GetBlueValue(double x)671 double vtkColorTransferFunction::GetBlueValue( double x )
672 {
673 double rgb[3];
674 this->GetColor( x, rgb );
675
676 return rgb[2];
677 }
678
679 //----------------------------------------------------------------------------
680 // Returns a table of RGB colors at regular intervals along the function
GetTable(double xStart,double xEnd,int size,double * table)681 void vtkColorTransferFunction::GetTable( double xStart, double xEnd,
682 int size, double* table )
683 {
684 int i, j;
685
686 // Special case: If either the start or end is a NaN, then all any
687 // interpolation done on them is also a NaN. Therefore, fill the table with
688 // the NaN color.
689 if (vtkMath::IsNan(xStart) || vtkMath::IsNan(xEnd))
690 {
691 double *tableEntry = table;
692 for (i = 0; i < size; i++)
693 {
694 tableEntry[0] = this->NanColor[0];
695 tableEntry[1] = this->NanColor[1];
696 tableEntry[2] = this->NanColor[2];
697 tableEntry += 3;
698 }
699 return;
700 }
701
702 int idx = 0;
703 int numNodes = static_cast<int>(this->Internal->Nodes.size());
704
705 // Need to keep track of the last value so that
706 // we can fill in table locations past this with
707 // this value if Clamping is On.
708 double lastR = 0.0;
709 double lastG = 0.0;
710 double lastB = 0.0;
711 if ( numNodes != 0 )
712 {
713 lastR = this->Internal->Nodes[numNodes-1]->R;
714 lastG = this->Internal->Nodes[numNodes-1]->G;
715 lastB = this->Internal->Nodes[numNodes-1]->B;
716 }
717
718 double *tptr = NULL;
719 double x = 0.0;
720 double x1 = 0.0;
721 double x2 = 0.0;
722 double rgb1[3] = {0.0, 0.0, 0.0};
723 double rgb2[3] = {0.0, 0.0, 0.0};
724 double midpoint = 0.0;
725 double sharpness = 0.0;
726
727 // If the scale is logarithmic, make sure the range is valid.
728 bool usingLogScale = this->Scale == VTK_CTF_LOG10;
729 if(usingLogScale)
730 {
731 // Note: This requires range[0] <= range[1].
732 usingLogScale = this->Range[0] > 0.0;
733 }
734
735 double logStart = 0.0;
736 double logEnd = 0.0;
737 double logX = 0.0;
738 if(usingLogScale)
739 {
740 logStart = log10(xStart);
741 logEnd = log10(xEnd);
742 }
743
744 // For each table entry
745 for ( i = 0; i < size; i++ )
746 {
747
748 // Find our location in the table
749 tptr = table + 3*i;
750
751 // Find our X location. If we are taking only 1 sample, make
752 // it halfway between start and end (usually start and end will
753 // be the same in this case)
754 if ( size > 1 )
755 {
756 if(usingLogScale)
757 {
758 logX = logStart +
759 (static_cast<double>(i)/static_cast<double>(size-1))
760 *(logEnd-logStart);
761 x = pow(static_cast<double>(10.0), logX);
762 }
763 else
764 {
765 x = xStart + (static_cast<double>(i)/static_cast<double>(size-1))
766 *(xEnd-xStart);
767 }
768 }
769 else
770 {
771 if(usingLogScale)
772 {
773 logX = 0.5*(logStart+logEnd);
774 x = pow(static_cast<double>(10.0), logX);
775 }
776 else
777 {
778 x = 0.5*(xStart+xEnd);
779 }
780 }
781
782 // Do we need to move to the next node?
783 while ( idx < numNodes &&
784 x > this->Internal->Nodes[idx]->X )
785 {
786 idx++;
787 // If we are at a valid point index, fill in
788 // the value at this node, and the one before (the
789 // two that surround our current sample location)
790 // idx cannot be 0 since we just incremented it.
791 if ( idx < numNodes )
792 {
793 x1 = this->Internal->Nodes[idx-1]->X;
794 x2 = this->Internal->Nodes[idx ]->X;
795 if(usingLogScale)
796 {
797 x1 = log10(x1);
798 x2 = log10(x2);
799 }
800
801 rgb1[0] = this->Internal->Nodes[idx-1]->R;
802 rgb2[0] = this->Internal->Nodes[idx ]->R;
803
804 rgb1[1] = this->Internal->Nodes[idx-1]->G;
805 rgb2[1] = this->Internal->Nodes[idx ]->G;
806
807 rgb1[2] = this->Internal->Nodes[idx-1]->B;
808 rgb2[2] = this->Internal->Nodes[idx ]->B;
809
810 // We only need the previous midpoint and sharpness
811 // since these control this region
812 midpoint = this->Internal->Nodes[idx-1]->Midpoint;
813 sharpness = this->Internal->Nodes[idx-1]->Sharpness;
814
815 // Move midpoint away from extreme ends of range to avoid
816 // degenerate math
817 if ( midpoint < 0.00001 )
818 {
819 midpoint = 0.00001;
820 }
821
822 if ( midpoint > 0.99999 )
823 {
824 midpoint = 0.99999;
825 }
826 }
827 }
828
829 // Are we at or past the end? If so, just use the last value
830 if ( x > this->Range[1])
831 {
832 if (this->Clamping)
833 {
834 if (this->GetUseAboveRangeColor())
835 {
836 this->GetAboveRangeColor(tptr);
837 }
838 else
839 {
840 tptr[0] = lastR;
841 tptr[1] = lastG;
842 tptr[2] = lastB;
843 }
844 }
845 else
846 {
847 tptr[0] = 0.0;
848 tptr[1] = 0.0;
849 tptr[2] = 0.0;
850 }
851 }
852 // Are we before the first node? If so, duplicate this node's values.
853 // We have to deal with -inf here
854 else if (x < this->Range[0] || (vtkMath::IsInf(x) && x < 0))
855 {
856 if (this->Clamping)
857 {
858 if (this->GetUseBelowRangeColor())
859 {
860 this->GetBelowRangeColor(tptr);
861 }
862 else
863 {
864 tptr[0] = this->Internal->Nodes[0]->R;
865 tptr[1] = this->Internal->Nodes[0]->G;
866 tptr[2] = this->Internal->Nodes[0]->B;
867 }
868 }
869 else
870 {
871 tptr[0] = 0.0;
872 tptr[1] = 0.0;
873 tptr[2] = 0.0;
874 }
875 }
876 else if (idx == 0 && std::fabs(x - xStart) < 1e-6)
877 {
878 tptr[0] = this->Internal->Nodes[0]->R;
879 tptr[1] = this->Internal->Nodes[0]->G;
880 tptr[2] = this->Internal->Nodes[0]->B;
881 }
882 // Otherwise, we are between two nodes - interpolate
883 else
884 {
885 // Our first attempt at a normalized location [0,1] -
886 // we will be modifying this based on midpoint and
887 // sharpness to get the curve shape we want and to have
888 // it pass through (y1+y2)/2 at the midpoint.
889 double s = 0.0;
890 if(usingLogScale)
891 {
892 s = (logX - x1) / (x2 - x1);
893 }
894 else
895 {
896 s = (x - x1) / (x2 - x1);
897 }
898
899 // Readjust based on the midpoint - linear adjustment
900 if ( s < midpoint )
901 {
902 s = 0.5 * s / midpoint;
903 }
904 else
905 {
906 s = 0.5 + 0.5*(s-midpoint)/(1.0-midpoint);
907 }
908
909 // override for sharpness > 0.99
910 // In this case we just want piecewise constant
911 if ( sharpness > 0.99 )
912 {
913 // Use the first value since we are below the midpoint
914 if ( s < 0.5 )
915 {
916 tptr[0] = rgb1[0];
917 tptr[1] = rgb1[1];
918 tptr[2] = rgb1[2];
919 continue;
920 }
921 // Use the second value at or above the midpoint
922 else
923 {
924 tptr[0] = rgb2[0];
925 tptr[1] = rgb2[1];
926 tptr[2] = rgb2[2];
927 continue;
928 }
929 }
930
931 // Override for sharpness < 0.01
932 // In this case we want piecewise linear
933 if ( sharpness < 0.01 )
934 {
935 // Simple linear interpolation
936 if ( this->ColorSpace == VTK_CTF_RGB )
937 {
938 tptr[0] = (1-s)*rgb1[0] + s*rgb2[0];
939 tptr[1] = (1-s)*rgb1[1] + s*rgb2[1];
940 tptr[2] = (1-s)*rgb1[2] + s*rgb2[2];
941 }
942 else if ( this->ColorSpace == VTK_CTF_HSV )
943 {
944 double hsv1[3], hsv2[3];
945 vtkMath::RGBToHSV(rgb1, hsv1);
946 vtkMath::RGBToHSV(rgb2, hsv2);
947
948 if ( this->HSVWrap &&
949 (hsv1[0] - hsv2[0] > 0.5 ||
950 hsv2[0] - hsv1[0] > 0.5) )
951 {
952 if ( hsv1[0] > hsv2[0] )
953 {
954 hsv1[0] -= 1.0;
955 }
956 else
957 {
958 hsv2[0] -= 1.0;
959 }
960 }
961
962 double hsvTmp[3];
963 hsvTmp[0] = (1-s)*hsv1[0] + s*hsv2[0];
964 if ( hsvTmp[0] < 0.0 )
965 {
966 hsvTmp[0] += 1.0;
967 }
968 hsvTmp[1] = (1-s)*hsv1[1] + s*hsv2[1];
969 hsvTmp[2] = (1-s)*hsv1[2] + s*hsv2[2];
970
971 // Now convert this back to RGB
972 vtkMath::HSVToRGB( hsvTmp, tptr );
973 }
974 else if (this->ColorSpace == VTK_CTF_LAB)
975 {
976 double lab1[3], lab2[3];
977 vtkMath::RGBToLab(rgb1, lab1);
978 vtkMath::RGBToLab(rgb2, lab2);
979
980 double labTmp[3];
981 labTmp[0] = (1-s)*lab1[0] + s*lab2[0];
982 labTmp[1] = (1-s)*lab1[1] + s*lab2[1];
983 labTmp[2] = (1-s)*lab1[2] + s*lab2[2];
984
985 // Now convert back to RGB
986 vtkMath::LabToRGB(labTmp, tptr);
987 }
988 else if (this->ColorSpace == VTK_CTF_DIVERGING)
989 {
990 vtkColorTransferFunctionInterpolateDiverging(s, rgb1, rgb2, tptr);
991 }
992 else
993 {
994 vtkErrorMacro("ColorSpace set to invalid value.");
995 }
996 continue;
997 }
998
999 // We have a sharpness between [0.01, 0.99] - we will
1000 // used a modified hermite curve interpolation where we
1001 // derive the slope based on the sharpness, and we compress
1002 // the curve non-linearly based on the sharpness
1003
1004 // First, we will adjust our position based on sharpness in
1005 // order to make the curve sharper (closer to piecewise constant)
1006 if ( s < .5 )
1007 {
1008 s = 0.5 * pow(s*2,1.0 + 10*sharpness);
1009 }
1010 else if ( s > .5 )
1011 {
1012 s = 1.0 - 0.5 * pow((1.0-s)*2,1+10*sharpness);
1013 }
1014
1015 // Compute some coefficients we will need for the hermite curve
1016 double ss = s*s;
1017 double sss = ss*s;
1018
1019 double h1 = 2*sss - 3*ss + 1;
1020 double h2 = -2*sss + 3*ss;
1021 double h3 = sss - 2*ss + s;
1022 double h4 = sss - ss;
1023
1024 double slope;
1025 double t;
1026
1027 if ( this->ColorSpace == VTK_CTF_RGB )
1028 {
1029 for ( j = 0; j < 3; j++ )
1030 {
1031 // Use one slope for both end points
1032 slope = rgb2[j] - rgb1[j];
1033 t = (1.0 - sharpness)*slope;
1034
1035 // Compute the value
1036 tptr[j] = h1*rgb1[j] + h2*rgb2[j] + h3*t + h4*t;
1037 }
1038 }
1039 else if (this->ColorSpace == VTK_CTF_HSV)
1040 {
1041 double hsv1[3], hsv2[3];
1042 vtkMath::RGBToHSV(rgb1, hsv1);
1043 vtkMath::RGBToHSV(rgb2, hsv2);
1044
1045 if ( this->HSVWrap &&
1046 (hsv1[0] - hsv2[0] > 0.5 ||
1047 hsv2[0] - hsv1[0] > 0.5) )
1048 {
1049 if ( hsv1[0] > hsv2[0] )
1050 {
1051 hsv1[0] -= 1.0;
1052 }
1053 else
1054 {
1055 hsv2[0] -= 1.0;
1056 }
1057 }
1058
1059 double hsvTmp[3];
1060
1061 for ( j = 0; j < 3; j++ )
1062 {
1063 // Use one slope for both end points
1064 slope = hsv2[j] - hsv1[j];
1065 t = (1.0 - sharpness)*slope;
1066
1067 // Compute the value
1068 hsvTmp[j] = h1*hsv1[j] + h2*hsv2[j] + h3*t + h4*t;
1069 if ( j == 0 && hsvTmp[j] < 0.0 )
1070 {
1071 hsvTmp[j] += 1.0;
1072 }
1073 }
1074 // Now convert this back to RGB
1075 vtkMath::HSVToRGB( hsvTmp, tptr );
1076 }
1077 else if (this->ColorSpace == VTK_CTF_LAB)
1078 {
1079 double lab1[3], lab2[3];
1080 vtkMath::RGBToLab(rgb1, lab1);
1081 vtkMath::RGBToLab(rgb2, lab2);
1082
1083 double labTmp[3];
1084 for (j = 0; j < 3; j++)
1085 {
1086 // Use one slope for both end points
1087 slope = lab2[j] - lab1[j];
1088 t = (1.0 - sharpness)*slope;
1089
1090 // Compute the value
1091 labTmp[j] = h1*lab1[j] + h2*lab2[j] + h3*t + h4*t;
1092 }
1093 // Now convert this back to RGB
1094 vtkMath::LabToRGB(labTmp, tptr);
1095 }
1096 else if (this->ColorSpace == VTK_CTF_DIVERGING)
1097 {
1098 // I have not implemented proper interpolation by a hermite curve for
1099 // the diverging color map, but I cannot think of a good use case for
1100 // that anyway.
1101 vtkColorTransferFunctionInterpolateDiverging(s, rgb1, rgb2, tptr);
1102 }
1103 else
1104 {
1105 vtkErrorMacro("ColorSpace set to invalid value.");
1106 }
1107
1108 // Final error check to make sure we don't go outside [0,1]
1109 for ( j = 0; j < 3; j++ )
1110 {
1111 tptr[j] = (tptr[j] < 0.0)?(0.0):(tptr[j]);
1112 tptr[j] = (tptr[j] > 1.0)?(1.0):(tptr[j]);
1113 }
1114 }
1115 }
1116 }
1117
1118 //----------------------------------------------------------------------------
GetTable(double xStart,double xEnd,int size,float * table)1119 void vtkColorTransferFunction::GetTable( double xStart, double xEnd,
1120 int size, float* table )
1121 {
1122 double *tmpTable = new double [size*3];
1123
1124 this->GetTable( xStart, xEnd, size, tmpTable );
1125
1126 double *tmpPtr = tmpTable;
1127 float *tPtr = table;
1128
1129 for ( int i = 0; i < size*3; i++ )
1130 {
1131 *tPtr = static_cast<float>(*tmpPtr);
1132 tPtr ++;
1133 tmpPtr ++;
1134 }
1135
1136 delete[] tmpTable;
1137 }
1138
1139 //----------------------------------------------------------------------------
GetTable(double xStart,double xEnd,int size)1140 const unsigned char *vtkColorTransferFunction::GetTable( double xStart,
1141 double xEnd,
1142 int size)
1143 {
1144 if (this->GetMTime() <= this->BuildTime &&
1145 this->TableSize == size)
1146 {
1147 return this->Table;
1148 }
1149
1150 if ( this->Internal->Nodes.size() == 0 )
1151 {
1152 vtkErrorMacro(
1153 "Attempting to lookup a value with no points in the function");
1154 return this->Table;
1155 }
1156
1157 if (this->TableSize != size)
1158 {
1159 delete [] this->Table;
1160 this->Table = new unsigned char [size*3];
1161 this->TableSize = size;
1162 }
1163
1164 double *tmpTable = new double [size*3];
1165
1166 this->GetTable( xStart, xEnd, size, tmpTable );
1167
1168 double *tmpPtr = tmpTable;
1169 unsigned char *tPtr = this->Table;
1170
1171 for ( int i = 0; i < size*3; i++ )
1172 {
1173 *tPtr = static_cast<unsigned char>(*tmpPtr*255.0 + 0.5);
1174 tPtr ++;
1175 tmpPtr ++;
1176 }
1177
1178 delete[] tmpTable;
1179
1180 this->BuildTime.Modified();
1181
1182 return this->Table;
1183 }
1184
1185 //----------------------------------------------------------------------------
BuildFunctionFromTable(double xStart,double xEnd,int size,double * table)1186 void vtkColorTransferFunction::BuildFunctionFromTable(double xStart,
1187 double xEnd,
1188 int size,
1189 double *table)
1190 {
1191 double inc = 0.0;
1192 double *tptr = table;
1193
1194 this->RemoveAllPoints();
1195
1196 if( size > 1 )
1197 {
1198 inc = (xEnd-xStart)/static_cast<double>(size-1);
1199 }
1200
1201 int i;
1202 for (i=0; i < size; i++)
1203 {
1204 vtkCTFNode *node = new vtkCTFNode;
1205 node->X = xStart + inc*i;
1206 node->R = tptr[0];
1207 node->G = tptr[1];
1208 node->B = tptr[2];
1209 node->Sharpness = 0.0;
1210 node->Midpoint = 0.5;
1211
1212 this->Internal->Nodes.push_back(node);
1213 tptr += 3;
1214 }
1215
1216 this->SortAndUpdateRange();
1217 }
1218
1219 //----------------------------------------------------------------------------
1220 // For a specified index value, get the node parameters
GetNodeValue(int index,double val[6])1221 int vtkColorTransferFunction::GetNodeValue( int index, double val[6] )
1222 {
1223 int size = static_cast<int>(this->Internal->Nodes.size());
1224
1225 if ( index < 0 || index >= size )
1226 {
1227 vtkErrorMacro("Index out of range!");
1228 return -1;
1229 }
1230
1231 val[0] = this->Internal->Nodes[index]->X;
1232 val[1] = this->Internal->Nodes[index]->R;
1233 val[2] = this->Internal->Nodes[index]->G;
1234 val[3] = this->Internal->Nodes[index]->B;
1235 val[4] = this->Internal->Nodes[index]->Midpoint;
1236 val[5] = this->Internal->Nodes[index]->Sharpness;
1237
1238 return 1;
1239 }
1240
1241 //----------------------------------------------------------------------------
1242 // For a specified index value, get the node parameters
SetNodeValue(int index,double val[6])1243 int vtkColorTransferFunction::SetNodeValue( int index, double val[6] )
1244 {
1245 int size = static_cast<int>(this->Internal->Nodes.size());
1246
1247 if ( index < 0 || index >= size )
1248 {
1249 vtkErrorMacro("Index out of range!");
1250 return -1;
1251 }
1252
1253 double oldX = this->Internal->Nodes[index]->X;
1254 this->Internal->Nodes[index]->X = val[0];
1255 this->Internal->Nodes[index]->R = val[1];
1256 this->Internal->Nodes[index]->G = val[2];
1257 this->Internal->Nodes[index]->B = val[3];
1258 this->Internal->Nodes[index]->Midpoint = val[4];
1259 this->Internal->Nodes[index]->Sharpness = val[5];
1260
1261 if (oldX != val[0])
1262 {
1263 // The point has been moved, the order of points or the range might have
1264 // been modified.
1265 this->SortAndUpdateRange();
1266 // No need to call Modified() here because SortAndUpdateRange() has done it
1267 // already.
1268 }
1269 else
1270 {
1271 this->Modified();
1272 }
1273
1274 return 1;
1275 }
1276
1277 //----------------------------------------------------------------------------
DeepCopy(vtkScalarsToColors * o)1278 void vtkColorTransferFunction::DeepCopy( vtkScalarsToColors *o )
1279 {
1280 vtkColorTransferFunction *f = NULL;
1281 if (o)
1282 {
1283 this->Superclass::DeepCopy(o);
1284 f = vtkColorTransferFunction::SafeDownCast(o);
1285 }
1286
1287 if (f != NULL)
1288 {
1289 this->Clamping = f->Clamping;
1290 this->ColorSpace = f->ColorSpace;
1291 this->HSVWrap = f->HSVWrap;
1292 this->Scale = f->Scale;
1293
1294 int i;
1295 this->RemoveAllPoints();
1296 for ( i = 0; i < f->GetSize(); i++ )
1297 {
1298 double val[6];
1299 f->GetNodeValue(i, val);
1300 this->AddRGBPoint(val[0], val[1], val[2], val[3], val[4], val[5]);
1301 }
1302 this->Modified();
1303 }
1304 }
1305
1306 //----------------------------------------------------------------------------
ShallowCopy(vtkColorTransferFunction * f)1307 void vtkColorTransferFunction::ShallowCopy( vtkColorTransferFunction *f )
1308 {
1309 if (f != NULL)
1310 {
1311 this->Superclass::DeepCopy(f);
1312
1313 this->Clamping = f->Clamping;
1314 this->ColorSpace = f->ColorSpace;
1315 this->HSVWrap = f->HSVWrap;
1316 this->Scale = f->Scale;
1317
1318 int i;
1319 this->RemoveAllPoints();
1320 for ( i = 0; i < f->GetSize(); i++ )
1321 {
1322 double val[6];
1323 f->GetNodeValue(i, val);
1324 this->AddRGBPoint(val[0], val[1], val[2], val[3], val[4], val[5]);
1325 }
1326 this->Modified();
1327 }
1328 }
1329
1330 //----------------------------------------------------------------------------
1331 // Accelerate the mapping by copying the data in 32-bit chunks instead
1332 // of 8-bit chunks. The extra "long" argument is to help broken
1333 // compilers select the non-templates below for unsigned char
1334 // and unsigned short.
1335 template <class T>
vtkColorTransferFunctionMapData(vtkColorTransferFunction * self,T * input,unsigned char * output,int length,int inIncr,int outFormat,long)1336 void vtkColorTransferFunctionMapData(vtkColorTransferFunction* self,
1337 T* input,
1338 unsigned char* output,
1339 int length, int inIncr,
1340 int outFormat, long)
1341 {
1342 double x;
1343 int i = length;
1344 double rgb[3];
1345 unsigned char *optr = output;
1346 T *iptr = input;
1347 unsigned char alpha = static_cast<unsigned char>(self->GetAlpha()*255.0);
1348
1349 if(self->GetSize() == 0)
1350 {
1351 vtkGenericWarningMacro("Transfer Function Has No Points!");
1352 return;
1353 }
1354
1355 while (--i >= 0)
1356 {
1357 x = static_cast<double>(*iptr);
1358 self->GetColor(x, rgb);
1359
1360 if (outFormat == VTK_RGB || outFormat == VTK_RGBA)
1361 {
1362 *(optr++) = static_cast<unsigned char>(rgb[0]*255.0 + 0.5);
1363 *(optr++) = static_cast<unsigned char>(rgb[1]*255.0 + 0.5);
1364 *(optr++) = static_cast<unsigned char>(rgb[2]*255.0 + 0.5);
1365 }
1366 else // LUMINANCE use coeffs of (0.30 0.59 0.11)*255.0
1367 {
1368 *(optr++) = static_cast<unsigned char>(rgb[0]*76.5 + rgb[1]*150.45 +
1369 rgb[2]*28.05 + 0.5);
1370 }
1371
1372 if (outFormat == VTK_RGBA || outFormat == VTK_LUMINANCE_ALPHA)
1373 {
1374 *(optr++) = alpha;
1375 }
1376 iptr += inIncr;
1377 }
1378 }
1379
1380
1381
1382 //----------------------------------------------------------------------------
1383 // Special implementation for unsigned char input.
vtkColorTransferFunctionMapData(vtkColorTransferFunction * self,unsigned char * input,unsigned char * output,int length,int inIncr,int outFormat,int)1384 static void vtkColorTransferFunctionMapData(vtkColorTransferFunction* self,
1385 unsigned char* input,
1386 unsigned char* output,
1387 int length, int inIncr,
1388 int outFormat, int)
1389 {
1390 int x;
1391 int i = length;
1392 unsigned char *optr = output;
1393 unsigned char *iptr = input;
1394
1395 if(self->GetSize() == 0)
1396 {
1397 vtkGenericWarningMacro("Transfer Function Has No Points!");
1398 return;
1399 }
1400
1401 const unsigned char *table = self->GetTable(0,255,256);
1402 switch (outFormat)
1403 {
1404 case VTK_RGB:
1405 while (--i >= 0)
1406 {
1407 x = *iptr*3;
1408 *(optr++) = table[x];
1409 *(optr++) = table[x+1];
1410 *(optr++) = table[x+2];
1411 iptr += inIncr;
1412 }
1413 break;
1414 case VTK_RGBA:
1415 while (--i >= 0)
1416 {
1417 x = *iptr*3;
1418 *(optr++) = table[x];
1419 *(optr++) = table[x+1];
1420 *(optr++) = table[x+2];
1421 *(optr++) = 255;
1422 iptr += inIncr;
1423 }
1424 break;
1425 case VTK_LUMINANCE_ALPHA:
1426 while (--i >= 0)
1427 {
1428 x = *iptr*3;
1429 *(optr++) = table[x];
1430 *(optr++) = 255;
1431 iptr += inIncr;
1432 }
1433 break;
1434 case VTK_LUMINANCE:
1435 while (--i >= 0)
1436 {
1437 x = *iptr*3;
1438 *(optr++) = table[x];
1439 iptr += inIncr;
1440 }
1441 break;
1442 }
1443 }
1444
1445 //----------------------------------------------------------------------------
1446 // Special implementation for unsigned short input.
vtkColorTransferFunctionMapData(vtkColorTransferFunction * self,unsigned short * input,unsigned char * output,int length,int inIncr,int outFormat,int)1447 static void vtkColorTransferFunctionMapData(vtkColorTransferFunction* self,
1448 unsigned short* input,
1449 unsigned char* output,
1450 int length, int inIncr,
1451 int outFormat, int)
1452 {
1453 int x;
1454 int i = length;
1455 unsigned char *optr = output;
1456 unsigned short *iptr = input;
1457
1458 if(self->GetSize() == 0)
1459 {
1460 vtkGenericWarningMacro("Transfer Function Has No Points!");
1461 return;
1462 }
1463
1464
1465 const unsigned char *table = self->GetTable(0,65535,65536);
1466 switch (outFormat)
1467 {
1468 case VTK_RGB:
1469 while (--i >= 0)
1470 {
1471 x = *iptr*3;
1472 *(optr++) = table[x];
1473 *(optr++) = table[x+1];
1474 *(optr++) = table[x+2];
1475 iptr += inIncr;
1476 }
1477 break;
1478 case VTK_RGBA:
1479 while (--i >= 0)
1480 {
1481 x = *iptr*3;
1482 *(optr++) = table[x];
1483 *(optr++) = table[x+1];
1484 *(optr++) = table[x+2];
1485 *(optr++) = 255;
1486 iptr += inIncr;
1487 }
1488 break;
1489 case VTK_LUMINANCE_ALPHA:
1490 while (--i >= 0)
1491 {
1492 x = *iptr*3;
1493 *(optr++) = table[x];
1494 *(optr++) = 255;
1495 iptr += inIncr;
1496 }
1497 break;
1498 case VTK_LUMINANCE:
1499 while (--i >= 0)
1500 {
1501 x = *iptr*3;
1502 *(optr++) = table[x];
1503 iptr += inIncr;
1504 }
1505 break;
1506 }
1507 }
1508
1509 //----------------------------------------------------------------------------
1510 template<class T>
vtkColorTransferFunctionIndexedMapData(vtkColorTransferFunction * self,T * input,unsigned char * output,int length,int inIncr,int outFormat,long)1511 void vtkColorTransferFunctionIndexedMapData(
1512 vtkColorTransferFunction* self, T* input, unsigned char* output, int length,
1513 int inIncr, int outFormat, long )
1514 {
1515 int i = length;
1516 double nodeVal[6];
1517 double alpha;
1518 int numNodes = self->GetSize();
1519
1520 vtkVariant vin;
1521 if ( (alpha=self->GetAlpha()) >= 1.0 ) //no blending required
1522 {
1523 if (outFormat == VTK_RGBA)
1524 {
1525 while (--i >= 0)
1526 {
1527 vin = *input;
1528 vtkIdType idx = self->GetAnnotatedValueIndexInternal( vin );
1529 if ( idx < 0 || numNodes == 0 )
1530 self->GetNanColor( &nodeVal[1] );
1531 else
1532 self->GetNodeValue( idx % numNodes, nodeVal );
1533
1534 output[0] = static_cast<unsigned char>(255. * nodeVal[1]);
1535 output[1] = static_cast<unsigned char>(255. * nodeVal[2]);
1536 output[2] = static_cast<unsigned char>(255. * nodeVal[3]);
1537 output[3] = static_cast<unsigned char>(255.); // * nodeVal[3];
1538 input += inIncr;
1539 output += 4;
1540 }
1541 }
1542 else if (outFormat == VTK_RGB)
1543 {
1544 while (--i >= 0)
1545 {
1546 vin = *input;
1547 vtkIdType idx = self->GetAnnotatedValueIndexInternal( vin );
1548 if ( idx < 0 || numNodes == 0 )
1549 self->GetNanColor( &nodeVal[1] );
1550 else
1551 self->GetNodeValue( idx % numNodes, nodeVal );
1552
1553 output[0] = static_cast<unsigned char>(255. * nodeVal[1]);
1554 output[1] = static_cast<unsigned char>(255. * nodeVal[2]);
1555 output[2] = static_cast<unsigned char>(255. * nodeVal[3]);
1556 input += inIncr;
1557 output += 3;
1558 }
1559 }
1560 else if (outFormat == VTK_LUMINANCE_ALPHA)
1561 {
1562 while (--i >= 0)
1563 {
1564 vin = *input;
1565 vtkIdType idx = self->GetAnnotatedValueIndexInternal( vin );
1566 if ( idx < 0 || numNodes == 0 )
1567 self->GetNanColor( &nodeVal[1] );
1568 else
1569 self->GetNodeValue( idx % numNodes, nodeVal );
1570 output[0] = static_cast<unsigned char>(255. * nodeVal[1]*0.30 + 255. * nodeVal[2]*0.59 +
1571 255. * nodeVal[3]*0.11 + 0.5);
1572 output[1] = static_cast<unsigned char>(255. * nodeVal[3]);
1573 input += inIncr;
1574 output += 2;
1575 }
1576 }
1577 else // outFormat == VTK_LUMINANCE
1578 {
1579 while (--i >= 0)
1580 {
1581 vin = *input;
1582 vtkIdType idx = self->GetAnnotatedValueIndexInternal( vin );
1583 if ( idx < 0 || numNodes == 0 )
1584 self->GetNanColor( &nodeVal[1] );
1585 else
1586 self->GetNodeValue( idx % numNodes, nodeVal );
1587 *output++ = static_cast<unsigned char>(255. * nodeVal[1]*0.30 + 255. * nodeVal[2]*0.59 +
1588 255. * nodeVal[3]*0.11 + 0.5);
1589 input += inIncr;
1590 }
1591 }
1592 } // if blending not needed
1593
1594 else // blend with the specified alpha
1595 {
1596 if (outFormat == VTK_RGBA)
1597 {
1598 while (--i >= 0)
1599 {
1600 vin = *input;
1601 vtkIdType idx = self->GetAnnotatedValueIndexInternal( vin );
1602 if ( idx < 0 || numNodes == 0 )
1603 self->GetNanColor( &nodeVal[1] );
1604 else
1605 self->GetNodeValue( idx % numNodes, nodeVal );
1606 output[0] = static_cast<unsigned char>(255. * nodeVal[1]);
1607 output[1] = static_cast<unsigned char>(255. * nodeVal[2]);
1608 output[2] = static_cast<unsigned char>(255. * nodeVal[3]);
1609 output[3] = static_cast<unsigned char>(255. * /*nodeVal[3]*/alpha + 0.5);
1610 input += inIncr;
1611 output += 4;
1612 }
1613 }
1614 else if (outFormat == VTK_RGB)
1615 {
1616 while (--i >= 0)
1617 {
1618 vin = *input;
1619 vtkIdType idx = self->GetAnnotatedValueIndexInternal( vin );
1620 if ( idx < 0 || numNodes == 0 )
1621 self->GetNanColor( &nodeVal[1] );
1622 else
1623 self->GetNodeValue( idx % numNodes, nodeVal );
1624 output[0] = static_cast<unsigned char>(255. * nodeVal[1]);
1625 output[1] = static_cast<unsigned char>(255. * nodeVal[2]);
1626 output[2] = static_cast<unsigned char>(255. * nodeVal[3]);
1627 input += inIncr;
1628 output += 3;
1629 }
1630 }
1631 else if (outFormat == VTK_LUMINANCE_ALPHA)
1632 {
1633 while (--i >= 0)
1634 {
1635 vin = *input;
1636 vtkIdType idx = self->GetAnnotatedValueIndexInternal( vin );
1637 if ( idx < 0 || numNodes == 0 )
1638 self->GetNanColor( &nodeVal[1] );
1639 else
1640 self->GetNodeValue( idx % numNodes, nodeVal );
1641 output[0] = static_cast<unsigned char>(255. * nodeVal[1]*0.30 + 255. * nodeVal[2]*0.59 +
1642 255. * nodeVal[3]*0.11 + 0.5);
1643 output[1] = static_cast<unsigned char>(255. * /*nodeVal[3]*/alpha + 0.5);
1644 input += inIncr;
1645 output += 2;
1646 }
1647 }
1648 else // outFormat == VTK_LUMINANCE
1649 {
1650 while (--i >= 0)
1651 {
1652 vin = *input;
1653 vtkIdType idx = self->GetAnnotatedValueIndexInternal( vin );
1654 if ( idx < 0 || numNodes == 0 )
1655 self->GetNanColor( &nodeVal[1] );
1656 else
1657 self->GetNodeValue( idx % numNodes, nodeVal );
1658 *output++ = static_cast<unsigned char>(255. * nodeVal[1]*0.30 + 255. * nodeVal[2]*0.59 +
1659 255. * nodeVal[3]*0.11 + 0.5);
1660 input += inIncr;
1661 }
1662 }
1663 } // alpha blending
1664 }
1665
1666 //----------------------------------------------------------------------------
MapScalarsThroughTable2(void * input,unsigned char * output,int inputDataType,int numberOfValues,int inputIncrement,int outputFormat)1667 void vtkColorTransferFunction::MapScalarsThroughTable2(void *input,
1668 unsigned char *output,
1669 int inputDataType,
1670 int numberOfValues,
1671 int inputIncrement,
1672 int outputFormat)
1673 {
1674 if(this->GetSize() == 0)
1675 {
1676 vtkDebugMacro("Transfer Function Has No Points!");
1677 return;
1678 }
1679 if ( this->IndexedLookup )
1680 {
1681 switch (inputDataType)
1682 {
1683 // Use vtkExtendedTemplateMacro to cover case of VTK_STRING input
1684 vtkExtendedTemplateMacro(
1685 vtkColorTransferFunctionIndexedMapData(
1686 this, static_cast<VTK_TT*>(input),
1687 output, numberOfValues, inputIncrement,
1688 outputFormat, 1)
1689 );
1690
1691 default:
1692 vtkErrorMacro(<< "MapImageThroughTable: Unknown input ScalarType");
1693 return;
1694 }
1695 }
1696 else
1697 {
1698 switch (inputDataType)
1699 {
1700 vtkTemplateMacro(
1701 vtkColorTransferFunctionMapData(this, static_cast<VTK_TT*>(input),
1702 output, numberOfValues, inputIncrement,
1703 outputFormat, 1)
1704 );
1705 default:
1706 vtkErrorMacro(<< "MapImageThroughTable: Unknown input ScalarType");
1707 return;
1708 }
1709 }
1710 }
1711
1712 //----------------------------------------------------------------------------
GetNumberOfAvailableColors()1713 vtkIdType vtkColorTransferFunction::GetNumberOfAvailableColors()
1714 {
1715 if ( this->IndexedLookup && this->GetSize() )
1716 {
1717 return this->GetSize();
1718 }
1719 if(this->Table)
1720 {
1721 // Not sure if this is correct since it is only set if
1722 // "const unsigned char *::GetTable( double xStart, double xEnd,int size)"
1723 // has been called.
1724 return static_cast<vtkIdType>(this->TableSize);
1725 }
1726 return 16777216; //2^24
1727 }
1728
1729 //----------------------------------------------------------------------------
GetIndexedColor(vtkIdType idx,double rgba[4])1730 void vtkColorTransferFunction::GetIndexedColor(vtkIdType idx, double rgba[4])
1731 {
1732 vtkIdType n = this->GetSize();
1733 if (n > 0 && idx >= 0)
1734 {
1735 double nodeValue[6];
1736 this->GetNodeValue(idx % n, nodeValue);
1737 for (int j = 0; j < 3; ++j)
1738 {
1739 rgba[j] = nodeValue[j+1];
1740 }
1741 rgba[3] = 1.0; // NodeColor is RGB-only.
1742 return;
1743 }
1744 this->GetNanColor(rgba);
1745 rgba[3] = 1.0; // NanColor is RGB-only.
1746 }
1747
1748 //----------------------------------------------------------------------------
FillFromDataPointer(int nb,double * ptr)1749 void vtkColorTransferFunction::FillFromDataPointer(int nb, double *ptr)
1750 {
1751 if (nb <= 0 || !ptr)
1752 {
1753 return;
1754 }
1755
1756 this->RemoveAllPoints();
1757
1758 while (nb)
1759 {
1760 this->AddRGBPoint(ptr[0], ptr[1], ptr[2], ptr[3]);
1761 ptr += 4;
1762 nb--;
1763 }
1764 }
1765
1766 //----------------------------------------------------------------------------
AdjustRange(double range[2])1767 int vtkColorTransferFunction::AdjustRange(double range[2])
1768 {
1769 if (!range)
1770 {
1771 return 0;
1772 }
1773
1774 double *function_range = this->GetRange();
1775
1776 // Make sure we have points at each end of the range
1777
1778 double rgb[3];
1779 if (function_range[0] < range[0])
1780 {
1781 this->GetColor(range[0], rgb);
1782 this->AddRGBPoint(range[0], rgb[0], rgb[1], rgb[2]);
1783 }
1784 else
1785 {
1786 this->GetColor(function_range[0], rgb);
1787 this->AddRGBPoint(range[0], rgb[0], rgb[1], rgb[2]);
1788 }
1789
1790 if (function_range[1] > range[1])
1791 {
1792 this->GetColor(range[1], rgb);
1793 this->AddRGBPoint(range[1], rgb[0], rgb[1], rgb[2]);
1794 }
1795 else
1796 {
1797 this->GetColor(function_range[1], rgb);
1798 this->AddRGBPoint(range[1], rgb[0], rgb[1], rgb[2]);
1799 }
1800
1801 // Remove all points out-of-range
1802 int done;
1803
1804 done = 0;
1805 while ( !done )
1806 {
1807 done = 1;
1808
1809 this->Internal->FindNodeOutOfRange.X1 = range[0];
1810 this->Internal->FindNodeOutOfRange.X2 = range[1];
1811
1812 std::vector<vtkCTFNode*>::iterator iter =
1813 std::find_if(this->Internal->Nodes.begin(),
1814 this->Internal->Nodes.end(),
1815 this->Internal->FindNodeOutOfRange );
1816
1817 if ( iter != this->Internal->Nodes.end() )
1818 {
1819 delete *iter;
1820 this->Internal->Nodes.erase(iter);
1821 this->Modified();
1822 done = 0;
1823 }
1824 }
1825
1826 this->SortAndUpdateRange();
1827
1828
1829 return 1;
1830 }
1831
1832 //----------------------------------------------------------------------------
1833 // Print method for vtkColorTransferFunction
PrintSelf(ostream & os,vtkIndent indent)1834 void vtkColorTransferFunction::PrintSelf(ostream& os, vtkIndent indent)
1835 {
1836 this->Superclass::PrintSelf(os, indent);
1837
1838 os << indent << "Size: " << this->Internal->Nodes.size() << endl;
1839 if ( this->Clamping )
1840 {
1841 os << indent << "Clamping: On\n";
1842 }
1843 else
1844 {
1845 os << indent << "Clamping: Off\n";
1846 }
1847
1848 if ( this->ColorSpace == VTK_CTF_RGB )
1849 {
1850 os << indent << "Color Space: RGB\n";
1851 }
1852 else if ( this->ColorSpace == VTK_CTF_HSV && this->HSVWrap )
1853 {
1854 os << indent << "Color Space: HSV\n";
1855 }
1856 else if ( this->ColorSpace == VTK_CTF_HSV )
1857 {
1858 os << indent << "Color Space: HSV (No Wrap)\n";
1859 }
1860 else
1861 {
1862 os << indent << "Color Space: CIE-L*ab\n";
1863 }
1864
1865 if ( this->Scale == VTK_CTF_LOG10 )
1866 {
1867 os << indent << "Scale: Log10\n";
1868 }
1869 else
1870 {
1871 os << indent << "Scale: Linear\n";
1872 }
1873
1874 os << indent << "Range: " << this->Range[0] << " to "
1875 << this->Range[1] << endl;
1876
1877 os << indent << "AllowDuplicateScalars: " << this->AllowDuplicateScalars << endl;
1878
1879 os << indent << "NanColor: "
1880 << this->NanColor[0] << ", " << this->NanColor[1] << ", "
1881 << this->NanColor[2] << endl;
1882
1883 os << indent << "BelowRangeColor: (" << this->BelowRangeColor[0] << ", "
1884 << this->BelowRangeColor[1] << ", " << this->BelowRangeColor[2] << ")\n";
1885 os << indent << "UseBelowRangeColor: "
1886 << (this->UseBelowRangeColor != 0 ? "ON" : "OFF") << "\n";
1887
1888 os << indent << "ABoveRangeColor: (" << this->AboveRangeColor[0] << ", "
1889 << this->AboveRangeColor[1] << ", " << this->AboveRangeColor[2] << ")\n";
1890 os << indent << "UseAboveRangeColor: "
1891 << (this->UseAboveRangeColor != 0 ? "ON" : "OFF") << "\n";
1892
1893
1894 unsigned int i;
1895 for( i = 0; i < this->Internal->Nodes.size(); i++ )
1896 {
1897 os << indent << " " << i
1898 << " X: " << this->Internal->Nodes[i]->X
1899 << " R: " << this->Internal->Nodes[i]->R
1900 << " G: " << this->Internal->Nodes[i]->G
1901 << " B: " << this->Internal->Nodes[i]->B
1902 << " Sharpness: " << this->Internal->Nodes[i]->Sharpness
1903 << " Midpoint: " << this->Internal->Nodes[i]->Midpoint << endl;
1904 }
1905 }
1906
1907
1908
1909