source: vtkvis/branches/1.7/ColorMap.cpp @ 4783

Last change on this file since 4783 was 4783, checked in by ldelgass, 10 years ago

Merge some changes from trunk, including 2 new (currently unused) commands for
the image (slice) object

  • Property svn:eol-style set to native
File size: 16.3 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}
490
491/**
492 * \brief Render (using CPU) color map to an image
493 */
494void ColorMap::renderColorMap(ColorMap *map, int width, int height,
495                              vtkUnsignedCharArray *imgData,
496                              bool opaque, float bgColor[3],
497                              bool bgr, int bytesPerPixel)
498{
499    int size = bytesPerPixel * width * height;
500    if (imgData->GetMaxId() + 1 != size) {
501        imgData->SetNumberOfComponents(bytesPerPixel);
502        imgData->SetNumberOfValues(size);
503    }
504    unsigned char *dst = imgData->GetPointer(0);
505    vtkLookupTable *table = map->getLookupTable();
506    if (height > width) {
507        for (int i = 0; i < height; i++) {
508            double x = (double)i/(height-1);
509            double rgb[3];
510            table->GetColor(x, rgb);
511            unsigned char color[3];
512            if (opaque) {
513                color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
514                color[1] = (unsigned char)(255. * rgb[1]);
515                color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
516            } else {
517                double opacity = table->GetOpacity(x);
518                color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
519                color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
520                color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
521            }
522            for (int j = 0; j < width; j++) {
523                memcpy(dst, color, 3);
524                dst += 3;
525                if (bytesPerPixel == 4) {
526                    *(dst++) = (unsigned char)255.;
527                }
528            }
529        }
530    } else {
531        for (int i = 0; i < height; i++) {
532            for (int j = 0; j < width; j++) {
533                double x = (double)j/(width-1);
534                double rgb[3];
535                table->GetColor(x, rgb);
536                unsigned char color[3];
537                if (opaque) {
538                    color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
539                    color[1] = (unsigned char)(255. * rgb[1]);
540                    color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
541                } else {
542                    double opacity = table->GetOpacity(x);
543                    color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
544                    color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
545                    color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
546                }
547                memcpy(dst, color, 3);
548                dst += 3;
549                if (bytesPerPixel == 4) {
550                    *(dst++) = (unsigned char)255.;
551                }
552            }
553        }
554    }
555}
Note: See TracBrowser for help on using the repository browser.