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

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

Add option to scale opacity in VTK transfer function

  • Property svn:eol-style set to native
File size: 11.4 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()
40{
41}
42
43/**
44 * \brief Return the name/ID of the ColorMap
45 */
46const std::string& ColorMap::getName()
47{
48    return _name;
49}
50
51/**
52 * \brief Build and return the vtkLookupTable from the transfer function
53 */
54vtkLookupTable *ColorMap::getLookupTable()
55{
56    build();
57    assert(_lookupTable != NULL);
58    return _lookupTable;
59}
60
61/**
62 * \brief Return a newly allocated color transfer function with values
63 * scaled to the given data range
64 */
65vtkSmartPointer<vtkColorTransferFunction>
66ColorMap::getColorTransferFunction(double range[2])
67{
68    vtkSmartPointer<vtkColorTransferFunction> tf = vtkSmartPointer<vtkColorTransferFunction>::New();
69    double tmp[6];
70    for (int i = 0; i < _colorTF->GetSize(); i++) {
71        _colorTF->GetNodeValue(i, tmp);
72        tmp[0] = range[0] + tmp[0] * (range[1] - range[0]);
73        tf->AddRGBPoint(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]);
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], double opacityScale)
84{
85    vtkSmartPointer<vtkPiecewiseFunction> tf = vtkSmartPointer<vtkPiecewiseFunction>::New();
86    double tmp[4];
87    for (int i = 0; i < _opacityTF->GetSize(); i++) {
88        _opacityTF->GetNodeValue(i, tmp);
89        tmp[0] = range[0] + tmp[0] * (range[1] - range[0]);
90        tmp[1] *= opacityScale;
91        tf->AddPoint(tmp[0], tmp[1], tmp[2], tmp[3]);
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 Get the number of discrete color table entries
178 *
179 * The number of table entries refers to the underlying
180 * vtkLookupTable and is independent of the number of
181 * control points in the transfer function.
182 */
183int ColorMap::getNumberOfTableEntries()
184{
185    return _numTableEntries;
186}
187
188/**
189 * \brief Build the lookup table from the control points in the transfer
190 * function
191 */
192void ColorMap::build()
193{
194    if (!_needsBuild)
195        return;
196
197    TRACE("%s", _name.c_str());
198
199    if (_lookupTable == NULL) {
200        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
201    }
202
203    _lookupTable->SetNumberOfTableValues(_numTableEntries);
204
205    std::list<ControlPoint>::iterator itr = _controlPoints.begin();
206    std::list<OpacityControlPoint>::iterator oitr = _opacityControlPoints.begin();
207
208    // If first value is > 0, insert a copy at 0 to create
209    // constant range up to first specified cp
210    if (itr->value > 0.0) {
211        ControlPoint cp = *itr;
212        cp.value = 0.0;
213        itr = _controlPoints.insert(itr, cp);
214    }
215    if (oitr->value > 0.0) {
216        OpacityControlPoint ocp = *oitr;
217        ocp.value = 0.0;
218        oitr = _opacityControlPoints.insert(oitr, ocp);
219    }
220
221    std::list<ControlPoint>::iterator itr2 = itr;
222    itr2++;
223    std::list<OpacityControlPoint>::iterator oitr2 = oitr;
224    oitr2++;
225
226    for (int i = 0; i < _numTableEntries; i++) {
227        double value = _numTableEntries < 2 ? 0.0 : ((double)i)/(_numTableEntries-1);
228        double color[4];
229        while (itr2 != _controlPoints.end() && value > itr2->value) {
230            itr = itr2;
231            itr2++;
232        }
233        while (oitr2 != _opacityControlPoints.end() && value > oitr2->value) {
234            oitr = oitr2;
235            oitr2++;
236        }
237        if (itr2 == _controlPoints.end()) {
238#ifdef DEBUG
239            TRACE("val: %g Range: %g - 1 Color: %g %g %g", value, itr->value,
240                  itr->color[0], itr->color[1], itr->color[2]);
241#endif
242            memcpy(color, itr->color, sizeof(double)*3);
243        } else {
244            assert(itr->value < itr2->value);
245            assert(value >= itr->value && value <= itr2->value);
246            lerp(color, *itr, *itr2, value);
247#ifdef DEBUG
248            TRACE("val: %g Range: %g - %g Color: %g %g %g", value, itr->value, itr2->value,
249                  color[0], color[1], color[2]);
250#endif
251        }
252        if (oitr2 == _opacityControlPoints.end()) {
253#ifdef DEBUG
254            TRACE("val: %g Range: %g - 1 Alpha %g", value, oitr->value,
255                  oitr->alpha);
256#endif
257            color[3] = oitr->alpha;
258        } else {
259            assert(oitr->value < oitr2->value);
260            assert(value >= oitr->value && value <= oitr2->value);
261            lerp(&color[3], *oitr, *oitr2, value);
262#ifdef DEBUG
263            TRACE("val: %g Range: %g - %g Alpha: %g", value, oitr->value, oitr2->value,
264                  color[3]);
265#endif
266        }
267        _lookupTable->SetTableValue(i, color);
268    }
269    _needsBuild = false;
270    TRACE("Leave");
271}
272
273/**
274 * \brief Perform linear interpolation of two color control points
275 */
276void ColorMap::lerp(double *result,
277                    const ControlPoint& cp1,
278                    const ControlPoint& cp2,
279                    double value)
280{
281    double factor = (value - cp1.value) / (cp2.value - cp1.value);
282    for (int i = 0; i < 3; i++) {
283        result[i] = cp1.color[i] * (1.0 - factor) + cp2.color[i] * factor;
284    }
285}
286
287/**
288 * \brief Perform linear interpolation of two opacity control points
289 */
290void ColorMap::lerp(double *result,
291                    const OpacityControlPoint& cp1,
292                    const OpacityControlPoint& cp2,
293                    double value)
294{
295    double factor = (value - cp1.value) / (cp2.value - cp1.value);
296    *result = cp1.alpha * (1.0 - factor) + cp2.alpha * factor;
297}
298
299/**
300 * \brief Remove all control points and lookup table
301 */
302void ColorMap::clear()
303{
304    _controlPoints.clear();
305    _colorTF->RemoveAllPoints();
306    _opacityControlPoints.clear();
307    _opacityTF->RemoveAllPoints();
308    _lookupTable = NULL;
309}
310
311/**
312 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
313 */
314ColorMap *ColorMap::getDefault()
315{
316    if (_default != NULL) {
317        return _default;
318    }
319
320    _default = new ColorMap("default");
321    ControlPoint cp[5];
322    cp[0].value = 0.0;
323    cp[0].color[0] = 0.0;
324    cp[0].color[1] = 0.0;
325    cp[0].color[2] = 1.0;
326    cp[1].value = 0.25;
327    cp[1].color[0] = 0.0;
328    cp[1].color[1] = 1.0;
329    cp[1].color[2] = 1.0;
330    cp[2].value = 0.5;
331    cp[2].color[0] = 0.0;
332    cp[2].color[1] = 1.0;
333    cp[2].color[2] = 0.0;
334    cp[3].value = 0.75;
335    cp[3].color[0] = 1.0;
336    cp[3].color[1] = 1.0;
337    cp[3].color[2] = 0.0;
338    cp[4].value = 1.0;
339    cp[4].color[0] = 1.0;
340    cp[4].color[1] = 0.0;
341    cp[4].color[2] = 0.0;
342    for (int i = 0; i < 5; i++) {
343        _default->addControlPoint(cp[i]);
344    }
345    OpacityControlPoint ocp[2];
346    ocp[0].value = 0.0;
347    ocp[0].alpha = 1.0;
348    ocp[1].value = 1.0;
349    ocp[1].alpha = 1.0;
350    _default->addOpacityControlPoint(ocp[0]);
351    _default->addOpacityControlPoint(ocp[1]);
352    _default->build();
353    return _default;
354}
355
356/**
357 * \brief Create a default ColorMap with a black-white grayscale ramp
358 */
359ColorMap *ColorMap::getGrayDefault()
360{
361    if (_grayDefault != NULL) {
362        return _grayDefault;
363    }
364
365    _grayDefault = new ColorMap("grayDefault");
366    ControlPoint cp[2];
367    cp[0].value = 0.0;
368    cp[0].color[0] = 0.0;
369    cp[0].color[1] = 0.0;
370    cp[0].color[2] = 0.0;
371    _grayDefault->addControlPoint(cp[0]);
372    cp[1].value = 1.0;
373    cp[1].color[0] = 1.0;
374    cp[1].color[1] = 1.0;
375    cp[1].color[2] = 1.0;
376    _grayDefault->addControlPoint(cp[1]);
377    OpacityControlPoint ocp[2];
378    ocp[0].value = 0.0;
379    ocp[0].alpha = 1.0;
380    ocp[1].value = 1.0;
381    ocp[1].alpha = 1.0;
382    _grayDefault->addOpacityControlPoint(ocp[0]);
383    _grayDefault->addOpacityControlPoint(ocp[1]);
384    _grayDefault->build();
385    return _grayDefault;
386}
387
388/**
389 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
390 * and transparent to opaque ramp
391 */
392ColorMap *ColorMap::getVolumeDefault()
393{
394    if (_volumeDefault != NULL) {
395        return _volumeDefault;
396    }
397
398    _volumeDefault = new ColorMap("volumeDefault");
399    ControlPoint cp[5];
400    cp[0].value = 0.0;
401    cp[0].color[0] = 0.0;
402    cp[0].color[1] = 0.0;
403    cp[0].color[2] = 1.0;
404    cp[1].value = 0.25;
405    cp[1].color[0] = 0.0;
406    cp[1].color[1] = 1.0;
407    cp[1].color[2] = 1.0;
408    cp[2].value = 0.5;
409    cp[2].color[0] = 0.0;
410    cp[2].color[1] = 1.0;
411    cp[2].color[2] = 0.0;
412    cp[3].value = 0.75;
413    cp[3].color[0] = 1.0;
414    cp[3].color[1] = 1.0;
415    cp[3].color[2] = 0.0;
416    cp[4].value = 1.0;
417    cp[4].color[0] = 1.0;
418    cp[4].color[1] = 0.0;
419    cp[4].color[2] = 0.0;
420    for (int i = 0; i < 5; i++) {
421        _volumeDefault->addControlPoint(cp[i]);
422    }
423    OpacityControlPoint ocp[2];
424    ocp[0].value = 0.0;
425    ocp[0].alpha = 0.0;
426    ocp[1].value = 1.0;
427    ocp[1].alpha = 1.0;
428    _volumeDefault->addOpacityControlPoint(ocp[0]);
429    _volumeDefault->addOpacityControlPoint(ocp[1]);
430    _volumeDefault->build();
431    return _volumeDefault;
432}
433
434/**
435 * \brief Create a default ColorMap for coloring by atomic
436 * number in Molecules
437 */
438ColorMap *ColorMap::getElementDefault()
439{
440    if (_elementDefault != NULL) {
441        return _elementDefault;
442    }
443
444    _elementDefault = Molecule::createElementColorMap();
445    return _elementDefault;
446}
Note: See TracBrowser for help on using the repository browser.