Commit 16543376 authored by Utkarsh Ayachit's avatar Utkarsh Ayachit
Browse files

Cleanup logic for indexed colors.

vtkDiscretizableColorTransferFunction used control points to map indexed colors.
To make applications simpler, added a new API to add indexed colors which are
used when present when in IndexedLookup mode. If no IndexedColors are specified,
the vtkDiscretizableColorTransferFunction reverts to the previous behaviour for
backwards compatibility.

Change-Id: If5b6791baccf2ee0022fd1b1098093d226ef64c3
parent 37a10c60
......@@ -19,12 +19,22 @@
#include "vtkMath.h"
#include "vtkObjectFactory.h"
#include "vtkPiecewiseFunction.h"
#include "vtkTuple.h"
#include <vector>
class vtkDiscretizableColorTransferFunction::vtkInternals
{
public:
std::vector<vtkTuple<double, 3> > IndexedColors;
};
vtkStandardNewMacro(vtkDiscretizableColorTransferFunction);
vtkCxxSetObjectMacro(vtkDiscretizableColorTransferFunction,
ScalarOpacityFunction, vtkPiecewiseFunction);
//-----------------------------------------------------------------------------
vtkDiscretizableColorTransferFunction::vtkDiscretizableColorTransferFunction()
: Internals(new vtkInternals())
{
this->LookupTable = vtkLookupTable::New();
......@@ -47,13 +57,93 @@ vtkDiscretizableColorTransferFunction::~vtkDiscretizableColorTransferFunction()
this->SetScalarOpacityFunction(NULL);
this->LookupTable->Delete();
delete [] this->Data;
delete this->Internals;
this->Internals = NULL;
}
//-----------------------------------------------------------------------------
struct vtkDiscretizableColorTransferFunctionNode
unsigned long vtkDiscretizableColorTransferFunction::GetMTime()
{
double Value[6];
};
unsigned long mtime = this->Superclass::GetMTime();
if (this->ScalarOpacityFunction)
{
unsigned long somtime = this->ScalarOpacityFunction->GetMTime();
mtime = somtime > mtime? somtime : mtime;
}
if (this->LookupTable)
{
unsigned ltmtime = this->LookupTable->GetMTime();
mtime = ltmtime > mtime? ltmtime : mtime;
}
return mtime;
}
//-----------------------------------------------------------------------------
void vtkDiscretizableColorTransferFunction::SetNumberOfIndexedColors(
unsigned int count)
{
if (static_cast<unsigned int>(this->Internals->IndexedColors.size()) != count)
{
this->Internals->IndexedColors.resize(count);
this->Modified();
}
}
//-----------------------------------------------------------------------------
unsigned int vtkDiscretizableColorTransferFunction::GetNumberOfIndexedColors()
{
return static_cast<unsigned int>(this->Internals->IndexedColors.size());
}
//-----------------------------------------------------------------------------
void vtkDiscretizableColorTransferFunction::SetIndexedColor(
unsigned int index, double r, double g, double b)
{
if (static_cast<unsigned int>(this->Internals->IndexedColors.size()) <= index)
{
// resize and fill all new colors with the same color as specified.
size_t old_size = this->Internals->IndexedColors.size();
size_t new_size = static_cast<size_t>(index+1);
this->Internals->IndexedColors.resize(new_size);
for (size_t cc = old_size; cc < new_size; cc++)
{
double *data = this->Internals->IndexedColors[cc].GetData();
data[0] = r;
data[1] = g;
data[2] = b;
}
this->Modified();
}
else if (this->Internals->IndexedColors[index].GetData()[0] != r ||
this->Internals->IndexedColors[index].GetData()[1] != g ||
this->Internals->IndexedColors[index].GetData()[2] != b )
{
// color has changed, change it.
double *data = this->Internals->IndexedColors[index].GetData();
data[0] = r;
data[1] = g;
data[2] = b;
this->Modified();
}
}
//-----------------------------------------------------------------------------
void vtkDiscretizableColorTransferFunction::GetIndexedColor(vtkIdType i, double rgba[4])
{
if (this->IndexedLookup || this->Discretize)
{
this->LookupTable->GetIndexedColor(i, rgba);
}
else
{
this->Superclass::GetIndexedColor(i, rgba);
}
}
//-----------------------------------------------------------------------------
void vtkDiscretizableColorTransferFunction::SetUseLogScale(int useLogScale)
......@@ -76,14 +166,6 @@ void vtkDiscretizableColorTransferFunction::SetUseLogScale(int useLogScale)
}
}
//-----------------------------------------------------------------------------
void vtkDiscretizableColorTransferFunction::SetNumberOfValues(vtkIdType number)
{
this->NumberOfValues = number;
this->LookupTable->SetNumberOfTableValues(number);
this->Modified();
}
//-----------------------------------------------------------------------------
int vtkDiscretizableColorTransferFunction::IsOpaque()
{
......@@ -95,28 +177,55 @@ void vtkDiscretizableColorTransferFunction::Build()
{
this->Superclass::Build();
if (this->BuildTime > this->GetMTime())
{
// no need to rebuild anything.
return;
}
this->LookupTable->SetVectorMode(this->VectorMode);
this->LookupTable->SetVectorComponent(this->VectorComponent);
this->LookupTable->SetIndexedLookup(this->IndexedLookup);
this->LookupTable->SetAnnotations( this->AnnotatedValues, this->Annotations );
if ( this->IndexedLookup )
// this is essential since other the LookupTable doesn't update the
// annotations map. That's a bug in the implementation of
// vtkScalarsToColors::SetAnnotations(..,..);
this->LookupTable->SetAnnotations(NULL, NULL);
this->LookupTable->SetAnnotations(this->AnnotatedValues, this->Annotations);
if (this->IndexedLookup)
{
int nv = this->GetSize();
this->LookupTable->SetNumberOfTableValues( nv );
double nodeVal[6];
for ( int i = 0; i < nv; ++ i )
if (this->GetNumberOfIndexedColors() > 0)
{
this->GetNodeValue( i, nodeVal );
nodeVal[4] = 1.;
this->LookupTable->SetTableValue( i, &nodeVal[1] );
// Use the specified indexed-colors.
vtkIdType count = this->GetNumberOfAnnotatedValues();
this->LookupTable->SetNumberOfTableValues(count);
for (size_t cc=0; cc < this->Internals->IndexedColors.size() &&
cc < static_cast<size_t>(count); cc++)
{
double rgba[4];
rgba[0] = this->Internals->IndexedColors[cc].GetData()[0];
rgba[1] = this->Internals->IndexedColors[cc].GetData()[1];
rgba[2] = this->Internals->IndexedColors[cc].GetData()[2];
rgba[3] = 1.0;
this->LookupTable->SetTableValue(static_cast<int>(cc), rgba);
}
}
else
{
// old logic for backwards compatibility.
int nv = this->GetSize();
this->LookupTable->SetNumberOfTableValues( nv );
double nodeVal[6];
for ( int i = 0; i < nv; ++ i )
{
this->GetNodeValue( i, nodeVal );
nodeVal[4] = 1.;
this->LookupTable->SetTableValue( i, &nodeVal[1] );
}
}
return;
}
if (this->Discretize && (this->GetMTime() > this->BuildTime ||
(this->ScalarOpacityFunction.GetPointer() &&
this->ScalarOpacityFunction->GetMTime() > this->BuildTime)))
else if (this->Discretize)
{
// Do not omit the LookupTable->SetNumberOfTableValues call:
// WritePointer does not update the NumberOfColors ivar.
......@@ -127,7 +236,7 @@ void vtkDiscretizableColorTransferFunction::Build()
double range[2];
this->GetRange(range);
bool logRangeValid = true;
if(this->UseLogScale)
if (this->UseLogScale)
{
logRangeValid = range[0] > 0.0 || range[1] < 0.0;
if(!logRangeValid && this->LookupTable->GetScale() == VTK_SCALE_LOG10)
......@@ -153,9 +262,9 @@ void vtkDiscretizableColorTransferFunction::Build()
lut_ptr[4*cc+3] = 255;
}
delete [] table;
this->BuildTime.Modified();
}
this->BuildTime.Modified();
}
//-----------------------------------------------------------------------------
......@@ -177,26 +286,7 @@ void vtkDiscretizableColorTransferFunction::SetNanColor(
unsigned char* vtkDiscretizableColorTransferFunction::MapValue(double v)
{
this->Build();
if ( this->IndexedLookup )
{
vtkIdType idx = this->GetAnnotatedValueIndex( v );
if ( idx < 0 || this->GetSize() == 0 )
{
return this->Superclass::MapValue( vtkMath::Nan() );
}
double nodeValue[6];
this->GetNodeValue( idx % this->GetSize(), nodeValue );
this->UnsignedCharRGBAValue[0] =
static_cast<unsigned char>(255.0*nodeValue[1] + 0.5);
this->UnsignedCharRGBAValue[1] =
static_cast<unsigned char>(255.0*nodeValue[2] + 0.5);
this->UnsignedCharRGBAValue[2] =
static_cast<unsigned char>(255.0*nodeValue[3] + 0.5);
this->UnsignedCharRGBAValue[3] = 255;
return this->UnsignedCharRGBAValue;
}
if (this->Discretize)
if (this->Discretize || this->IndexedLookup)
{
return this->LookupTable->MapValue(v);
}
......@@ -208,13 +298,14 @@ unsigned char* vtkDiscretizableColorTransferFunction::MapValue(double v)
void vtkDiscretizableColorTransferFunction::GetColor(double v, double rgb[3])
{
this->Build();
if (this->Discretize)
if (this->Discretize || this->IndexedLookup)
{
this->LookupTable->GetColor(v, rgb);
return;
}
this->Superclass::GetColor(v, rgb);
else
{
this->Superclass::GetColor(v, rgb);
}
}
//-----------------------------------------------------------------------------
......@@ -234,24 +325,20 @@ double vtkDiscretizableColorTransferFunction::GetOpacity(double v)
vtkUnsignedCharArray* vtkDiscretizableColorTransferFunction::MapScalars(vtkDataArray *scalars,
int colorMode, int component)
{
if ( this->IndexedLookup )
{
return this->Superclass::MapScalars( scalars, colorMode, component );
}
this->Build();
bool scalars_are_mapped = !(colorMode == VTK_COLOR_MODE_DEFAULT) &&
vtkUnsignedCharArray::SafeDownCast(scalars);
vtkUnsignedCharArray *colors = this->Discretize ?
vtkUnsignedCharArray *colors = (this->Discretize || this->IndexedLookup) ?
this->LookupTable->MapScalars(scalars, colorMode, component):
this->Superclass::MapScalars(scalars, colorMode, component);
// calculate alpha values
if(colors &&
if (colors &&
colors->GetNumberOfComponents() == 4 &&
!scalars_are_mapped && !this->IndexedLookup &&
!scalars_are_mapped &&
!this->IndexedLookup && // we don't change alpha for IndexedLookup.
this->EnableOpacityMapping &&
this->ScalarOpacityFunction.GetPointer())
{
......@@ -299,41 +386,12 @@ vtkIdType vtkDiscretizableColorTransferFunction::GetNumberOfAvailableColors()
return this->NumberOfValues;
}
//----------------------------------------------------------------------------
void vtkDiscretizableColorTransferFunction::SetScalarOpacityFunction(vtkPiecewiseFunction *function)
{
if(this->ScalarOpacityFunction != function)
{
if (this->ScalarOpacityFunction &&
this->ScalarOpacityFunctionObserverId > 0)
{
this->ScalarOpacityFunction->RemoveObserver(this->ScalarOpacityFunctionObserverId);
this->ScalarOpacityFunctionObserverId = 0;
}
this->ScalarOpacityFunction = function;
if (function)
{
this->ScalarOpacityFunctionObserverId =
function->AddObserver(vtkCommand::ModifiedEvent,
this,
&vtkDiscretizableColorTransferFunction::ScalarOpacityFunctionModified);
}
this->Modified();
}
}
//----------------------------------------------------------------------------
vtkPiecewiseFunction* vtkDiscretizableColorTransferFunction::GetScalarOpacityFunction() const
{
return this->ScalarOpacityFunction;
}
//-----------------------------------------------------------------------------
void vtkDiscretizableColorTransferFunction::ScalarOpacityFunctionModified()
{
this->Modified();
}
//-----------------------------------------------------------------------------
void vtkDiscretizableColorTransferFunction::PrintSelf(ostream& os, vtkIndent indent)
{
......
......@@ -16,23 +16,26 @@
// vtkLookupTable.
// .SECTION Description
// This is a cross between a vtkColorTransferFunction and a vtkLookupTable
// selectively combiniting the functionality of both.
// NOTE: One must call Build() after making any changes to the points
// in the ColorTransferFunction to ensure that the discrete and non-discrete
// version match up.
// selectively combining the functionality of both. This class is a
// vtkColorTransferFunction allowing users to specify the RGB control points
// that control the color transfer function. At the same time, by setting
// \a Discretize to 1 (true), one can force the transfer function to only have
// \a NumberOfValues discrete colors.
//
// This class behaves differently depending on how \a IndexedLookup is set.
// When true, vtkLookupTable enters a mode for representing categorical color maps.
// By setting \a IndexedLookup to true, you indicate that the annotated
// values are the only valid values for which entries in the color table
// should be returned. The colors in the lookup \a Table are assigned
// When \a IndexedLookup is true, this class behaves differently. The annotated
// valyes are considered to the be only valid values for which entries in the
// color table should be returned. The colors for annotated values are those
// specified using \a AddIndexedColors. Typically, there must be atleast as many
// indexed colors specified as the annotations. For backwards compatibility, if
// no indexed-colors are specified, the colors in the lookup \a Table are assigned
// to annotated values by taking the modulus of their index in the list
// of annotations. \a IndexedLookup changes the behavior of \a GetIndex,
// which in turn changes the way \a MapScalars behaves;
// when \a IndexedLookup is true, \a MapScalars will search for
// scalar values in \a AnnotatedValues and use the resulting index to
// determine the color. If a scalar value is not present in \a AnnotatedValues,
// of annotations. If a scalar value is not present in \a AnnotatedValues,
// then \a NanColor will be used.
//
// NOTE: One must call Build() after making any changes to the points
// in the ColorTransferFunction to ensure that the discrete and non-discrete
// versions match up.
#ifndef __vtkDiscretizableColorTransferFunction_h
#define __vtkDiscretizableColorTransferFunction_h
......@@ -54,6 +57,36 @@ public:
int IsOpaque();
// Description:
// Add colors to use when \a IndexedLookup is true.
// \a SetIndexedColor() will automatically call
// SetNumberOfIndexedColors(index+1) if the current number of indexed colors
// is not sufficient for the specified index and all will be initialized to
// with the rgb values passed to this call.
void SetIndexedColor(unsigned int index, const double rgb[3])
{ this->SetIndexedColor(index, rgb[0], rgb[1], rgb[2]); }
void SetIndexedColor(unsigned int index, double r, double g, double b);
/** Get the "indexed color" assigned to an index.
*
* The index is used in \a IndexedLookup mode to assign colors to annotations (in the order
* the annotations were set).
* Subclasses must implement this and interpret how to treat the index.
* vtkLookupTable simply returns GetTableValue(\a index % \a this->GetNumberOfTableValues()).
* vtkColorTransferFunction returns the color assocated with node \a index % \a this->GetSize().
*
* Note that implementations *must* set the opacity (alpha) component of the color, even if they
* do not provide opacity values in their colormaps. In that case, alpha = 1 should be used.
*/
virtual void GetIndexedColor(vtkIdType i, double rgba[4]);
// Description:
// Set the number of indexed colors. These are used when IndexedLookup is
// true. If no indexed colors are specified, for backwards compatibility,
// this class reverts to using the RGBPoints for colors.
void SetNumberOfIndexedColors(unsigned int count);
unsigned int GetNumberOfIndexedColors();
// Description:
// Generate discretized lookup table, if applicable.
// This method must be called after changes to the ColorTransferFunction
......@@ -80,7 +113,7 @@ public:
// Set the number of values i.e. colors to be generated in the
// discrete lookup table. This has no effect if Discretize is off.
// The default is 256.
void SetNumberOfValues(vtkIdType number);
vtkSetMacro(NumberOfValues, vtkIdType);
vtkGetMacro(NumberOfValues, vtkIdType);
// Description:
......@@ -158,14 +191,14 @@ public:
vtkGetMacro(EnableOpacityMapping, bool)
vtkBooleanMacro(EnableOpacityMapping, bool)
// Description:
// Overridden to include the ScalarOpacityFunction's MTime.
virtual unsigned long GetMTime();
protected:
vtkDiscretizableColorTransferFunction();
~vtkDiscretizableColorTransferFunction();
// Description:
// Called when ScalarOpacityFunction is modified.
void ScalarOpacityFunctionModified();
int Discretize;
int UseLogScale;
......@@ -184,7 +217,9 @@ private:
// Pointer used by GetRGBPoints().
double* Data;
class vtkInternals;
vtkInternals* Internals;
};
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment