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
RevLine 
[2112]1/* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
[3177]3 * Copyright (C) 2004-2012  HUBzero Foundation, LLC
[2112]4 *
5 * Author: Leif Delgass <ldelgass@purdue.edu>
6 */
7
8#include <string>
9#include <list>
[2317]10#include <cstring>
[2112]11#include <cassert>
[3616]12
[2112]13#include <vtkLookupTable.h>
[2261]14#include <vtkColorTransferFunction.h>
15#include <vtkPiecewiseFunction.h>
[2112]16
17#include "ColorMap.h"
[3616]18#include "Molecule.h"
[2112]19#include "Trace.h"
20
[3615]21using namespace VtkVis;
[2112]22
[2261]23ColorMap *ColorMap::_default = NULL;
[2404]24ColorMap *ColorMap::_grayDefault = NULL;
[2261]25ColorMap *ColorMap::_volumeDefault = NULL;
[2320]26ColorMap *ColorMap::_elementDefault = NULL;
[2261]27
[2112]28ColorMap::ColorMap(const std::string& name) :
29    _name(name),
30    _needsBuild(true),
31    _numTableEntries(256)
32{
[2261]33    _colorTF = vtkSmartPointer<vtkColorTransferFunction>::New();
34    _colorTF->ClampingOn();
35    _opacityTF = vtkSmartPointer<vtkPiecewiseFunction>::New();
36    _opacityTF->ClampingOn();
[2112]37}
38
[3982]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
[2112]76ColorMap::~ColorMap()
77{
78}
79
[2194]80/**
81 * \brief Return the name/ID of the ColorMap
82 */
[2112]83const std::string& ColorMap::getName()
84{
85    return _name;
86}
87
[2194]88/**
89 * \brief Build and return the vtkLookupTable from the transfer function
90 */
[2112]91vtkLookupTable *ColorMap::getLookupTable()
92{
93    build();
94    assert(_lookupTable != NULL);
95    return _lookupTable;
96}
97
[2194]98/**
[2261]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];
[3976]107    for (int i = 0; i < _colorTF->GetSize(); i++) {
108        _colorTF->GetNodeValue(i, tmp);
[3982]109        //TRACE("Norm: %d: %g %g,%g,%g", i, tmp[0], tmp[1], tmp[2], tmp[3]);
[2261]110        tmp[0] = range[0] + tmp[0] * (range[1] - range[0]);
[3976]111        tf->AddRGBPoint(tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]);
[3982]112        //TRACE("New : %d: %g %g,%g,%g", i, tmp[0], tmp[1], tmp[2], tmp[3]);
[2261]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>
[3978]122ColorMap::getOpacityTransferFunction(double range[2], double opacityScale)
[2261]123{
124    vtkSmartPointer<vtkPiecewiseFunction> tf = vtkSmartPointer<vtkPiecewiseFunction>::New();
125    double tmp[4];
[3976]126    for (int i = 0; i < _opacityTF->GetSize(); i++) {
127        _opacityTF->GetNodeValue(i, tmp);
[3982]128        //TRACE("Norm: %d: %g %g", i, tmp[0], tmp[1]);
[2261]129        tmp[0] = range[0] + tmp[0] * (range[1] - range[0]);
[3982]130        if (opacityScale < 1.0) {
131            tmp[1] *= opacityScale;
132        }
[3976]133        tf->AddPoint(tmp[0], tmp[1], tmp[2], tmp[3]);
[3982]134        //TRACE("New : %d: %g %g", i, tmp[0], tmp[1]);
[2261]135    }
136    return tf;
137}
138
139/**
[2194]140 * \brief Insert a new RGB control point into the transfer function
141 */
[2112]142void ColorMap::addControlPoint(ControlPoint& cp)
143{
144    _needsBuild = true;
[4358]145    // Clamp value,midpoint,sharpness to [0,1]
[2112]146    if (cp.value < 0.0)
[4358]147        cp.value = 0.0;
[2112]148    if (cp.value > 1.0)
[4358]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;
[2112]158
[2573]159#ifdef DEBUG
[4358]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);
[2573]163#endif
[2112]164    for (std::list<ControlPoint>::iterator itr = _controlPoints.begin();
[4358]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        }
[2112]173    }
174    // If we reach here, our control point goes at the end
175    _controlPoints.insert(_controlPoints.end(), cp);
[4358]176    _colorTF->AddRGBPoint(cp.value, cp.color[0], cp.color[1], cp.color[2],
177                          cp.midpoint, cp.sharpness);
[2112]178}
179
[2194]180/**
181 * \brief Insert a new opacity/alpha control point into the transfer function
182 */
[2112]183void ColorMap::addOpacityControlPoint(OpacityControlPoint& cp)
184{
185    _needsBuild = true;
[4358]186    // Clamp value,midpoint,sharpness to [0,1]
[2112]187    if (cp.value < 0.0)
[4358]188        cp.value = 0.0;
[2112]189    if (cp.value > 1.0)
[4358]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;
[2112]199
[2573]200#ifdef DEBUG
[4358]201    TRACE("New opacity control point: %g = %g, %g %g",
202          cp.value, cp.alpha, cp.midpoint, cp.sharpness);
[2573]203#endif
[2112]204    for (std::list<OpacityControlPoint>::iterator itr = _opacityControlPoints.begin();
[4358]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        }
[2112]213    }
214    // If we reach here, our control point goes at the end
215    _opacityControlPoints.insert(_opacityControlPoints.end(), cp);
[4358]216    _opacityTF->AddPoint(cp.value, cp.alpha, cp.midpoint, cp.sharpness);
[2112]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) {
[4358]229        _needsBuild = true;
230        _numTableEntries = numEntries;
231        if (_lookupTable != NULL) {
232            build();
233        }
[2112]234    }
235}
236
[2194]237/**
[3455]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}
[5076]248
[3455]249/**
[2194]250 * \brief Build the lookup table from the control points in the transfer
251 * function
252 */
[2112]253void ColorMap::build()
254{
255    if (!_needsBuild)
[4358]256        return;
[2112]257
[3330]258    TRACE("%s", _name.c_str());
259
[2112]260    if (_lookupTable == NULL) {
[4358]261        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
[2112]262    }
263
264    _lookupTable->SetNumberOfTableValues(_numTableEntries);
[5076]265#if 0
[2112]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) {
[4358]272        ControlPoint cp = *itr;
273        cp.value = 0.0;
274        itr = _controlPoints.insert(itr, cp);
[2112]275    }
276    if (oitr->value > 0.0) {
[4358]277        OpacityControlPoint ocp = *oitr;
278        ocp.value = 0.0;
279        oitr = _opacityControlPoints.insert(oitr, ocp);
[2112]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++) {
[4358]288        double value = _numTableEntries < 2 ? 0.0 : ((double)i)/(_numTableEntries-1);
[2112]289        double color[4];
[4358]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()) {
[2402]299#ifdef DEBUG
[4358]300            TRACE("val: %g Range: %g - 1 Color: %g %g %g", value, itr->value,
301                  itr->color[0], itr->color[1], itr->color[2]);
[2402]302#endif
[2112]303            memcpy(color, itr->color, sizeof(double)*3);
[4358]304        } else {
305            assert(itr->value < itr2->value);
306            assert(value >= itr->value && value <= itr2->value);
307            lerp(color, *itr, *itr2, value);
[2402]308#ifdef DEBUG
[4358]309            TRACE("val: %g Range: %g - %g Color: %g %g %g", value, itr->value, itr2->value,
310                  color[0], color[1], color[2]);
[2402]311#endif
[4358]312        }
313        if (oitr2 == _opacityControlPoints.end()) {
[2402]314#ifdef DEBUG
[4358]315            TRACE("val: %g Range: %g - 1 Alpha %g", value, oitr->value,
316                  oitr->alpha);
[2402]317#endif
[2112]318            color[3] = oitr->alpha;
[4358]319        } else {
320            assert(oitr->value < oitr2->value);
321            assert(value >= oitr->value && value <= oitr2->value);
322            lerp(&color[3], *oitr, *oitr2, value);
[2402]323#ifdef DEBUG
[4358]324            TRACE("val: %g Range: %g - %g Alpha: %g", value, oitr->value, oitr2->value,
325                  color[3]);
[2402]326#endif
[4358]327        }
[2112]328        _lookupTable->SetTableValue(i, color);
329    }
[4358]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    }
[5076]342#endif
[4358]343    _needsBuild = false;
344    TRACE("Leave");
345}
[5076]346
[2261]347/**
348 * \brief Perform linear interpolation of two color control points
349 */
[2332]350void ColorMap::lerp(double *result,
351                    const ControlPoint& cp1,
352                    const ControlPoint& cp2,
353                    double value)
[2112]354{
355    double factor = (value - cp1.value) / (cp2.value - cp1.value);
356    for (int i = 0; i < 3; i++) {
[4358]357        result[i] = cp1.color[i] * (1.0 - factor) + cp2.color[i] * factor;
[2112]358    }
359}
360
[2261]361/**
362 * \brief Perform linear interpolation of two opacity control points
363 */
[2332]364void ColorMap::lerp(double *result,
365                    const OpacityControlPoint& cp1,
366                    const OpacityControlPoint& cp2,
367                    double value)
[2112]368{
369    double factor = (value - cp1.value) / (cp2.value - cp1.value);
370    *result = cp1.alpha * (1.0 - factor) + cp2.alpha * factor;
371}
372
[2194]373/**
374 * \brief Remove all control points and lookup table
375 */
[2112]376void ColorMap::clear()
377{
378    _controlPoints.clear();
[2261]379    _colorTF->RemoveAllPoints();
[2112]380    _opacityControlPoints.clear();
[2261]381    _opacityTF->RemoveAllPoints();
[2112]382    _lookupTable = NULL;
383}
384
[2194]385/**
386 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
387 */
[2320]388ColorMap *ColorMap::getDefault()
[2112]389{
[2261]390    if (_default != NULL) {
391        return _default;
392    }
393
394    _default = new ColorMap("default");
[2112]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++) {
[4358]417        _default->addControlPoint(cp[i]);
[2112]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;
[2261]424    _default->addOpacityControlPoint(ocp[0]);
425    _default->addOpacityControlPoint(ocp[1]);
426    _default->build();
427    return _default;
[2112]428}
[2261]429
430/**
[2404]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/**
[2261]463 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
464 * and transparent to opaque ramp
465 */
[2320]466ColorMap *ColorMap::getVolumeDefault()
[2261]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++) {
[4358]495        _volumeDefault->addControlPoint(cp[i]);
[2261]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}
[2320]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}
[4073]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.