source: trunk/packages/vizservers/vtkvis/ColorMap.cpp @ 3982

Last change on this file since 3982 was 3982, checked in by ldelgass, 11 years ago

Add explicit copy ctor, assignment operator for ColorMap?, in order to make
deep copies of vtk transfer functions and lookup table (instead of copying
smart pointer refs)

  • Property svn:eol-style set to native
File size: 13.0 KB
Line 
1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 * Copyright (C) 2004-2012  HUBzero Foundation, LLC
4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <string>
9#include <list>
10#include <cstring>
11#include <cassert>
12
13#include <vtkLookupTable.h>
14#include <vtkColorTransferFunction.h>
15#include <vtkPiecewiseFunction.h>
16
17#include "ColorMap.h"
18#include "Molecule.h"
19#include "Trace.h"
20
21using namespace VtkVis;
22
23ColorMap *ColorMap::_default = NULL;
24ColorMap *ColorMap::_grayDefault = NULL;
25ColorMap *ColorMap::_volumeDefault = NULL;
26ColorMap *ColorMap::_elementDefault = NULL;
27
28ColorMap::ColorMap(const std::string& name) :
29    _name(name),
30    _needsBuild(true),
31    _numTableEntries(256)
32{
33    _colorTF = vtkSmartPointer<vtkColorTransferFunction>::New();
34    _colorTF->ClampingOn();
35    _opacityTF = vtkSmartPointer<vtkPiecewiseFunction>::New();
36    _opacityTF->ClampingOn();
37}
38
39ColorMap::ColorMap(const ColorMap& other) :
40    _name(other._name),
41    _controlPoints(other._controlPoints),
42    _opacityControlPoints(other._opacityControlPoints),
43    _needsBuild(other._needsBuild),
44    _numTableEntries(other._numTableEntries)
45{
46    _colorTF = vtkSmartPointer<vtkColorTransferFunction>::New();
47    _colorTF->ClampingOn();
48    _opacityTF = vtkSmartPointer<vtkPiecewiseFunction>::New();
49    _opacityTF->ClampingOn();
50    _colorTF->DeepCopy(other._colorTF);
51    _opacityTF->DeepCopy(other._opacityTF);
52    if (other._lookupTable != NULL) {
53        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
54        _lookupTable->DeepCopy(other._lookupTable);
55    }
56}
57
58ColorMap& ColorMap::operator=(const ColorMap& other)
59{
60    if (&other != this) {
61        _name = other._name;
62        _controlPoints = other._controlPoints;
63        _opacityControlPoints = other._opacityControlPoints;
64        _needsBuild = other._needsBuild;
65        _numTableEntries = other._numTableEntries;
66        _colorTF->DeepCopy(other._colorTF);
67        _opacityTF->DeepCopy(other._opacityTF);
68        if (other._lookupTable != NULL) {
69            _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
70            _lookupTable->DeepCopy(other._lookupTable);
71        }
72    }
73    return *this;
74}
75
76ColorMap::~ColorMap()
77{
78}
79
80/**
81 * \brief Return the name/ID of the ColorMap
82 */
83const std::string& ColorMap::getName()
84{
85    return _name;
86}
87
88/**
89 * \brief Build and return the vtkLookupTable from the transfer function
90 */
91vtkLookupTable *ColorMap::getLookupTable()
92{
93    build();
94    assert(_lookupTable != NULL);
95    return _lookupTable;
96}
97
98/**
99 * \brief Return a newly allocated color transfer function with values
100 * scaled to the given data range
101 */
102vtkSmartPointer<vtkColorTransferFunction>
103ColorMap::getColorTransferFunction(double range[2])
104{
105    vtkSmartPointer<vtkColorTransferFunction> tf = vtkSmartPointer<vtkColorTransferFunction>::New();
106    double tmp[6];
107    for (int i = 0; i < _colorTF->GetSize(); i++) {
108        _colorTF->GetNodeValue(i, tmp);
109        //TRACE("Norm: %d: %g %g,%g,%g", i, tmp[0], tmp[1], tmp[2], tmp[3]);
110        tmp[0] = range[0] + tmp[0] * (range[1] - range[0]);
111        tf->AddRGBPoint(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]);
112        //TRACE("New : %d: %g %g,%g,%g", i, tmp[0], tmp[1], tmp[2], tmp[3]);
113    }
114    return tf;
115}
116
117/**
118 * \brief Return a newly allocated opacity transfer function with values
119 * scaled to the given data range
120 */
121vtkSmartPointer<vtkPiecewiseFunction>
122ColorMap::getOpacityTransferFunction(double range[2], double opacityScale)
123{
124    vtkSmartPointer<vtkPiecewiseFunction> tf = vtkSmartPointer<vtkPiecewiseFunction>::New();
125    double tmp[4];
126    for (int i = 0; i < _opacityTF->GetSize(); i++) {
127        _opacityTF->GetNodeValue(i, tmp);
128        //TRACE("Norm: %d: %g %g", i, tmp[0], tmp[1]);
129        tmp[0] = range[0] + tmp[0] * (range[1] - range[0]);
130        if (opacityScale < 1.0) {
131            tmp[1] *= opacityScale;
132        }
133        tf->AddPoint(tmp[0], tmp[1], tmp[2], tmp[3]);
134        //TRACE("New : %d: %g %g", i, tmp[0], tmp[1]);
135    }
136    return tf;
137}
138
139/**
140 * \brief Insert a new RGB control point into the transfer function
141 */
142void ColorMap::addControlPoint(ControlPoint& cp)
143{
144    _needsBuild = true;
145    // Clamp value to [0,1]
146    if (cp.value < 0.0)
147        cp.value = 0.0;
148    if (cp.value > 1.0)
149        cp.value = 1.0;
150
151#ifdef DEBUG
152    TRACE("New control point: %g = %g %g %g",
153          cp.value, cp.color[0], cp.color[1], cp.color[2]);
154#endif
155    for (std::list<ControlPoint>::iterator itr = _controlPoints.begin();
156         itr != _controlPoints.end(); ++itr) {
157        if (itr->value == cp.value) {
158            *itr = cp;
159            return;
160        } else if (itr->value > cp.value) {
161            _controlPoints.insert(itr, cp);
162            return;
163        }
164    }
165    // If we reach here, our control point goes at the end
166    _controlPoints.insert(_controlPoints.end(), cp);
167    _colorTF->AddRGBPoint(cp.value, cp.color[0], cp.color[1], cp.color[2]);
168}
169
170/**
171 * \brief Insert a new opacity/alpha control point into the transfer function
172 */
173void ColorMap::addOpacityControlPoint(OpacityControlPoint& cp)
174{
175    _needsBuild = true;
176    // Clamp value to [0,1]
177    if (cp.value < 0.0)
178        cp.value = 0.0;
179    if (cp.value > 1.0)
180        cp.value = 1.0;
181
182#ifdef DEBUG
183    TRACE("New opacity control point: %g = %g",
184          cp.value, cp.alpha);
185#endif
186    for (std::list<OpacityControlPoint>::iterator itr = _opacityControlPoints.begin();
187         itr != _opacityControlPoints.end(); ++itr) {
188        if (itr->value == cp.value) {
189            *itr = cp;
190            return;
191        } else if (itr->value > cp.value) {
192            _opacityControlPoints.insert(itr, cp);
193            return;
194        }
195    }
196    // If we reach here, our control point goes at the end
197    _opacityControlPoints.insert(_opacityControlPoints.end(), cp);
198    _opacityTF->AddPoint(cp.value, cp.alpha);
199}
200
201/**
202 * \brief Set the number of discrete color table entries
203 *
204 * The number of table entries refers to the underlying
205 * vtkLookupTable and is independent of the number of
206 * control points in the transfer function.
207 */
208void ColorMap::setNumberOfTableEntries(int numEntries)
209{
210    if (numEntries != _numTableEntries) {
211        _needsBuild = true;
212        _numTableEntries = numEntries;
213        if (_lookupTable != NULL) {
214            build();
215        }
216    }
217}
218
219/**
220 * \brief Get the number of discrete color table entries
221 *
222 * The number of table entries refers to the underlying
223 * vtkLookupTable and is independent of the number of
224 * control points in the transfer function.
225 */
226int ColorMap::getNumberOfTableEntries()
227{
228    return _numTableEntries;
229}
230
231/**
232 * \brief Build the lookup table from the control points in the transfer
233 * function
234 */
235void ColorMap::build()
236{
237    if (!_needsBuild)
238        return;
239
240    TRACE("%s", _name.c_str());
241
242    if (_lookupTable == NULL) {
243        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
244    }
245
246    _lookupTable->SetNumberOfTableValues(_numTableEntries);
247
248    std::list<ControlPoint>::iterator itr = _controlPoints.begin();
249    std::list<OpacityControlPoint>::iterator oitr = _opacityControlPoints.begin();
250
251    // If first value is > 0, insert a copy at 0 to create
252    // constant range up to first specified cp
253    if (itr->value > 0.0) {
254        ControlPoint cp = *itr;
255        cp.value = 0.0;
256        itr = _controlPoints.insert(itr, cp);
257    }
258    if (oitr->value > 0.0) {
259        OpacityControlPoint ocp = *oitr;
260        ocp.value = 0.0;
261        oitr = _opacityControlPoints.insert(oitr, ocp);
262    }
263
264    std::list<ControlPoint>::iterator itr2 = itr;
265    itr2++;
266    std::list<OpacityControlPoint>::iterator oitr2 = oitr;
267    oitr2++;
268
269    for (int i = 0; i < _numTableEntries; i++) {
270        double value = _numTableEntries < 2 ? 0.0 : ((double)i)/(_numTableEntries-1);
271        double color[4];
272        while (itr2 != _controlPoints.end() && value > itr2->value) {
273            itr = itr2;
274            itr2++;
275        }
276        while (oitr2 != _opacityControlPoints.end() && value > oitr2->value) {
277            oitr = oitr2;
278            oitr2++;
279        }
280        if (itr2 == _controlPoints.end()) {
281#ifdef DEBUG
282            TRACE("val: %g Range: %g - 1 Color: %g %g %g", value, itr->value,
283                  itr->color[0], itr->color[1], itr->color[2]);
284#endif
285            memcpy(color, itr->color, sizeof(double)*3);
286        } else {
287            assert(itr->value < itr2->value);
288            assert(value >= itr->value && value <= itr2->value);
289            lerp(color, *itr, *itr2, value);
290#ifdef DEBUG
291            TRACE("val: %g Range: %g - %g Color: %g %g %g", value, itr->value, itr2->value,
292                  color[0], color[1], color[2]);
293#endif
294        }
295        if (oitr2 == _opacityControlPoints.end()) {
296#ifdef DEBUG
297            TRACE("val: %g Range: %g - 1 Alpha %g", value, oitr->value,
298                  oitr->alpha);
299#endif
300            color[3] = oitr->alpha;
301        } else {
302            assert(oitr->value < oitr2->value);
303            assert(value >= oitr->value && value <= oitr2->value);
304            lerp(&color[3], *oitr, *oitr2, value);
305#ifdef DEBUG
306            TRACE("val: %g Range: %g - %g Alpha: %g", value, oitr->value, oitr2->value,
307                  color[3]);
308#endif
309        }
310        _lookupTable->SetTableValue(i, color);
311    }
312    _needsBuild = false;
313    TRACE("Leave");
314}
315
316/**
317 * \brief Perform linear interpolation of two color control points
318 */
319void ColorMap::lerp(double *result,
320                    const ControlPoint& cp1,
321                    const ControlPoint& cp2,
322                    double value)
323{
324    double factor = (value - cp1.value) / (cp2.value - cp1.value);
325    for (int i = 0; i < 3; i++) {
326        result[i] = cp1.color[i] * (1.0 - factor) + cp2.color[i] * factor;
327    }
328}
329
330/**
331 * \brief Perform linear interpolation of two opacity control points
332 */
333void ColorMap::lerp(double *result,
334                    const OpacityControlPoint& cp1,
335                    const OpacityControlPoint& cp2,
336                    double value)
337{
338    double factor = (value - cp1.value) / (cp2.value - cp1.value);
339    *result = cp1.alpha * (1.0 - factor) + cp2.alpha * factor;
340}
341
342/**
343 * \brief Remove all control points and lookup table
344 */
345void ColorMap::clear()
346{
347    _controlPoints.clear();
348    _colorTF->RemoveAllPoints();
349    _opacityControlPoints.clear();
350    _opacityTF->RemoveAllPoints();
351    _lookupTable = NULL;
352}
353
354/**
355 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
356 */
357ColorMap *ColorMap::getDefault()
358{
359    if (_default != NULL) {
360        return _default;
361    }
362
363    _default = new ColorMap("default");
364    ControlPoint cp[5];
365    cp[0].value = 0.0;
366    cp[0].color[0] = 0.0;
367    cp[0].color[1] = 0.0;
368    cp[0].color[2] = 1.0;
369    cp[1].value = 0.25;
370    cp[1].color[0] = 0.0;
371    cp[1].color[1] = 1.0;
372    cp[1].color[2] = 1.0;
373    cp[2].value = 0.5;
374    cp[2].color[0] = 0.0;
375    cp[2].color[1] = 1.0;
376    cp[2].color[2] = 0.0;
377    cp[3].value = 0.75;
378    cp[3].color[0] = 1.0;
379    cp[3].color[1] = 1.0;
380    cp[3].color[2] = 0.0;
381    cp[4].value = 1.0;
382    cp[4].color[0] = 1.0;
383    cp[4].color[1] = 0.0;
384    cp[4].color[2] = 0.0;
385    for (int i = 0; i < 5; i++) {
386        _default->addControlPoint(cp[i]);
387    }
388    OpacityControlPoint ocp[2];
389    ocp[0].value = 0.0;
390    ocp[0].alpha = 1.0;
391    ocp[1].value = 1.0;
392    ocp[1].alpha = 1.0;
393    _default->addOpacityControlPoint(ocp[0]);
394    _default->addOpacityControlPoint(ocp[1]);
395    _default->build();
396    return _default;
397}
398
399/**
400 * \brief Create a default ColorMap with a black-white grayscale ramp
401 */
402ColorMap *ColorMap::getGrayDefault()
403{
404    if (_grayDefault != NULL) {
405        return _grayDefault;
406    }
407
408    _grayDefault = new ColorMap("grayDefault");
409    ControlPoint cp[2];
410    cp[0].value = 0.0;
411    cp[0].color[0] = 0.0;
412    cp[0].color[1] = 0.0;
413    cp[0].color[2] = 0.0;
414    _grayDefault->addControlPoint(cp[0]);
415    cp[1].value = 1.0;
416    cp[1].color[0] = 1.0;
417    cp[1].color[1] = 1.0;
418    cp[1].color[2] = 1.0;
419    _grayDefault->addControlPoint(cp[1]);
420    OpacityControlPoint ocp[2];
421    ocp[0].value = 0.0;
422    ocp[0].alpha = 1.0;
423    ocp[1].value = 1.0;
424    ocp[1].alpha = 1.0;
425    _grayDefault->addOpacityControlPoint(ocp[0]);
426    _grayDefault->addOpacityControlPoint(ocp[1]);
427    _grayDefault->build();
428    return _grayDefault;
429}
430
431/**
432 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
433 * and transparent to opaque ramp
434 */
435ColorMap *ColorMap::getVolumeDefault()
436{
437    if (_volumeDefault != NULL) {
438        return _volumeDefault;
439    }
440
441    _volumeDefault = new ColorMap("volumeDefault");
442    ControlPoint cp[5];
443    cp[0].value = 0.0;
444    cp[0].color[0] = 0.0;
445    cp[0].color[1] = 0.0;
446    cp[0].color[2] = 1.0;
447    cp[1].value = 0.25;
448    cp[1].color[0] = 0.0;
449    cp[1].color[1] = 1.0;
450    cp[1].color[2] = 1.0;
451    cp[2].value = 0.5;
452    cp[2].color[0] = 0.0;
453    cp[2].color[1] = 1.0;
454    cp[2].color[2] = 0.0;
455    cp[3].value = 0.75;
456    cp[3].color[0] = 1.0;
457    cp[3].color[1] = 1.0;
458    cp[3].color[2] = 0.0;
459    cp[4].value = 1.0;
460    cp[4].color[0] = 1.0;
461    cp[4].color[1] = 0.0;
462    cp[4].color[2] = 0.0;
463    for (int i = 0; i < 5; i++) {
464        _volumeDefault->addControlPoint(cp[i]);
465    }
466    OpacityControlPoint ocp[2];
467    ocp[0].value = 0.0;
468    ocp[0].alpha = 0.0;
469    ocp[1].value = 1.0;
470    ocp[1].alpha = 1.0;
471    _volumeDefault->addOpacityControlPoint(ocp[0]);
472    _volumeDefault->addOpacityControlPoint(ocp[1]);
473    _volumeDefault->build();
474    return _volumeDefault;
475}
476
477/**
478 * \brief Create a default ColorMap for coloring by atomic
479 * number in Molecules
480 */
481ColorMap *ColorMap::getElementDefault()
482{
483    if (_elementDefault != NULL) {
484        return _elementDefault;
485    }
486
487    _elementDefault = Molecule::createElementColorMap();
488    return _elementDefault;
489}
Note: See TracBrowser for help on using the repository browser.