ResInsight/Fwk/VizFwk/LibRender/cvfScalarMapperUniformLevels.cpp

376 lines
12 KiB
C++

//##################################################################################################
//
// Custom Visualization Core library
// Copyright (C) 2011-2013 Ceetron AS
//
// This library may be used under the terms of either the GNU General Public License or
// the GNU Lesser General Public License as follows:
//
// GNU General Public License Usage
// This library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU General Public License at <<http://www.gnu.org/licenses/gpl.html>>
// for more details.
//
// GNU Lesser General Public License Usage
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.
//
// See the GNU Lesser General Public License at <<http://www.gnu.org/licenses/lgpl-2.1.html>>
// for more details.
//
//##################################################################################################
#include "cvfBase.h"
#include "cvfMath.h"
#include "cvfScalarMapperUniformLevels.h"
#include "cvfOverlayColorLegend.h"
#include "cvfTextureImage.h"
namespace cvf {
//==================================================================================================
///
/// \class cvf::ScalarMapperUniformLevels
/// \ingroup Render
///
/// Maps scalar values to texture coordinates/colors using levels of uniform size.
/// Configured by specifying a number of level colors and a min/max range.
//==================================================================================================
ScalarMapperUniformLevels::ScalarMapperUniformLevels()
: m_rangeMin(0),
m_rangeMax(0),
m_textureSize(64),
m_maxTexCoord(1.0)
{
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void ScalarMapperUniformLevels::setRange(double min, double max)
{
m_rangeMin = min;
m_rangeMax = max;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double ScalarMapperUniformLevels::rangeMin() const
{
return m_rangeMin;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
double ScalarMapperUniformLevels::rangeMax() const
{
return m_rangeMax;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void ScalarMapperUniformLevels::setColors(const Color3ubArray& colorArray)
{
CVF_ASSERT(colorArray.size() > 0);
m_colors = colorArray;
recomputeMaxTexCoord();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void ScalarMapperUniformLevels::setColors(ColorTable colorTable, uint levelCount)
{
// The normal color table has 13 hand coded levels, so handle these level counts separately
if (colorTable == NORMAL && levelCount <= 13)
{
m_colors = *normalColorTableArray(levelCount);
}
else
{
ref<Color3ubArray> baseColors = colorTableArray(colorTable);
if (baseColors->size() == levelCount)
{
m_colors = *baseColors;
}
else
{
m_colors = *interpolateColorArray(*baseColors, levelCount);
}
}
CVF_ASSERT(m_colors.size() == levelCount);
recomputeMaxTexCoord();
}
//--------------------------------------------------------------------------------------------------
/// Set the size of the texture produces by updateTexture()
///
/// For compatibility with OpenGL versions older than 2.0, the specified size should be a power of 2
/// Note that changing this size affects the texture coordinate generation.
///
/// \warning The texture size must be at least the the same as the number of colors specified.
/// If this is not the case, updateTexture() will fail.
//--------------------------------------------------------------------------------------------------
void ScalarMapperUniformLevels::setTextureSize(uint textureSize)
{
CVF_ASSERT(m_textureSize > 0);
m_textureSize = textureSize;
recomputeMaxTexCoord();
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
uint ScalarMapperUniformLevels::textureSize() const
{
return m_textureSize;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
void ScalarMapperUniformLevels::recomputeMaxTexCoord()
{
const uint numColors = static_cast<uint>(m_colors.size());
if (numColors == 0)
{
m_maxTexCoord = 1.0;
return;
}
const uint numPixelsPerColor = m_textureSize/numColors;
if (numPixelsPerColor == 0)
{
m_maxTexCoord = 1.0;
return;
}
uint texturePixelsInUse = numColors*numPixelsPerColor;
CVF_ASSERT(texturePixelsInUse <= m_textureSize);
m_maxTexCoord = static_cast<double>(texturePixelsInUse)/static_cast<double>(m_textureSize);
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Vec2f ScalarMapperUniformLevels::mapToTextureCoord(double scalarValue) const
{
const double range = m_rangeMax - m_rangeMin;
if (range >= 0)
{
if (scalarValue > m_rangeMin && scalarValue < m_rangeMax)
{
// Truly inside the range, implies a range > 0
CVF_ASSERT(range > 0);
double s = static_cast<float>(((scalarValue - m_rangeMin)/range)*m_maxTexCoord);
// Clamp to the currently legal texture coord range
// Might need to add code to correct for float precision, but that is probably not the main enemy.
// Our real problem is the fact that in most cases the texture coords get treated with even less precision
// on the graphics hardware. What we would really like is to guess at the HW precision and then correct for that.
// Currently the workaround is done in updateTexture() which pads the upper end of the texture when we're not filling
// all the texture pixels.
s = Math::clamp(s, 0.0, m_maxTexCoord);
return Vec2f(static_cast<float>(s), 0.5f);
}
else if (scalarValue <= m_rangeMin)
{
return Vec2f(0.0f, 0.5f);
}
else if (scalarValue >= m_rangeMax)
{
return Vec2f(static_cast<float>(m_maxTexCoord), 0.5f);
}
else
{
// If we get here we have som NaN, Use lowest legal texture coord
return Vec2f(0.0f, 0.0f);
}
}
else
{
// Range is invalid (min > max)
return Vec2f(0.0f, 0.5f);
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
Color3ub ScalarMapperUniformLevels::mapToColor(double scalarValue) const
{
uint colorCount = static_cast<uint>(m_colors.size());
CVF_ASSERT(colorCount > 0);
if (colorCount < 1)
{
return Color3::BLACK;
}
const double range = m_rangeMax - m_rangeMin;
if (range >= 0)
{
if (scalarValue > m_rangeMin && scalarValue < m_rangeMax)
{
// Truly inside the range, implies a range > 0
CVF_ASSERT(range > 0);
const double levelRange = range/colorCount;
const double dblIndex = (scalarValue - m_rangeMin)/levelRange;
uint index = 0;
if (dblIndex > 0 && dblIndex < colorCount)
{
index = static_cast<uint>(dblIndex);
}
else if (dblIndex >= colorCount)
{
index = colorCount - 1;
}
CVF_ASSERT(index < colorCount);
return m_colors[index];
}
else if (scalarValue <= m_rangeMin)
{
return m_colors[0];
}
else if (scalarValue >= m_rangeMax)
{
return m_colors[colorCount - 1];
}
else
{
// If we get here we have some NaN
return Color3::BLACK;
}
}
else
{
// Range is invalid
return m_colors[0];
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool ScalarMapperUniformLevels::updateColorLegend(OverlayColorLegend* legend) const
{
CVF_ASSERT(legend);
size_t numTicks = m_colors.size() + 1;
if (numTicks < 2)
{
return false;
}
DoubleArray ticks;
ticks.reserve(numTicks);
double delta = (m_rangeMax - m_rangeMin)/static_cast<double>(numTicks - 1);
size_t i;
for (i = 0; i < numTicks - 1; i++)
{
double tickVal = m_rangeMin + static_cast<double>(i)*delta;
ticks.add(tickVal);
}
ticks.add(m_rangeMax);
Color3ubArray colorArr(m_colors);
legend->configureLevels(colorArr, ticks);
return true;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool ScalarMapperUniformLevels::updateTexture(TextureImage* image) const
{
CVF_ASSERT(image);
const uint numColors = static_cast<uint>(m_colors.size());
if (numColors < 1)
{
return false;
}
CVF_ASSERT(m_textureSize >= numColors);
if (m_textureSize < numColors)
{
return false;
}
image->allocate(m_textureSize, 1);
// For now fill with white so we can see any errors more easily
image->fill(Color4ub(Color3::WHITE));
const uint numPixelsPerColor = m_textureSize/numColors;
CVF_ASSERT(numPixelsPerColor >= 1);
uint ic;
for (ic = 0; ic < numColors; ic++)
{
const Color4ub clr(m_colors[ic], 255);
uint ip;
for (ip = 0; ip < numPixelsPerColor; ip++)
{
image->setPixel(ic*numPixelsPerColor + ip, 0, clr);
}
}
// In cases where we're not using the entire texture we might get into problems with texture coordinate precision on the graphics hardware.
// Therefore we set one extra pixel with the 'highest' color in the color table
if (numColors*numPixelsPerColor < m_textureSize)
{
const Color4ub topClr(m_colors[numColors - 1], 255);
image->setPixel(numColors*numPixelsPerColor, 0, topClr);
}
return true;
}
} // namespace cvf