907 lines
31 KiB
C++
907 lines
31 KiB
C++
//============================================================================
|
|
// Copyright (c) Kitware, Inc.
|
|
// All rights reserved.
|
|
// See LICENSE.txt for details.
|
|
// This software is distributed WITHOUT ANY WARRANTY; without even
|
|
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
|
// PURPOSE. See the above copyright notice for more information.
|
|
//
|
|
// Copyright 2015 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
|
|
// Copyright 2015 UT-Battelle, LLC.
|
|
// Copyright 2015 Los Alamos National Security.
|
|
//
|
|
// Under the terms of Contract DE-NA0003525 with NTESS,
|
|
// the U.S. Government retains certain rights in this software.
|
|
//
|
|
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
|
|
// Laboratory (LANL), the U.S. Government retains certain rights in
|
|
// this software.
|
|
//============================================================================
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
#include <vtkm/cont/ColorTable.h>
|
|
#include <vtkm/cont/ColorTable.hxx>
|
|
#include <vtkm/cont/ColorTablePrivate.hxx>
|
|
|
|
namespace vtkm
|
|
{
|
|
namespace cont
|
|
{
|
|
|
|
namespace detail
|
|
{
|
|
bool loadColorTablePreset(vtkm::cont::ColorTable::Preset preset, vtkm::cont::ColorTable& table);
|
|
std::set<std::string> GetPresetNames();
|
|
bool loadColorTablePreset(std::string name, vtkm::cont::ColorTable& table);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ColorTable::ColorTable(vtkm::cont::ColorTable::Preset preset)
|
|
: Impl(std::make_shared<detail::ColorTableInternals>())
|
|
{
|
|
const bool loaded = this->LoadPreset(preset);
|
|
if (!loaded)
|
|
{ //if we failed to load the requested color table, call SetColorSpace
|
|
//so that the internal host side cache is constructed and we leave
|
|
//the constructor in a valid state. We use RGB as it is the default
|
|
//when the no parameter constructor is called
|
|
this->SetColorSpace(ColorSpace::LAB);
|
|
}
|
|
this->AddSegmentAlpha(this->Impl->TableRange.Min, 1.0f, this->Impl->TableRange.Max, 1.0f);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ColorTable::ColorTable(const std::string& name)
|
|
: Impl(std::make_shared<detail::ColorTableInternals>())
|
|
{
|
|
const bool loaded = this->LoadPreset(name);
|
|
if (!loaded)
|
|
{ //if we failed to load the requested color table, call SetColorSpace
|
|
//so that the internal host side cache is constructed and we leave
|
|
//the constructor in a valid state. We use RGB as it is the default
|
|
//when the no parameter constructor is called
|
|
this->SetColorSpace(ColorSpace::LAB);
|
|
}
|
|
this->AddSegmentAlpha(this->Impl->TableRange.Min, 1.0f, this->Impl->TableRange.Max, 1.0f);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ColorTable::ColorTable(ColorSpace space)
|
|
: Impl(std::make_shared<detail::ColorTableInternals>())
|
|
{
|
|
this->SetColorSpace(space);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ColorTable::ColorTable(const vtkm::Range& range,
|
|
const vtkm::Vec<float, 3>& rgb1,
|
|
const vtkm::Vec<float, 3>& rgb2,
|
|
ColorSpace space)
|
|
: Impl(std::make_shared<detail::ColorTableInternals>())
|
|
{
|
|
this->AddSegment(range.Min, rgb1, range.Max, rgb2);
|
|
this->AddSegmentAlpha(range.Min, 1.0f, range.Max, 1.0f);
|
|
this->SetColorSpace(space);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ColorTable::ColorTable(const vtkm::Range& range,
|
|
const vtkm::Vec<float, 4>& rgba1,
|
|
const vtkm::Vec<float, 4>& rgba2,
|
|
ColorSpace space)
|
|
: Impl(std::make_shared<detail::ColorTableInternals>())
|
|
{
|
|
vtkm::Vec<float, 3> rgb1(rgba1[0], rgba1[1], rgba1[2]);
|
|
vtkm::Vec<float, 3> rgb2(rgba2[0], rgba2[1], rgba2[2]);
|
|
this->AddSegment(range.Min, rgb1, range.Max, rgb2);
|
|
this->AddSegmentAlpha(range.Min, rgba1[3], range.Max, rgba2[3]);
|
|
this->SetColorSpace(space);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ColorTable::~ColorTable()
|
|
{
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool ColorTable::LoadPreset(vtkm::cont::ColorTable::Preset preset)
|
|
{
|
|
return detail::loadColorTablePreset(preset, *this);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
std::set<std::string> ColorTable::GetPresets() const
|
|
{
|
|
return detail::GetPresetNames();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool ColorTable::LoadPreset(const std::string& name)
|
|
{
|
|
return detail::loadColorTablePreset(name, *this);
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ColorTable ColorTable::MakeDeepCopy()
|
|
{
|
|
ColorTable dcopy(this->Impl->CSpace);
|
|
|
|
dcopy.Impl->TableRange = this->Impl->TableRange;
|
|
|
|
dcopy.Impl->HostSideCache->NaNColor = this->Impl->HostSideCache->NaNColor;
|
|
dcopy.Impl->HostSideCache->BelowRangeColor = this->Impl->HostSideCache->BelowRangeColor;
|
|
dcopy.Impl->HostSideCache->AboveRangeColor = this->Impl->HostSideCache->AboveRangeColor;
|
|
|
|
dcopy.Impl->HostSideCache->UseClamping = this->Impl->HostSideCache->UseClamping;
|
|
|
|
dcopy.Impl->ColorNodePos = this->Impl->ColorNodePos;
|
|
dcopy.Impl->ColorRGB = this->Impl->ColorRGB;
|
|
|
|
dcopy.Impl->OpacityNodePos = this->Impl->OpacityNodePos;
|
|
dcopy.Impl->OpacityAlpha = this->Impl->OpacityAlpha;
|
|
dcopy.Impl->OpacityMidSharp = this->Impl->OpacityMidSharp;
|
|
return dcopy;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
ColorSpace ColorTable::GetColorSpace() const
|
|
{
|
|
return this->Impl->CSpace;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void ColorTable::SetColorSpace(ColorSpace space)
|
|
{
|
|
if (this->Impl->CSpace != space || this->Impl->HostSideCache.get() == nullptr)
|
|
{
|
|
this->Impl->CSpace = space;
|
|
//Remove any existing host and execution data
|
|
|
|
using HandleType = vtkm::cont::VirtualObjectHandle<vtkm::exec::ColorTableBase>;
|
|
switch (space)
|
|
{
|
|
case vtkm::cont::ColorSpace::RGB:
|
|
{
|
|
auto* hostPortal = new vtkm::exec::ColorTableRGB();
|
|
this->Impl->ExecHandle.reset(new HandleType(hostPortal, false));
|
|
this->Impl->HostSideCache.reset(hostPortal);
|
|
break;
|
|
}
|
|
case vtkm::cont::ColorSpace::HSV:
|
|
{
|
|
auto* hostPortal = new vtkm::exec::ColorTableHSV();
|
|
this->Impl->ExecHandle.reset(new HandleType(hostPortal, false));
|
|
this->Impl->HostSideCache.reset(hostPortal);
|
|
break;
|
|
}
|
|
case vtkm::cont::ColorSpace::HSV_WRAP:
|
|
{
|
|
auto* hostPortal = new vtkm::exec::ColorTableHSVWrap();
|
|
this->Impl->ExecHandle.reset(new HandleType(hostPortal, false));
|
|
this->Impl->HostSideCache.reset(hostPortal);
|
|
break;
|
|
}
|
|
case vtkm::cont::ColorSpace::LAB:
|
|
{
|
|
auto* hostPortal = new vtkm::exec::ColorTableLab();
|
|
this->Impl->ExecHandle.reset(new HandleType(hostPortal, false));
|
|
this->Impl->HostSideCache.reset(hostPortal);
|
|
break;
|
|
}
|
|
case vtkm::cont::ColorSpace::DIVERGING:
|
|
{
|
|
auto* hostPortal = new vtkm::exec::ColorTableDiverging();
|
|
this->Impl->ExecHandle.reset(new HandleType(hostPortal, false));
|
|
this->Impl->HostSideCache.reset(hostPortal);
|
|
break;
|
|
}
|
|
default:
|
|
throw vtkm::cont::ErrorBadType("unkown vtkm::cont::ColorType requested");
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void ColorTable::SetClamping(bool state)
|
|
{
|
|
this->Impl->HostSideCache->UseClamping = state;
|
|
this->Impl->HostSideCache->Modified();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
bool ColorTable::GetClamping() const
|
|
{
|
|
return this->Impl->HostSideCache->UseClamping;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void ColorTable::SetBelowRangeColor(const vtkm::Vec<float, 3>& c)
|
|
{
|
|
this->Impl->HostSideCache->BelowRangeColor = c;
|
|
this->Impl->HostSideCache->Modified();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const vtkm::Vec<float, 3>& ColorTable::GetBelowRangeColor() const
|
|
{
|
|
return this->Impl->HostSideCache->BelowRangeColor;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void ColorTable::SetAboveRangeColor(const vtkm::Vec<float, 3>& c)
|
|
{
|
|
this->Impl->HostSideCache->AboveRangeColor = c;
|
|
this->Impl->HostSideCache->Modified();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const vtkm::Vec<float, 3>& ColorTable::GetAboveRangeColor() const
|
|
{
|
|
return this->Impl->HostSideCache->AboveRangeColor;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void ColorTable::SetNaNColor(const vtkm::Vec<float, 3>& c)
|
|
{
|
|
this->Impl->HostSideCache->NaNColor = c;
|
|
this->Impl->HostSideCache->Modified();
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
const vtkm::Vec<float, 3>& ColorTable::GetNaNColor() const
|
|
{
|
|
return this->Impl->HostSideCache->NaNColor;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
void ColorTable::Clear()
|
|
{
|
|
this->ClearColors();
|
|
this->ClearAlpha();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
void ColorTable::ClearColors()
|
|
{
|
|
this->Impl->ColorNodePos.clear();
|
|
this->Impl->ColorRGB.clear();
|
|
this->Impl->ColorArraysChanged = true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
void ColorTable::ClearAlpha()
|
|
{
|
|
this->Impl->OpacityNodePos.clear();
|
|
this->Impl->OpacityAlpha.clear();
|
|
this->Impl->OpacityMidSharp.clear();
|
|
this->Impl->OpacityArraysChanged = true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
void ColorTable::ReverseColors()
|
|
{
|
|
std::reverse(this->Impl->ColorRGB.begin(), this->Impl->ColorRGB.end());
|
|
this->Impl->ColorArraysChanged = true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
void ColorTable::ReverseAlpha()
|
|
{
|
|
std::reverse(this->Impl->OpacityAlpha.begin(), this->Impl->OpacityAlpha.end());
|
|
//To keep the shape correct the mid and sharp values of the last node are not included in the reversal
|
|
std::reverse(this->Impl->OpacityMidSharp.begin(), this->Impl->OpacityMidSharp.end() - 1);
|
|
this->Impl->OpacityArraysChanged = true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
const vtkm::Range& ColorTable::GetRange() const
|
|
{
|
|
return this->Impl->TableRange;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
void ColorTable::RescaleToRange(const vtkm::Range& r)
|
|
{
|
|
if (r == this->GetRange())
|
|
{
|
|
return;
|
|
}
|
|
//make sure range has space.
|
|
auto newRange = adjustRange(r);
|
|
|
|
//slam control points down to 0.0 - 1.0, and than rescale to new range
|
|
const double minv = this->GetRange().Min;
|
|
const double oldScale = this->GetRange().Length();
|
|
const double newScale = newRange.Length();
|
|
VTKM_ASSERT(oldScale > 0);
|
|
VTKM_ASSERT(newScale > 0);
|
|
for (auto i = this->Impl->ColorNodePos.begin(); i != this->Impl->ColorNodePos.end(); ++i)
|
|
{
|
|
const auto t = (*i - minv) / oldScale;
|
|
*i = (t * newScale) + newRange.Min;
|
|
}
|
|
for (auto i = this->Impl->OpacityNodePos.begin(); i != this->Impl->OpacityNodePos.end(); ++i)
|
|
{
|
|
const auto t = (*i - minv) / oldScale;
|
|
*i = (t * newScale) + newRange.Min;
|
|
}
|
|
|
|
this->Impl->ColorArraysChanged = true;
|
|
this->Impl->OpacityArraysChanged = true;
|
|
this->Impl->TableRange = newRange;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::AddPoint(double x, const vtkm::Vec<float, 3>& rgb)
|
|
{
|
|
if (outside_range(rgb))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
std::size_t index = 0;
|
|
if (this->Impl->ColorNodePos.size() == 0 || this->Impl->ColorNodePos.back() < x)
|
|
{
|
|
this->Impl->ColorNodePos.emplace_back(x);
|
|
this->Impl->ColorRGB.emplace_back(rgb);
|
|
index = this->Impl->ColorNodePos.size();
|
|
}
|
|
else
|
|
{
|
|
auto begin = this->Impl->ColorNodePos.begin();
|
|
auto pos = std::lower_bound(begin, this->Impl->ColorNodePos.end(), x);
|
|
index = static_cast<std::size_t>(std::distance(begin, pos));
|
|
|
|
if (*pos == x)
|
|
{
|
|
this->Impl->ColorRGB[index] = rgb;
|
|
}
|
|
else
|
|
{
|
|
this->Impl->ColorNodePos.emplace(pos, x);
|
|
this->Impl->ColorRGB.emplace(this->Impl->ColorRGB.begin() + std::distance(begin, pos), rgb);
|
|
}
|
|
}
|
|
this->Impl->TableRange.Include(x); //update range to include x
|
|
this->Impl->ColorArraysChanged = true;
|
|
return static_cast<vtkm::Int32>(index);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::AddPointHSV(double x, const vtkm::Vec<float, 3>& hsv)
|
|
{
|
|
return this->AddPoint(x, hsvTorgb(hsv));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::AddSegment(double x1,
|
|
const vtkm::Vec<float, 3>& rgb1,
|
|
double x2,
|
|
const vtkm::Vec<float, 3>& rgb2)
|
|
{
|
|
if (outside_range(rgb1, rgb2))
|
|
{
|
|
return -1;
|
|
}
|
|
if (this->Impl->ColorNodePos.size() > 0)
|
|
{
|
|
//Todo:
|
|
// - This could be optimized so we do 2 less lower_bound calls when
|
|
// the table already exists
|
|
|
|
//When we add a segment we remove all points that are inside the line
|
|
|
|
auto nodeBegin = this->Impl->ColorNodePos.begin();
|
|
auto nodeEnd = this->Impl->ColorNodePos.end();
|
|
|
|
auto rgbBegin = this->Impl->ColorRGB.begin();
|
|
|
|
auto nodeStart = std::lower_bound(nodeBegin, nodeEnd, x1);
|
|
auto nodeStop = std::lower_bound(nodeBegin, nodeEnd, x2);
|
|
|
|
auto rgbStart = rgbBegin + std::distance(nodeBegin, nodeStart);
|
|
auto rgbStop = rgbBegin + std::distance(nodeBegin, nodeStop);
|
|
|
|
//erase is exclusive so if end->x == x2 it will be kept around, and
|
|
//than we will update it in AddPoint
|
|
this->Impl->ColorNodePos.erase(nodeStart, nodeStop);
|
|
this->Impl->ColorRGB.erase(rgbStart, rgbStop);
|
|
}
|
|
vtkm::Int32 pos = this->AddPoint(x1, rgb1);
|
|
this->AddPoint(x2, rgb2);
|
|
return pos;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::AddSegmentHSV(double x1,
|
|
const vtkm::Vec<float, 3>& hsv1,
|
|
double x2,
|
|
const vtkm::Vec<float, 3>& hsv2)
|
|
{
|
|
return this->AddSegment(x1, hsvTorgb(hsv1), x2, hsvTorgb(hsv2));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::GetPoint(vtkm::Int32 index, vtkm::Vec<double, 4>& data) const
|
|
{
|
|
std::size_t i = static_cast<std::size_t>(index);
|
|
const std::size_t size = this->Impl->ColorNodePos.size();
|
|
if (index < 0 || i >= size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto& pos = this->Impl->ColorNodePos[i];
|
|
const auto& rgb = this->Impl->ColorRGB[i];
|
|
|
|
data[0] = pos;
|
|
data[1] = rgb[0];
|
|
data[2] = rgb[1];
|
|
data[3] = rgb[2];
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::UpdatePoint(vtkm::Int32 index, const vtkm::Vec<double, 4>& data)
|
|
{
|
|
//skip data[0] as we don't care about position
|
|
if (outside_range(data[1], data[2], data[3]))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
std::size_t i = static_cast<std::size_t>(index);
|
|
const std::size_t size = this->Impl->ColorNodePos.size();
|
|
if (index < 0 || i >= size)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
//When updating the first question is has the relative position of the point changed?
|
|
//If it hasn't we can quickly just update the RGB value
|
|
auto oldPos = this->Impl->ColorNodePos.begin() + index;
|
|
auto newPos =
|
|
std::lower_bound(this->Impl->ColorNodePos.begin(), this->Impl->ColorNodePos.end(), data[0]);
|
|
if (oldPos == newPos)
|
|
{ //node's relative location hasn't changed
|
|
this->Impl->ColorArraysChanged = true;
|
|
auto& rgb = this->Impl->ColorRGB[i];
|
|
*newPos = data[0];
|
|
rgb[0] = static_cast<float>(data[1]);
|
|
rgb[1] = static_cast<float>(data[2]);
|
|
rgb[2] = static_cast<float>(data[3]);
|
|
return index;
|
|
}
|
|
else
|
|
{ //remove the point, and add the new values as the relative location is different
|
|
this->RemovePoint(index);
|
|
vtkm::Vec<float, 3> newrgb(
|
|
static_cast<float>(data[1]), static_cast<float>(data[2]), static_cast<float>(data[3]));
|
|
return this->AddPoint(data[0], newrgb);
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::RemovePoint(double x)
|
|
{
|
|
auto begin = this->Impl->ColorNodePos.begin();
|
|
auto pos = std::lower_bound(begin, this->Impl->ColorNodePos.end(), x);
|
|
return this->RemovePoint(static_cast<vtkm::Int32>(std::distance(begin, pos)));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::RemovePoint(vtkm::Int32 index)
|
|
{
|
|
std::size_t i = static_cast<std::size_t>(index);
|
|
const std::size_t size = this->Impl->ColorNodePos.size();
|
|
if (index < 0 || i >= size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
this->Impl->ColorNodePos.erase(this->Impl->ColorNodePos.begin() + index);
|
|
this->Impl->ColorRGB.erase(this->Impl->ColorRGB.begin() + index);
|
|
this->Impl->ColorArraysChanged = true;
|
|
this->Impl->RecalculateRange();
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::GetNumberOfPoints() const
|
|
{
|
|
return static_cast<vtkm::Int32>(this->Impl->ColorNodePos.size());
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::AddPointAlpha(double x, float alpha, float midpoint, float sharpness)
|
|
{
|
|
if (outside_range(alpha, midpoint, sharpness))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
const vtkm::Vec<float, 2> midsharp(midpoint, sharpness);
|
|
std::size_t index = 0;
|
|
if (this->Impl->OpacityNodePos.size() == 0 || this->Impl->OpacityNodePos.back() < x)
|
|
{
|
|
this->Impl->OpacityNodePos.emplace_back(x);
|
|
this->Impl->OpacityAlpha.emplace_back(alpha);
|
|
this->Impl->OpacityMidSharp.emplace_back(midsharp);
|
|
index = this->Impl->OpacityNodePos.size();
|
|
}
|
|
else
|
|
{
|
|
auto begin = this->Impl->OpacityNodePos.begin();
|
|
auto pos = std::lower_bound(begin, this->Impl->OpacityNodePos.end(), x);
|
|
index = static_cast<std::size_t>(std::distance(begin, pos));
|
|
if (*pos == x)
|
|
{
|
|
this->Impl->OpacityAlpha[index] = alpha;
|
|
this->Impl->OpacityMidSharp[index] = midsharp;
|
|
}
|
|
else
|
|
{
|
|
this->Impl->OpacityNodePos.emplace(pos, x);
|
|
this->Impl->OpacityAlpha.emplace(this->Impl->OpacityAlpha.begin() + std::distance(begin, pos),
|
|
alpha);
|
|
this->Impl->OpacityMidSharp.emplace(
|
|
this->Impl->OpacityMidSharp.begin() + std::distance(begin, pos), midsharp);
|
|
}
|
|
}
|
|
this->Impl->OpacityArraysChanged = true;
|
|
this->Impl->TableRange.Include(x); //update range to include x
|
|
return static_cast<vtkm::Int32>(index);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::AddSegmentAlpha(double x1,
|
|
float alpha1,
|
|
double x2,
|
|
float alpha2,
|
|
const vtkm::Vec<float, 2>& mid_sharp1,
|
|
const vtkm::Vec<float, 2>& mid_sharp2)
|
|
{
|
|
if (outside_range(alpha1, alpha2, mid_sharp1, mid_sharp2))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (this->Impl->OpacityNodePos.size() > 0)
|
|
{
|
|
//Todo:
|
|
// - This could be optimized so we do 2 less lower_bound calls when
|
|
// the table already exists
|
|
|
|
//When we add a segment we remove all points that are inside the line
|
|
|
|
auto nodeBegin = this->Impl->OpacityNodePos.begin();
|
|
auto nodeEnd = this->Impl->OpacityNodePos.end();
|
|
|
|
auto alphaBegin = this->Impl->OpacityAlpha.begin();
|
|
auto midBegin = this->Impl->OpacityMidSharp.begin();
|
|
|
|
auto nodeStart = std::lower_bound(nodeBegin, nodeEnd, x1);
|
|
auto nodeStop = std::lower_bound(nodeBegin, nodeEnd, x2);
|
|
|
|
auto alphaStart = alphaBegin + std::distance(nodeBegin, nodeStart);
|
|
auto alphaStop = alphaBegin + std::distance(nodeBegin, nodeStop);
|
|
auto midStart = midBegin + std::distance(nodeBegin, nodeStart);
|
|
auto midStop = midBegin + std::distance(nodeBegin, nodeStop);
|
|
|
|
//erase is exclusive so if end->x == x2 it will be kept around, and
|
|
//than we will update it in AddPoint
|
|
this->Impl->OpacityNodePos.erase(nodeStart, nodeStop);
|
|
this->Impl->OpacityAlpha.erase(alphaStart, alphaStop);
|
|
this->Impl->OpacityMidSharp.erase(midStart, midStop);
|
|
}
|
|
|
|
vtkm::Int32 pos = this->AddPointAlpha(x1, alpha1, mid_sharp1[0], mid_sharp1[1]);
|
|
this->AddPointAlpha(x2, alpha2, mid_sharp2[0], mid_sharp2[1]);
|
|
return pos;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::GetPointAlpha(vtkm::Int32 index, vtkm::Vec<double, 4>& data) const
|
|
{
|
|
std::size_t i = static_cast<std::size_t>(index);
|
|
const std::size_t size = this->Impl->OpacityNodePos.size();
|
|
if (index < 0 || i >= size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
const auto& pos = this->Impl->OpacityNodePos[i];
|
|
const auto& alpha = this->Impl->OpacityAlpha[i];
|
|
const auto& midsharp = this->Impl->OpacityMidSharp[i];
|
|
|
|
data[0] = pos;
|
|
data[1] = alpha;
|
|
data[2] = midsharp[0];
|
|
data[3] = midsharp[1];
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::UpdatePointAlpha(vtkm::Int32 index, const vtkm::Vec<double, 4>& data)
|
|
{
|
|
//skip data[0] as we don't care about position
|
|
if (outside_range(data[1], data[2], data[3]))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
std::size_t i = static_cast<std::size_t>(index);
|
|
const std::size_t size = this->Impl->OpacityNodePos.size();
|
|
if (index < 0 || i >= size)
|
|
{
|
|
return -1;
|
|
}
|
|
//When updating the first question is has the relative position of the point changed?
|
|
//If it hasn't we can quickly just update the RGB value
|
|
auto oldPos = this->Impl->OpacityNodePos.begin() + index;
|
|
auto newPos =
|
|
std::lower_bound(this->Impl->OpacityNodePos.begin(), this->Impl->OpacityNodePos.end(), data[0]);
|
|
if (oldPos == newPos)
|
|
{ //node's relative location hasn't changed
|
|
this->Impl->OpacityArraysChanged = true;
|
|
auto& alpha = this->Impl->OpacityAlpha[i];
|
|
auto& midsharp = this->Impl->OpacityMidSharp[i];
|
|
*newPos = data[0];
|
|
alpha = static_cast<float>(data[1]);
|
|
midsharp[0] = static_cast<float>(data[2]);
|
|
midsharp[1] = static_cast<float>(data[3]);
|
|
return index;
|
|
}
|
|
else
|
|
{ //remove the point, and add the new values as the relative location is different
|
|
this->RemovePointAlpha(index);
|
|
return this->AddPointAlpha(data[0],
|
|
static_cast<float>(data[1]),
|
|
static_cast<float>(data[2]),
|
|
static_cast<float>(data[3]));
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::RemovePointAlpha(double x)
|
|
{
|
|
auto begin = this->Impl->OpacityNodePos.begin();
|
|
auto pos = std::lower_bound(begin, this->Impl->OpacityNodePos.end(), x);
|
|
return this->RemovePointAlpha(static_cast<vtkm::Int32>(std::distance(begin, pos)));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::RemovePointAlpha(vtkm::Int32 index)
|
|
{
|
|
std::size_t i = static_cast<std::size_t>(index);
|
|
const std::size_t size = this->Impl->OpacityNodePos.size();
|
|
if (index < 0 || i >= size)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
this->Impl->OpacityNodePos.erase(this->Impl->OpacityNodePos.begin() + index);
|
|
this->Impl->OpacityAlpha.erase(this->Impl->OpacityAlpha.begin() + index);
|
|
this->Impl->OpacityMidSharp.erase(this->Impl->OpacityMidSharp.begin() + index);
|
|
this->Impl->OpacityArraysChanged = true;
|
|
this->Impl->RecalculateRange();
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Int32 ColorTable::GetNumberOfPointsAlpha() const
|
|
{
|
|
return static_cast<vtkm::Int32>(this->Impl->OpacityNodePos.size());
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::FillColorTableFromDataPointer(vtkm::Int32 n, const double* ptr)
|
|
{
|
|
if (n <= 0 || ptr == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
this->ClearColors();
|
|
|
|
std::size_t size = static_cast<std::size_t>(n / 4);
|
|
this->Impl->ColorNodePos.reserve(size);
|
|
this->Impl->ColorRGB.reserve(size);
|
|
for (std::size_t i = 0; i < size; ++i)
|
|
{ //allows us to support unsorted arrays
|
|
vtkm::Vec<float, 3> rgb(
|
|
static_cast<float>(ptr[1]), static_cast<float>(ptr[2]), static_cast<float>(ptr[3]));
|
|
this->AddPoint(ptr[0], rgb);
|
|
ptr += 4;
|
|
}
|
|
this->Impl->ColorArraysChanged = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::FillColorTableFromDataPointer(vtkm::Int32 n, const float* ptr)
|
|
{
|
|
if (n <= 0 || ptr == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
this->ClearColors();
|
|
|
|
std::size_t size = static_cast<std::size_t>(n / 4);
|
|
this->Impl->ColorNodePos.reserve(size);
|
|
this->Impl->ColorRGB.reserve(size);
|
|
for (std::size_t i = 0; i < size; ++i)
|
|
{ //allows us to support unsorted arrays
|
|
vtkm::Vec<float, 3> rgb(ptr[1], ptr[2], ptr[3]);
|
|
this->AddPoint(ptr[0], rgb);
|
|
ptr += 4;
|
|
}
|
|
this->Impl->ColorArraysChanged = true;
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::FillOpacityTableFromDataPointer(vtkm::Int32 n, const double* ptr)
|
|
{
|
|
if (n <= 0 || ptr == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
this->ClearAlpha();
|
|
|
|
std::size_t size = static_cast<std::size_t>(n / 2);
|
|
this->Impl->OpacityNodePos.reserve(size);
|
|
this->Impl->OpacityAlpha.reserve(size);
|
|
this->Impl->OpacityMidSharp.reserve(size);
|
|
for (std::size_t i = 0; i < size; ++i)
|
|
{ //allows us to support unsorted arrays
|
|
this->AddPointAlpha(
|
|
ptr[0], static_cast<float>(ptr[1]), static_cast<float>(ptr[2]), static_cast<float>(ptr[3]));
|
|
ptr += 4;
|
|
}
|
|
|
|
this->Impl->OpacityArraysChanged = true;
|
|
return true;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::FillOpacityTableFromDataPointer(vtkm::Int32 n, const float* ptr)
|
|
{
|
|
if (n <= 0 || ptr == nullptr)
|
|
{
|
|
return false;
|
|
}
|
|
this->ClearAlpha();
|
|
|
|
|
|
std::size_t size = static_cast<std::size_t>(n / 2);
|
|
this->Impl->OpacityNodePos.reserve(size);
|
|
this->Impl->OpacityAlpha.reserve(size);
|
|
this->Impl->OpacityMidSharp.reserve(size);
|
|
for (std::size_t i = 0; i < size; ++i)
|
|
{ //allows us to support unsorted arrays
|
|
this->AddPointAlpha(ptr[0], ptr[1], ptr[2], ptr[3]);
|
|
ptr += 4;
|
|
}
|
|
this->Impl->OpacityArraysChanged = true;
|
|
return true;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::Sample(vtkm::Int32 numSamples,
|
|
vtkm::cont::ColorTableSamplesRGBA& samples,
|
|
double tolerance) const
|
|
{
|
|
if (numSamples <= 1)
|
|
{
|
|
return false;
|
|
}
|
|
samples.NumberOfSamples = numSamples;
|
|
samples.SampleRange = this->GetRange();
|
|
return sampleColorTable(this, numSamples, samples.Samples, tolerance, true);
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::Sample(vtkm::Int32 numSamples,
|
|
vtkm::cont::ColorTableSamplesRGB& samples,
|
|
double tolerance) const
|
|
{
|
|
if (numSamples <= 1)
|
|
{
|
|
return false;
|
|
}
|
|
samples.NumberOfSamples = numSamples;
|
|
samples.SampleRange = this->GetRange();
|
|
return sampleColorTable(this, numSamples, samples.Samples, tolerance, true);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::Sample(vtkm::Int32 numSamples,
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>& colors,
|
|
double tolerance) const
|
|
{
|
|
if (numSamples <= 1)
|
|
{
|
|
return false;
|
|
}
|
|
return sampleColorTable(this, numSamples, colors, tolerance, false);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
bool ColorTable::Sample(vtkm::Int32 numSamples,
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>& colors,
|
|
double tolerance) const
|
|
{
|
|
if (numSamples <= 1)
|
|
{
|
|
return false;
|
|
}
|
|
return sampleColorTable(this, numSamples, colors, tolerance, false);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::cont::VirtualObjectHandle<vtkm::exec::ColorTableBase>* ColorTable::GetHandleForExecution()
|
|
const
|
|
{
|
|
//Only rebuild the array handles that have changed since the last time
|
|
//we have modified or color / opacity information
|
|
if (this->Impl->ColorArraysChanged)
|
|
{
|
|
this->Impl->ColorPosHandle = vtkm::cont::make_ArrayHandle(this->Impl->ColorNodePos);
|
|
this->Impl->ColorRGBHandle = vtkm::cont::make_ArrayHandle(this->Impl->ColorRGB);
|
|
}
|
|
|
|
if (this->Impl->OpacityArraysChanged)
|
|
{
|
|
this->Impl->OpacityPosHandle = vtkm::cont::make_ArrayHandle(this->Impl->OpacityNodePos);
|
|
this->Impl->OpacityAlphaHandle = vtkm::cont::make_ArrayHandle(this->Impl->OpacityAlpha);
|
|
this->Impl->OpacityMidSharpHandle = vtkm::cont::make_ArrayHandle(this->Impl->OpacityMidSharp);
|
|
}
|
|
|
|
if (this->Impl->ColorArraysChanged || this->Impl->OpacityArraysChanged)
|
|
{
|
|
vtkm::cont::TryExecute(
|
|
detail::transfer_color_table_to_device{}, this->Impl->HostSideCache.get(), this->Impl.get());
|
|
|
|
this->Impl->HostSideCache->Modified();
|
|
}
|
|
|
|
this->Impl->ColorArraysChanged = false;
|
|
this->Impl->OpacityArraysChanged = false;
|
|
return this->Impl->ExecHandle.get();
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
vtkm::Id ColorTable::GetModifiedCount() const
|
|
{
|
|
return this->Impl->HostSideCache->GetModifiedCount();
|
|
}
|
|
|
|
|
|
/*
|
|
#define ColorTableExportMapFunctions(T) \
|
|
template VTKM_CONT_EXPORT bool ColorTable::Map( \
|
|
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>&, \
|
|
const vtkm::cont::ColorTableSamplesRGBA&, \
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>&) const; \
|
|
template VTKM_CONT_EXPORT bool ColorTable::Map( \
|
|
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>&, \
|
|
const vtkm::cont::ColorTableSamplesRGB&, \
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>&) const; \
|
|
template VTKM_CONT_EXPORT bool ColorTable::Map( \
|
|
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>&, \
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>&) const; \
|
|
template VTKM_CONT_EXPORT bool ColorTable::Map( \
|
|
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagBasic>&, \
|
|
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>&) const;
|
|
ColorTableExportMapFunctions(char);
|
|
ColorTableExportMapFunctions(vtkm::UInt8);
|
|
ColorTableExportMapFunctions(vtkm::Int8);
|
|
ColorTableExportMapFunctions(vtkm::Float32);
|
|
ColorTableExportMapFunctions(vtkm::Float64);
|
|
#undef ColorTableExportMapFunctions
|
|
*/
|
|
}
|
|
} //namespace vtkm::cont
|