source: vtkvis/trunk/ColorMap.cpp @ 5119

Last change on this file since 5119 was 5076, checked in by ldelgass, 9 years ago

minor cleanup

  • Property svn:eol-style set to native
File size: 17.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(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,midpoint,sharpness 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    if (cp.midpoint < 0.0)
151        cp.midpoint = 0.0;
152    if (cp.midpoint > 1.0)
153        cp.midpoint = 1.0;
154    if (cp.sharpness < 0.0)
155        cp.sharpness = 0.0;
156    if (cp.sharpness > 1.0)
157        cp.sharpness = 1.0;
158
159#ifdef DEBUG
160    TRACE("New control point: %g = %g %g %g, %g %g",
161          cp.value, cp.color[0], cp.color[1], cp.color[2],
162          cp.midpoint, cp.sharpness);
163#endif
164    for (std::list<ControlPoint>::iterator itr = _controlPoints.begin();
165         itr != _controlPoints.end(); ++itr) {
166        if (itr->value == cp.value) {
167            *itr = cp;
168            return;
169        } else if (itr->value > cp.value) {
170            _controlPoints.insert(itr, cp);
171            return;
172        }
173    }
174    // If we reach here, our control point goes at the end
175    _controlPoints.insert(_controlPoints.end(), cp);
176    _colorTF->AddRGBPoint(cp.value, cp.color[0], cp.color[1], cp.color[2],
177                          cp.midpoint, cp.sharpness);
178}
179
180/**
181 * \brief Insert a new opacity/alpha control point into the transfer function
182 */
183void ColorMap::addOpacityControlPoint(OpacityControlPoint& cp)
184{
185    _needsBuild = true;
186    // Clamp value,midpoint,sharpness to [0,1]
187    if (cp.value < 0.0)
188        cp.value = 0.0;
189    if (cp.value > 1.0)
190        cp.value = 1.0;
191    if (cp.midpoint < 0.0)
192        cp.midpoint = 0.0;
193    if (cp.midpoint > 1.0)
194        cp.midpoint = 1.0;
195    if (cp.sharpness < 0.0)
196        cp.sharpness = 0.0;
197    if (cp.sharpness > 1.0)
198        cp.sharpness = 1.0;
199
200#ifdef DEBUG
201    TRACE("New opacity control point: %g = %g, %g %g",
202          cp.value, cp.alpha, cp.midpoint, cp.sharpness);
203#endif
204    for (std::list<OpacityControlPoint>::iterator itr = _opacityControlPoints.begin();
205         itr != _opacityControlPoints.end(); ++itr) {
206        if (itr->value == cp.value) {
207            *itr = cp;
208            return;
209        } else if (itr->value > cp.value) {
210            _opacityControlPoints.insert(itr, cp);
211            return;
212        }
213    }
214    // If we reach here, our control point goes at the end
215    _opacityControlPoints.insert(_opacityControlPoints.end(), cp);
216    _opacityTF->AddPoint(cp.value, cp.alpha, cp.midpoint, cp.sharpness);
217}
218
219/**
220 * \brief Set 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 */
226void ColorMap::setNumberOfTableEntries(int numEntries)
227{
228    if (numEntries != _numTableEntries) {
229        _needsBuild = true;
230        _numTableEntries = numEntries;
231        if (_lookupTable != NULL) {
232            build();
233        }
234    }
235}
236
237/**
238 * \brief Get the number of discrete color table entries
239 *
240 * The number of table entries refers to the underlying
241 * vtkLookupTable and is independent of the number of
242 * control points in the transfer function.
243 */
244int ColorMap::getNumberOfTableEntries()
245{
246    return _numTableEntries;
247}
248
249/**
250 * \brief Build the lookup table from the control points in the transfer
251 * function
252 */
253void ColorMap::build()
254{
255    if (!_needsBuild)
256        return;
257
258    TRACE("%s", _name.c_str());
259
260    if (_lookupTable == NULL) {
261        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
262    }
263
264    _lookupTable->SetNumberOfTableValues(_numTableEntries);
265#if 0
266    std::list<ControlPoint>::iterator itr = _controlPoints.begin();
267    std::list<OpacityControlPoint>::iterator oitr = _opacityControlPoints.begin();
268
269    // If first value is > 0, insert a copy at 0 to create
270    // constant range up to first specified cp
271    if (itr->value > 0.0) {
272        ControlPoint cp = *itr;
273        cp.value = 0.0;
274        itr = _controlPoints.insert(itr, cp);
275    }
276    if (oitr->value > 0.0) {
277        OpacityControlPoint ocp = *oitr;
278        ocp.value = 0.0;
279        oitr = _opacityControlPoints.insert(oitr, ocp);
280    }
281
282    std::list<ControlPoint>::iterator itr2 = itr;
283    itr2++;
284    std::list<OpacityControlPoint>::iterator oitr2 = oitr;
285    oitr2++;
286
287    for (int i = 0; i < _numTableEntries; i++) {
288        double value = _numTableEntries < 2 ? 0.0 : ((double)i)/(_numTableEntries-1);
289        double color[4];
290        while (itr2 != _controlPoints.end() && value > itr2->value) {
291            itr = itr2;
292            itr2++;
293        }
294        while (oitr2 != _opacityControlPoints.end() && value > oitr2->value) {
295            oitr = oitr2;
296            oitr2++;
297        }
298        if (itr2 == _controlPoints.end()) {
299#ifdef DEBUG
300            TRACE("val: %g Range: %g - 1 Color: %g %g %g", value, itr->value,
301                  itr->color[0], itr->color[1], itr->color[2]);
302#endif
303            memcpy(color, itr->color, sizeof(double)*3);
304        } else {
305            assert(itr->value < itr2->value);
306            assert(value >= itr->value && value <= itr2->value);
307            lerp(color, *itr, *itr2, value);
308#ifdef DEBUG
309            TRACE("val: %g Range: %g - %g Color: %g %g %g", value, itr->value, itr2->value,
310                  color[0], color[1], color[2]);
311#endif
312        }
313        if (oitr2 == _opacityControlPoints.end()) {
314#ifdef DEBUG
315            TRACE("val: %g Range: %g - 1 Alpha %g", value, oitr->value,
316                  oitr->alpha);
317#endif
318            color[3] = oitr->alpha;
319        } else {
320            assert(oitr->value < oitr2->value);
321            assert(value >= oitr->value && value <= oitr2->value);
322            lerp(&color[3], *oitr, *oitr2, value);
323#ifdef DEBUG
324            TRACE("val: %g Range: %g - %g Alpha: %g", value, oitr->value, oitr2->value,
325                  color[3]);
326#endif
327        }
328        _lookupTable->SetTableValue(i, color);
329    }
330#else
331    double colorTable[_numTableEntries*3];
332    _colorTF->GetTable(0., 1., _numTableEntries, colorTable);
333    double opacityTable[_numTableEntries];
334    _opacityTF->GetTable(0., 1., _numTableEntries, opacityTable, 1);
335
336    for (int i = 0; i < _numTableEntries; i++) {
337        double color[4];
338        memcpy(color, colorTable+i*3, sizeof(double)*3);
339        color[3] = opacityTable[i];
340        _lookupTable->SetTableValue(i, color);
341    }
342#endif
343    _needsBuild = false;
344    TRACE("Leave");
345}
346
347/**
348 * \brief Perform linear interpolation of two color control points
349 */
350void ColorMap::lerp(double *result,
351                    const ControlPoint& cp1,
352                    const ControlPoint& cp2,
353                    double value)
354{
355    double factor = (value - cp1.value) / (cp2.value - cp1.value);
356    for (int i = 0; i < 3; i++) {
357        result[i] = cp1.color[i] * (1.0 - factor) + cp2.color[i] * factor;
358    }
359}
360
361/**
362 * \brief Perform linear interpolation of two opacity control points
363 */
364void ColorMap::lerp(double *result,
365                    const OpacityControlPoint& cp1,
366                    const OpacityControlPoint& cp2,
367                    double value)
368{
369    double factor = (value - cp1.value) / (cp2.value - cp1.value);
370    *result = cp1.alpha * (1.0 - factor) + cp2.alpha * factor;
371}
372
373/**
374 * \brief Remove all control points and lookup table
375 */
376void ColorMap::clear()
377{
378    _controlPoints.clear();
379    _colorTF->RemoveAllPoints();
380    _opacityControlPoints.clear();
381    _opacityTF->RemoveAllPoints();
382    _lookupTable = NULL;
383}
384
385/**
386 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
387 */
388ColorMap *ColorMap::getDefault()
389{
390    if (_default != NULL) {
391        return _default;
392    }
393
394    _default = new ColorMap("default");
395    ControlPoint cp[5];
396    cp[0].value = 0.0;
397    cp[0].color[0] = 0.0;
398    cp[0].color[1] = 0.0;
399    cp[0].color[2] = 1.0;
400    cp[1].value = 0.25;
401    cp[1].color[0] = 0.0;
402    cp[1].color[1] = 1.0;
403    cp[1].color[2] = 1.0;
404    cp[2].value = 0.5;
405    cp[2].color[0] = 0.0;
406    cp[2].color[1] = 1.0;
407    cp[2].color[2] = 0.0;
408    cp[3].value = 0.75;
409    cp[3].color[0] = 1.0;
410    cp[3].color[1] = 1.0;
411    cp[3].color[2] = 0.0;
412    cp[4].value = 1.0;
413    cp[4].color[0] = 1.0;
414    cp[4].color[1] = 0.0;
415    cp[4].color[2] = 0.0;
416    for (int i = 0; i < 5; i++) {
417        _default->addControlPoint(cp[i]);
418    }
419    OpacityControlPoint ocp[2];
420    ocp[0].value = 0.0;
421    ocp[0].alpha = 1.0;
422    ocp[1].value = 1.0;
423    ocp[1].alpha = 1.0;
424    _default->addOpacityControlPoint(ocp[0]);
425    _default->addOpacityControlPoint(ocp[1]);
426    _default->build();
427    return _default;
428}
429
430/**
431 * \brief Create a default ColorMap with a black-white grayscale ramp
432 */
433ColorMap *ColorMap::getGrayDefault()
434{
435    if (_grayDefault != NULL) {
436        return _grayDefault;
437    }
438
439    _grayDefault = new ColorMap("grayDefault");
440    ControlPoint cp[2];
441    cp[0].value = 0.0;
442    cp[0].color[0] = 0.0;
443    cp[0].color[1] = 0.0;
444    cp[0].color[2] = 0.0;
445    _grayDefault->addControlPoint(cp[0]);
446    cp[1].value = 1.0;
447    cp[1].color[0] = 1.0;
448    cp[1].color[1] = 1.0;
449    cp[1].color[2] = 1.0;
450    _grayDefault->addControlPoint(cp[1]);
451    OpacityControlPoint ocp[2];
452    ocp[0].value = 0.0;
453    ocp[0].alpha = 1.0;
454    ocp[1].value = 1.0;
455    ocp[1].alpha = 1.0;
456    _grayDefault->addOpacityControlPoint(ocp[0]);
457    _grayDefault->addOpacityControlPoint(ocp[1]);
458    _grayDefault->build();
459    return _grayDefault;
460}
461
462/**
463 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
464 * and transparent to opaque ramp
465 */
466ColorMap *ColorMap::getVolumeDefault()
467{
468    if (_volumeDefault != NULL) {
469        return _volumeDefault;
470    }
471
472    _volumeDefault = new ColorMap("volumeDefault");
473    ControlPoint cp[5];
474    cp[0].value = 0.0;
475    cp[0].color[0] = 0.0;
476    cp[0].color[1] = 0.0;
477    cp[0].color[2] = 1.0;
478    cp[1].value = 0.25;
479    cp[1].color[0] = 0.0;
480    cp[1].color[1] = 1.0;
481    cp[1].color[2] = 1.0;
482    cp[2].value = 0.5;
483    cp[2].color[0] = 0.0;
484    cp[2].color[1] = 1.0;
485    cp[2].color[2] = 0.0;
486    cp[3].value = 0.75;
487    cp[3].color[0] = 1.0;
488    cp[3].color[1] = 1.0;
489    cp[3].color[2] = 0.0;
490    cp[4].value = 1.0;
491    cp[4].color[0] = 1.0;
492    cp[4].color[1] = 0.0;
493    cp[4].color[2] = 0.0;
494    for (int i = 0; i < 5; i++) {
495        _volumeDefault->addControlPoint(cp[i]);
496    }
497    OpacityControlPoint ocp[2];
498    ocp[0].value = 0.0;
499    ocp[0].alpha = 0.0;
500    ocp[1].value = 1.0;
501    ocp[1].alpha = 1.0;
502    _volumeDefault->addOpacityControlPoint(ocp[0]);
503    _volumeDefault->addOpacityControlPoint(ocp[1]);
504    _volumeDefault->build();
505    return _volumeDefault;
506}
507
508/**
509 * \brief Create a default ColorMap for coloring by atomic
510 * number in Molecules
511 */
512ColorMap *ColorMap::getElementDefault()
513{
514    if (_elementDefault != NULL) {
515        return _elementDefault;
516    }
517
518    _elementDefault = Molecule::createElementColorMap();
519    return _elementDefault;
520}
521
522/**
523 * \brief Render (using CPU) color map to an image
524 */
525void ColorMap::renderColorMap(ColorMap *map, int width, int height,
526                              vtkUnsignedCharArray *imgData,
527                              bool opaque, float bgColor[3],
528                              bool bgr, int bytesPerPixel)
529{
530    int size = bytesPerPixel * width * height;
531    if (imgData->GetMaxId() + 1 != size) {
532        imgData->SetNumberOfComponents(bytesPerPixel);
533        imgData->SetNumberOfValues(size);
534    }
535    unsigned char *dst = imgData->GetPointer(0);
536    vtkLookupTable *table = map->getLookupTable();
537    if (height > width) {
538        for (int i = 0; i < height; i++) {
539            double x = (double)i/(height-1);
540            double rgb[3];
541            table->GetColor(x, rgb);
542            unsigned char color[3];
543            if (opaque) {
544                color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
545                color[1] = (unsigned char)(255. * rgb[1]);
546                color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
547            } else {
548                double opacity = table->GetOpacity(x);
549                color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
550                color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
551                color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
552            }
553            for (int j = 0; j < width; j++) {
554                memcpy(dst, color, 3);
555                dst += 3;
556                if (bytesPerPixel == 4) {
557                    *(dst++) = (unsigned char)255.;
558                }
559            }
560        }
561    } else {
562        for (int i = 0; i < height; i++) {
563            for (int j = 0; j < width; j++) {
564                double x = (double)j/(width-1);
565                double rgb[3];
566                table->GetColor(x, rgb);
567                unsigned char color[3];
568                if (opaque) {
569                    color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
570                    color[1] = (unsigned char)(255. * rgb[1]);
571                    color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
572                } else {
573                    double opacity = table->GetOpacity(x);
574                    color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
575                    color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
576                    color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
577                }
578                memcpy(dst, color, 3);
579                dst += 3;
580                if (bytesPerPixel == 4) {
581                    *(dst++) = (unsigned char)255.;
582                }
583            }
584        }
585    }
586}
Note: See TracBrowser for help on using the repository browser.