source: vtkvis/trunk/ColorMap.cpp @ 4952

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

Add midpoint/sharpness to color/opacity control points

  • Property svn:eol-style set to native
File size: 17.9 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#if 0
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
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    _needsBuild = false;
331    TRACE("Leave");
332}
333#else
334/**
335 * \brief Build the lookup table from the control points in the transfer
336 * function
337 */
338void ColorMap::build()
339{
340    if (!_needsBuild)
341        return;
342
343    TRACE("%s", _name.c_str());
344
345    if (_lookupTable == NULL) {
346        _lookupTable = vtkSmartPointer<vtkLookupTable>::New();
347    }
348
349    _lookupTable->SetNumberOfTableValues(_numTableEntries);
350
351    double colorTable[_numTableEntries*3];
352    _colorTF->GetTable(0., 1., _numTableEntries, colorTable);
353    double opacityTable[_numTableEntries];
354    _opacityTF->GetTable(0., 1., _numTableEntries, opacityTable, 1);
355
356    for (int i = 0; i < _numTableEntries; i++) {
357        //double value = _numTableEntries < 2 ? 0.0 : ((double)i)/(_numTableEntries-1);
358        double color[4];
359        memcpy(color, colorTable+i*3, sizeof(double)*3);
360        color[3] = opacityTable[i];
361        _lookupTable->SetTableValue(i, color);
362    }
363    _needsBuild = false;
364    TRACE("Leave");
365}
366#endif
367/**
368 * \brief Perform linear interpolation of two color control points
369 */
370void ColorMap::lerp(double *result,
371                    const ControlPoint& cp1,
372                    const ControlPoint& cp2,
373                    double value)
374{
375    double factor = (value - cp1.value) / (cp2.value - cp1.value);
376    for (int i = 0; i < 3; i++) {
377        result[i] = cp1.color[i] * (1.0 - factor) + cp2.color[i] * factor;
378    }
379}
380
381/**
382 * \brief Perform linear interpolation of two opacity control points
383 */
384void ColorMap::lerp(double *result,
385                    const OpacityControlPoint& cp1,
386                    const OpacityControlPoint& cp2,
387                    double value)
388{
389    double factor = (value - cp1.value) / (cp2.value - cp1.value);
390    *result = cp1.alpha * (1.0 - factor) + cp2.alpha * factor;
391}
392
393/**
394 * \brief Remove all control points and lookup table
395 */
396void ColorMap::clear()
397{
398    _controlPoints.clear();
399    _colorTF->RemoveAllPoints();
400    _opacityControlPoints.clear();
401    _opacityTF->RemoveAllPoints();
402    _lookupTable = NULL;
403}
404
405/**
406 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
407 */
408ColorMap *ColorMap::getDefault()
409{
410    if (_default != NULL) {
411        return _default;
412    }
413
414    _default = new ColorMap("default");
415    ControlPoint cp[5];
416    cp[0].value = 0.0;
417    cp[0].color[0] = 0.0;
418    cp[0].color[1] = 0.0;
419    cp[0].color[2] = 1.0;
420    cp[1].value = 0.25;
421    cp[1].color[0] = 0.0;
422    cp[1].color[1] = 1.0;
423    cp[1].color[2] = 1.0;
424    cp[2].value = 0.5;
425    cp[2].color[0] = 0.0;
426    cp[2].color[1] = 1.0;
427    cp[2].color[2] = 0.0;
428    cp[3].value = 0.75;
429    cp[3].color[0] = 1.0;
430    cp[3].color[1] = 1.0;
431    cp[3].color[2] = 0.0;
432    cp[4].value = 1.0;
433    cp[4].color[0] = 1.0;
434    cp[4].color[1] = 0.0;
435    cp[4].color[2] = 0.0;
436    for (int i = 0; i < 5; i++) {
437        _default->addControlPoint(cp[i]);
438    }
439    OpacityControlPoint ocp[2];
440    ocp[0].value = 0.0;
441    ocp[0].alpha = 1.0;
442    ocp[1].value = 1.0;
443    ocp[1].alpha = 1.0;
444    _default->addOpacityControlPoint(ocp[0]);
445    _default->addOpacityControlPoint(ocp[1]);
446    _default->build();
447    return _default;
448}
449
450/**
451 * \brief Create a default ColorMap with a black-white grayscale ramp
452 */
453ColorMap *ColorMap::getGrayDefault()
454{
455    if (_grayDefault != NULL) {
456        return _grayDefault;
457    }
458
459    _grayDefault = new ColorMap("grayDefault");
460    ControlPoint cp[2];
461    cp[0].value = 0.0;
462    cp[0].color[0] = 0.0;
463    cp[0].color[1] = 0.0;
464    cp[0].color[2] = 0.0;
465    _grayDefault->addControlPoint(cp[0]);
466    cp[1].value = 1.0;
467    cp[1].color[0] = 1.0;
468    cp[1].color[1] = 1.0;
469    cp[1].color[2] = 1.0;
470    _grayDefault->addControlPoint(cp[1]);
471    OpacityControlPoint ocp[2];
472    ocp[0].value = 0.0;
473    ocp[0].alpha = 1.0;
474    ocp[1].value = 1.0;
475    ocp[1].alpha = 1.0;
476    _grayDefault->addOpacityControlPoint(ocp[0]);
477    _grayDefault->addOpacityControlPoint(ocp[1]);
478    _grayDefault->build();
479    return _grayDefault;
480}
481
482/**
483 * \brief Create a default ColorMap with a blue-cyan-green-yellow-red ramp
484 * and transparent to opaque ramp
485 */
486ColorMap *ColorMap::getVolumeDefault()
487{
488    if (_volumeDefault != NULL) {
489        return _volumeDefault;
490    }
491
492    _volumeDefault = new ColorMap("volumeDefault");
493    ControlPoint cp[5];
494    cp[0].value = 0.0;
495    cp[0].color[0] = 0.0;
496    cp[0].color[1] = 0.0;
497    cp[0].color[2] = 1.0;
498    cp[1].value = 0.25;
499    cp[1].color[0] = 0.0;
500    cp[1].color[1] = 1.0;
501    cp[1].color[2] = 1.0;
502    cp[2].value = 0.5;
503    cp[2].color[0] = 0.0;
504    cp[2].color[1] = 1.0;
505    cp[2].color[2] = 0.0;
506    cp[3].value = 0.75;
507    cp[3].color[0] = 1.0;
508    cp[3].color[1] = 1.0;
509    cp[3].color[2] = 0.0;
510    cp[4].value = 1.0;
511    cp[4].color[0] = 1.0;
512    cp[4].color[1] = 0.0;
513    cp[4].color[2] = 0.0;
514    for (int i = 0; i < 5; i++) {
515        _volumeDefault->addControlPoint(cp[i]);
516    }
517    OpacityControlPoint ocp[2];
518    ocp[0].value = 0.0;
519    ocp[0].alpha = 0.0;
520    ocp[1].value = 1.0;
521    ocp[1].alpha = 1.0;
522    _volumeDefault->addOpacityControlPoint(ocp[0]);
523    _volumeDefault->addOpacityControlPoint(ocp[1]);
524    _volumeDefault->build();
525    return _volumeDefault;
526}
527
528/**
529 * \brief Create a default ColorMap for coloring by atomic
530 * number in Molecules
531 */
532ColorMap *ColorMap::getElementDefault()
533{
534    if (_elementDefault != NULL) {
535        return _elementDefault;
536    }
537
538    _elementDefault = Molecule::createElementColorMap();
539    return _elementDefault;
540}
541
542/**
543 * \brief Render (using CPU) color map to an image
544 */
545void ColorMap::renderColorMap(ColorMap *map, int width, int height,
546                              vtkUnsignedCharArray *imgData,
547                              bool opaque, float bgColor[3],
548                              bool bgr, int bytesPerPixel)
549{
550    int size = bytesPerPixel * width * height;
551    if (imgData->GetMaxId() + 1 != size) {
552        imgData->SetNumberOfComponents(bytesPerPixel);
553        imgData->SetNumberOfValues(size);
554    }
555    unsigned char *dst = imgData->GetPointer(0);
556    vtkLookupTable *table = map->getLookupTable();
557    if (height > width) {
558        for (int i = 0; i < height; i++) {
559            double x = (double)i/(height-1);
560            double rgb[3];
561            table->GetColor(x, rgb);
562            unsigned char color[3];
563            if (opaque) {
564                color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
565                color[1] = (unsigned char)(255. * rgb[1]);
566                color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
567            } else {
568                double opacity = table->GetOpacity(x);
569                color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
570                color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
571                color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
572            }
573            for (int j = 0; j < width; j++) {
574                memcpy(dst, color, 3);
575                dst += 3;
576                if (bytesPerPixel == 4) {
577                    *(dst++) = (unsigned char)255.;
578                }
579            }
580        }
581    } else {
582        for (int i = 0; i < height; i++) {
583            for (int j = 0; j < width; j++) {
584                double x = (double)j/(width-1);
585                double rgb[3];
586                table->GetColor(x, rgb);
587                unsigned char color[3];
588                if (opaque) {
589                    color[0] = (unsigned char)(255. * (bgr ? rgb[2] : rgb[0]));
590                    color[1] = (unsigned char)(255. * rgb[1]);
591                    color[2] = (unsigned char)(255. * (bgr ? rgb[0] : rgb[2]));
592                } else {
593                    double opacity = table->GetOpacity(x);
594                    color[0] = (unsigned char)(255. * (bgColor[0] * (1.0 - opacity) + (bgr ? rgb[2] : rgb[0]) * opacity));
595                    color[1] = (unsigned char)(255. * (bgColor[1] * (1.0 - opacity) + rgb[1] * opacity));
596                    color[2] = (unsigned char)(255. * (bgColor[2] * (1.0 - opacity) + (bgr ? rgb[0] : rgb[2]) * opacity));
597                }
598                memcpy(dst, color, 3);
599                dst += 3;
600                if (bytesPerPixel == 4) {
601                    *(dst++) = (unsigned char)255.;
602                }
603            }
604        }
605    }
606}
Note: See TracBrowser for help on using the repository browser.