source: branches/vtkvis_threaded/ColorMap.cpp @ 2549

Last change on this file since 2549 was 2404, checked in by ldelgass, 13 years ago

Create a default grayscale colormap and use it for LIC

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