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

Last change on this file since 3330 was 3330, checked in by gah, 12 years ago

merge (by hand) with Rappture1.2 branch

  • Property svn:eol-style set to native
File size: 11.1 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#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#ifdef DEBUG
109    TRACE("New control point: %g  = %g %g %g",
110          cp.value, cp.color[0], cp.color[1], cp.color[2]);
111#endif
112    for (std::list<ControlPoint>::iterator itr = _controlPoints.begin();
113         itr != _controlPoints.end(); ++itr) {
114        if (itr->value == cp.value) {
115            *itr = cp;
116            return;
117        } else if (itr->value > cp.value) {
118            _controlPoints.insert(itr, cp);
119            return;
120        }
121    }
122    // If we reach here, our control point goes at the end
123    _controlPoints.insert(_controlPoints.end(), cp);
124    _colorTF->AddRGBPoint(cp.value, cp.color[0], cp.color[1], cp.color[2]);
125}
126
127/**
128 * \brief Insert a new opacity/alpha control point into the transfer function
129 */
130void ColorMap::addOpacityControlPoint(OpacityControlPoint& cp)
131{
132    _needsBuild = true;
133    // Clamp value to [0,1]
134    if (cp.value < 0.0)
135        cp.value = 0.0;
136    if (cp.value > 1.0)
137        cp.value = 1.0;
138
139#ifdef DEBUG
140    TRACE("New opacity control point: %g  = %g",
141          cp.value, cp.alpha);
142#endif
143    for (std::list<OpacityControlPoint>::iterator itr = _opacityControlPoints.begin();
144         itr != _opacityControlPoints.end(); ++itr) {
145        if (itr->value == cp.value) {
146            *itr = cp;
147            return;
148        } else if (itr->value > cp.value) {
149            _opacityControlPoints.insert(itr, cp);
150            return;
151        }
152    }
153    // If we reach here, our control point goes at the end
154    _opacityControlPoints.insert(_opacityControlPoints.end(), cp);
155    _opacityTF->AddPoint(cp.value, cp.alpha);
156}
157
158/**
159 * \brief Set the number of discrete color table entries
160 *
161 * The number of table entries refers to the underlying
162 * vtkLookupTable and is independent of the number of
163 * control points in the transfer function.
164 */
165void ColorMap::setNumberOfTableEntries(int numEntries)
166{
167    if (numEntries != _numTableEntries) {
168        _needsBuild = true;
169        _numTableEntries = numEntries;
170        if (_lookupTable != NULL) {
171            build();
172        }
173    }
174}
175
176/**
177 * \brief Build the lookup table from the control points in the transfer
178 * function
179 */
180void ColorMap::build()
181{
182    if (!_needsBuild)
183        return;
184
185    TRACE("%s", _name.c_str());
186
187    if (_lookupTable == NULL) {
188        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
189    }
190
191    _lookupTable->SetNumberOfTableValues(_numTableEntries);
192
193    std::list<ControlPoint>::iterator itr = _controlPoints.begin();
194    std::list<OpacityControlPoint>::iterator oitr = _opacityControlPoints.begin();
195
196    // If first value is > 0, insert a copy at 0 to create
197    // constant range up to first specified cp
198    if (itr->value > 0.0) {
199        ControlPoint cp = *itr;
200        cp.value = 0.0;
201        itr = _controlPoints.insert(itr, cp);
202    }
203    if (oitr->value > 0.0) {
204        OpacityControlPoint ocp = *oitr;
205        ocp.value = 0.0;
206        oitr = _opacityControlPoints.insert(oitr, ocp);
207    }
208
209    std::list<ControlPoint>::iterator itr2 = itr;
210    itr2++;
211    std::list<OpacityControlPoint>::iterator oitr2 = oitr;
212    oitr2++;
213
214    for (int i = 0; i < _numTableEntries; i++) {
215        double value = ((double)i)/(_numTableEntries-1);
216        double color[4];
217        while (itr2 != _controlPoints.end() && value > itr2->value) {
218            itr = itr2;
219            itr2++;
220        }
221        while (oitr2 != _opacityControlPoints.end() && value > oitr2->value) {
222            oitr = oitr2;
223            oitr2++;
224        }
225        if (itr2 == _controlPoints.end()) {
226#ifdef DEBUG
227            TRACE("val: %g Range: %g - 1 Color: %g %g %g", value, itr->value,
228                  itr->color[0], itr->color[1], itr->color[2]);
229#endif
230            memcpy(color, itr->color, sizeof(double)*3);
231        } else {
232            assert(itr->value < itr2->value);
233            assert(value >= itr->value && value <= itr2->value);
234            lerp(color, *itr, *itr2, value);
235#ifdef DEBUG
236            TRACE("val: %g Range: %g - %g Color: %g %g %g", value, itr->value, itr2->value,
237                  color[0], color[1], color[2]);
238#endif
239        }
240        if (oitr2 == _opacityControlPoints.end()) {
241#ifdef DEBUG
242            TRACE("val: %g Range: %g - 1 Alpha %g", value, oitr->value,
243                  oitr->alpha);
244#endif
245            color[3] = oitr->alpha;
246        } else {
247            assert(oitr->value < oitr2->value);
248            assert(value >= oitr->value && value <= oitr2->value);
249            lerp(&color[3], *oitr, *oitr2, value);
250#ifdef DEBUG
251            TRACE("val: %g Range: %g - %g Alpha: %g", value, oitr->value, oitr2->value,
252                  color[3]);
253#endif
254        }
255        _lookupTable->SetTableValue(i, color);
256    }
257    _needsBuild = false;
258    TRACE("Leave");
259}
260
261/**
262 * \brief Perform linear interpolation of two color control points
263 */
264void ColorMap::lerp(double *result,
265                    const ControlPoint& cp1,
266                    const ControlPoint& cp2,
267                    double value)
268{
269    double factor = (value - cp1.value) / (cp2.value - cp1.value);
270    for (int i = 0; i < 3; i++) {
271        result[i] = cp1.color[i] * (1.0 - factor) + cp2.color[i] * factor;
272    }
273}
274
275/**
276 * \brief Perform linear interpolation of two opacity control points
277 */
278void ColorMap::lerp(double *result,
279                    const OpacityControlPoint& cp1,
280                    const OpacityControlPoint& cp2,
281                    double value)
282{
283    double factor = (value - cp1.value) / (cp2.value - cp1.value);
284    *result = cp1.alpha * (1.0 - factor) + cp2.alpha * factor;
285}
286
287/**
288 * \brief Remove all control points and lookup table
289 */
290void ColorMap::clear()
291{
292    _controlPoints.clear();
293    _colorTF->RemoveAllPoints();
294    _opacityControlPoints.clear();
295    _opacityTF->RemoveAllPoints();
296    _lookupTable = NULL;
297}
298
299/**
300 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
301 */
302ColorMap *ColorMap::getDefault()
303{
304    if (_default != NULL) {
305        return _default;
306    }
307
308    _default = new ColorMap("default");
309    ControlPoint cp[5];
310    cp[0].value = 0.0;
311    cp[0].color[0] = 0.0;
312    cp[0].color[1] = 0.0;
313    cp[0].color[2] = 1.0;
314    cp[1].value = 0.25;
315    cp[1].color[0] = 0.0;
316    cp[1].color[1] = 1.0;
317    cp[1].color[2] = 1.0;
318    cp[2].value = 0.5;
319    cp[2].color[0] = 0.0;
320    cp[2].color[1] = 1.0;
321    cp[2].color[2] = 0.0;
322    cp[3].value = 0.75;
323    cp[3].color[0] = 1.0;
324    cp[3].color[1] = 1.0;
325    cp[3].color[2] = 0.0;
326    cp[4].value = 1.0;
327    cp[4].color[0] = 1.0;
328    cp[4].color[1] = 0.0;
329    cp[4].color[2] = 0.0;
330    for (int i = 0; i < 5; i++) {
331        _default->addControlPoint(cp[i]);
332    }
333    OpacityControlPoint ocp[2];
334    ocp[0].value = 0.0;
335    ocp[0].alpha = 1.0;
336    ocp[1].value = 1.0;
337    ocp[1].alpha = 1.0;
338    _default->addOpacityControlPoint(ocp[0]);
339    _default->addOpacityControlPoint(ocp[1]);
340    _default->build();
341    return _default;
342}
343
344/**
345 * \brief Create a default ColorMap with a black-white grayscale ramp
346 */
347ColorMap *ColorMap::getGrayDefault()
348{
349    if (_grayDefault != NULL) {
350        return _grayDefault;
351    }
352
353    _grayDefault = new ColorMap("grayDefault");
354    ControlPoint cp[2];
355    cp[0].value = 0.0;
356    cp[0].color[0] = 0.0;
357    cp[0].color[1] = 0.0;
358    cp[0].color[2] = 0.0;
359    _grayDefault->addControlPoint(cp[0]);
360    cp[1].value = 1.0;
361    cp[1].color[0] = 1.0;
362    cp[1].color[1] = 1.0;
363    cp[1].color[2] = 1.0;
364    _grayDefault->addControlPoint(cp[1]);
365    OpacityControlPoint ocp[2];
366    ocp[0].value = 0.0;
367    ocp[0].alpha = 1.0;
368    ocp[1].value = 1.0;
369    ocp[1].alpha = 1.0;
370    _grayDefault->addOpacityControlPoint(ocp[0]);
371    _grayDefault->addOpacityControlPoint(ocp[1]);
372    _grayDefault->build();
373    return _grayDefault;
374}
375
376/**
377 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
378 * and transparent to opaque ramp
379 */
380ColorMap *ColorMap::getVolumeDefault()
381{
382    if (_volumeDefault != NULL) {
383        return _volumeDefault;
384    }
385
386    _volumeDefault = new ColorMap("volumeDefault");
387    ControlPoint cp[5];
388    cp[0].value = 0.0;
389    cp[0].color[0] = 0.0;
390    cp[0].color[1] = 0.0;
391    cp[0].color[2] = 1.0;
392    cp[1].value = 0.25;
393    cp[1].color[0] = 0.0;
394    cp[1].color[1] = 1.0;
395    cp[1].color[2] = 1.0;
396    cp[2].value = 0.5;
397    cp[2].color[0] = 0.0;
398    cp[2].color[1] = 1.0;
399    cp[2].color[2] = 0.0;
400    cp[3].value = 0.75;
401    cp[3].color[0] = 1.0;
402    cp[3].color[1] = 1.0;
403    cp[3].color[2] = 0.0;
404    cp[4].value = 1.0;
405    cp[4].color[0] = 1.0;
406    cp[4].color[1] = 0.0;
407    cp[4].color[2] = 0.0;
408    for (int i = 0; i < 5; i++) {
409        _volumeDefault->addControlPoint(cp[i]);
410    }
411    OpacityControlPoint ocp[2];
412    ocp[0].value = 0.0;
413    ocp[0].alpha = 0.0;
414    ocp[1].value = 1.0;
415    ocp[1].alpha = 1.0;
416    _volumeDefault->addOpacityControlPoint(ocp[0]);
417    _volumeDefault->addOpacityControlPoint(ocp[1]);
418    _volumeDefault->build();
419    return _volumeDefault;
420}
421
422/**
423 * \brief Create a default ColorMap for coloring by atomic
424 * number in Molecules
425 */
426ColorMap *ColorMap::getElementDefault()
427{
428    if (_elementDefault != NULL) {
429        return _elementDefault;
430    }
431
432    _elementDefault = Molecule::createElementColorMap();
433    return _elementDefault;
434}
Note: See TracBrowser for help on using the repository browser.