Commit 944bc3c0 authored by Robert Maynard's avatar Robert Maynard

Introduce vtkm::cont::ColorTable replacing vtkm::rendering::ColorTable

The new and improved vtkm::cont::ColorTable provides a more feature complete
color table implementation that is modeled after
vtkDiscretizableColorTransferFunction. This class therefore supports different
color spaces ( rgb, lab, hsv, diverging ) and supports execution across all
device adapters.
parent 428f7568
......@@ -153,7 +153,7 @@ camera.SetViewUp(vtkm::make_Vec(0.f, 1.f, 0.f));
camera.SetClippingRange(1.f, 100.f);
camera.SetFieldOfView(60.f);
camera.SetPosition(totalExtent*(mag * 2.f));
vtkm::rendering::ColorTable colorTable("thermal");
vtkm::cont::ColorTable colorTable("inferno");
// Create a mapper, canvas and view that will be used to render the scene
vtkm::rendering::Scene scene;
......
......@@ -35,7 +35,7 @@
#include <iostream>
void makeScene(const vtkm::cont::DataSet& inputData,
const vtkm::rendering::ColorTable& colorTable,
const vtkm::cont::ColorTable& colorTable,
const std::string& fieldName,
vtkm::rendering::Scene& scene)
{
......@@ -100,7 +100,7 @@ int main(int argc, char* argv[])
camera.SetClippingRange(1.f, 100.f);
camera.SetFieldOfView(60.f);
camera.SetPosition(totalExtent * (mag * 2.f));
vtkm::rendering::ColorTable colorTable("thermal");
vtkm::cont::ColorTable colorTable("inferno");
// Create a scene for rendering the input data
vtkm::rendering::Scene scene;
......
......@@ -42,8 +42,8 @@
#include <GL/glut.h>
#endif
#include <vtkm/cont/ColorTable.h>
#include <vtkm/rendering/CanvasGL.h>
#include <vtkm/rendering/ColorTable.h>
#include <vtkm/rendering/MapperGL.h>
#include <vtkm/rendering/View3D.h>
......@@ -141,7 +141,7 @@ int main(int argc, char* argv[])
scene.AddActor(vtkm::rendering::Actor(ds.GetCellSet(),
ds.GetCoordinateSystem(),
ds.GetField("pointvar"),
vtkm::rendering::ColorTable("thermal")));
vtkm::cont::ColorTable("inferno")));
//Create vtkm rendering stuff.
view = new vtkm::rendering::View3D(scene, mapper, canvas, bg);
......
......@@ -348,7 +348,7 @@ void RenderRTTest(const vtkm::cont::DataSet& ds, int N)
using V3 = vtkm::rendering::View3D;
//std::cout<<"Render: "<<i<<std::endl;
vtkm::rendering::ColorTable colorTable("thermal");
vtkm::cont::ColorTable colorTable("inferno");
vtkm::rendering::testing::Render<M, C, V3>(ds, "scalar", colorTable);
}
}
......@@ -366,7 +366,7 @@ void RenderVolTest(const vtkm::cont::DataSet& ds, int N)
using V3 = vtkm::rendering::View3D;
//std::cout<<"Render: "<<i<<std::endl;
vtkm::rendering::ColorTable colorTable("thermal");
vtkm::cont::ColorTable colorTable("inferno");
vtkm::rendering::testing::Render<M, C, V3>(ds, "scalar", colorTable);
}
}
......
......@@ -79,8 +79,24 @@ struct BenchRayTracing
Tracer.SetData(Coords.GetData(), Indices, field, NumberOfTriangles, range, bounds);
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>> temp;
vtkm::cont::ColorTable table("cool to warm");
table.Sample(100, temp);
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float32, 4>> colors;
vtkm::rendering::ColorTable("cool2warm").Sample(100, colors);
colors.Allocate(100);
auto portal = colors.GetPortalControl();
auto colorPortal = temp.GetPortalConstControl();
constexpr vtkm::Float32 conversionToFloatSpace = (1.0f / 255.0f);
for (vtkm::Id i = 0; i < 100; ++i)
{
auto color = colorPortal.Get(i);
vtkm::Vec<vtkm::Float32, 4> t(color[0] * conversionToFloatSpace,
color[1] * conversionToFloatSpace,
color[2] * conversionToFloatSpace,
color[3] * conversionToFloatSpace);
portal.Set(i, t);
}
Tracer.SetColorMap(colors);
Tracer.Render(Rays);
......
......@@ -55,6 +55,8 @@ set(headers
CellSetSingleType.h
CellSetStructured.h
CellSetPermutation.h
ColorTable.h
ColorTableSamples.h
CoordinateSystem.h
DataSet.h
DataSetBuilderExplicit.h
......@@ -110,6 +112,7 @@ set(sources
internal/SimplePolymorphicContainer.cxx
internal/ArrayManagerExecutionShareWithControl.cxx
internal/ArrayHandleBasicImpl.cxx
PresetColorTables.cxx
StorageBasic.cxx
)
......@@ -118,6 +121,7 @@ set(sources
set(device_sources
ArrayRangeCompute.cxx
CellSetExplicit.cxx
ColorTable.cxx
CoordinateSystem.cxx
DataSet.cxx
MultiBlock.cxx
......@@ -162,6 +166,5 @@ if(VTKm_ENABLE_CUDA)
add_dependencies(vtkm_cont vtkm_cont_cuda)
endif()
#-----------------------------------------------------------------------------
add_subdirectory(testing)
This diff is collapsed.
This diff is collapsed.
//============================================================================
// 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 <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandleTransform.h>
#include <vtkm/cont/TryExecute.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/colorconversion/LookupTable.h>
#include <vtkm/worklet/colorconversion/Portals.h>
#include <vtkm/worklet/colorconversion/TransferFunction.h>
#include <vtkm/exec/ColorTable.h>
namespace vtkm
{
namespace cont
{
namespace detail
{
struct map_color_table
{
template <typename DeviceAdapter, typename ColorTable, typename... Args>
bool operator()(DeviceAdapter device, ColorTable&& colors, Args&&... args) const
{
using namespace vtkm::worklet::colorconversion;
TransferFunction transfer(colors->PrepareForExecution(device));
vtkm::worklet::DispatcherMapField<TransferFunction, DeviceAdapter> dispatcher(transfer);
dispatcher.Invoke(std::forward<Args>(args)...);
return true;
}
};
struct map_color_table_with_samples
{
template <typename DeviceAdapter, typename... Args>
bool operator()(DeviceAdapter,
const vtkm::worklet::colorconversion::LookupTable& lookupTable,
Args&&... args) const
{
using namespace vtkm::worklet::colorconversion;
vtkm::worklet::DispatcherMapField<LookupTable, DeviceAdapter> dispatcher(lookupTable);
dispatcher.Invoke(std::forward<Args>(args)...);
return true;
}
};
struct transfer_color_table_to_device
{
template <typename DeviceAdapter, typename ColorTableInternals>
bool operator()(DeviceAdapter device,
vtkm::exec::ColorTableBase* portal,
ColorTableInternals* internals)
{
auto p1 = internals->ColorPosHandle.PrepareForInput(device);
auto p2 = internals->ColorRGBHandle.PrepareForInput(device);
auto p3 = internals->OpacityPosHandle.PrepareForInput(device);
auto p4 = internals->OpacityAlphaHandle.PrepareForInput(device);
auto p5 = internals->OpacityMidSharpHandle.PrepareForInput(device);
//The rest of the data member on portal are set when-ever the user
//modifies the ColorTable instance and don't need to specified here
portal->ColorSize = static_cast<vtkm::Int32>(internals->ColorPosHandle.GetNumberOfValues());
portal->OpacitySize = static_cast<vtkm::Int32>(internals->OpacityPosHandle.GetNumberOfValues());
#if !defined(VTKM_MSVC) || (defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 0)
portal->ColorNodes = vtkm::cont::ArrayPortalToIteratorBegin(p1);
portal->RGB = vtkm::cont::ArrayPortalToIteratorBegin(p2);
portal->ONodes = vtkm::cont::ArrayPortalToIteratorBegin(p3);
portal->Alpha = vtkm::cont::ArrayPortalToIteratorBegin(p4);
portal->MidSharp = vtkm::cont::ArrayPortalToIteratorBegin(p5);
#else
//ArrayPortalToIteratorBegin is returning a checked_array_iterator so
//we need to grab the underlying pointer
portal->ColorNodes = vtkm::cont::ArrayPortalToIteratorBegin(p1).base();
portal->RGB = vtkm::cont::ArrayPortalToIteratorBegin(p2).base();
portal->ONodes = vtkm::cont::ArrayPortalToIteratorBegin(p3).base();
portal->Alpha = vtkm::cont::ArrayPortalToIteratorBegin(p4).base();
portal->MidSharp = vtkm::cont::ArrayPortalToIteratorBegin(p5).base();
#endif
return true;
}
};
}
//---------------------------------------------------------------------------
template <typename T, typename S>
bool ColorTable::Map(const vtkm::cont::ArrayHandle<T, S>& values,
const vtkm::cont::ColorTableSamplesRGBA& samples,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>& rgbaOut) const
{
if (samples.NumberOfSamples <= 0)
{
return false;
}
vtkm::worklet::colorconversion::LookupTable lookupTable(samples);
return vtkm::cont::TryExecute(
detail::map_color_table_with_samples{}, lookupTable, values, samples.Samples, rgbaOut);
}
//---------------------------------------------------------------------------
template <typename T, typename S>
bool ColorTable::Map(const vtkm::cont::ArrayHandle<T, S>& values,
const vtkm::cont::ColorTableSamplesRGB& samples,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>& rgbOut) const
{
if (samples.NumberOfSamples <= 0)
{
return false;
}
vtkm::worklet::colorconversion::LookupTable lookupTable(samples);
return vtkm::cont::TryExecute(
detail::map_color_table_with_samples{}, lookupTable, values, samples.Samples, rgbOut);
}
//---------------------------------------------------------------------------
template <typename T, int N, typename S>
bool ColorTable::MapMagnitude(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, S>& values,
const vtkm::cont::ColorTableSamplesRGBA& samples,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>& rgbaOut) const
{
using namespace vtkm::worklet::colorconversion;
return this->Map(
vtkm::cont::make_ArrayHandleTransform(values, MagnitudePortal()), samples, rgbaOut);
}
//---------------------------------------------------------------------------
template <typename T, int N, typename S>
bool ColorTable::MapMagnitude(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, S>& values,
const vtkm::cont::ColorTableSamplesRGB& samples,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>& rgbOut) const
{
using namespace vtkm::worklet::colorconversion;
return this->Map(
vtkm::cont::make_ArrayHandleTransform(values, MagnitudePortal()), samples, rgbOut);
}
//---------------------------------------------------------------------------
template <typename T, int N, typename S>
bool ColorTable::MapComponent(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, S>& values,
vtkm::IdComponent comp,
const vtkm::cont::ColorTableSamplesRGBA& samples,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>& rgbaOut) const
{
using namespace vtkm::worklet::colorconversion;
return this->Map(
vtkm::cont::make_ArrayHandleTransform(values, ComponentPortal(comp)), samples, rgbaOut);
}
//---------------------------------------------------------------------------
template <typename T, int N, typename S>
bool ColorTable::MapComponent(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, S>& values,
vtkm::IdComponent comp,
const vtkm::cont::ColorTableSamplesRGB& samples,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>& rgbOut) const
{
using namespace vtkm::worklet::colorconversion;
return this->Map(
vtkm::cont::make_ArrayHandleTransform(values, ComponentPortal(comp)), samples, rgbOut);
}
//---------------------------------------------------------------------------
template <typename T, typename S>
bool ColorTable::Map(const vtkm::cont::ArrayHandle<T, S>& values,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>& rgbaOut) const
{
return vtkm::cont::TryExecute(
detail::map_color_table{}, this->GetHandleForExecution(), values, rgbaOut);
}
//---------------------------------------------------------------------------
template <typename T, typename S>
bool ColorTable::Map(const vtkm::cont::ArrayHandle<T, S>& values,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>& rgbOut) const
{
return vtkm::cont::TryExecute(
detail::map_color_table{}, this->GetHandleForExecution(), values, rgbOut);
}
//---------------------------------------------------------------------------
template <typename T, int N, typename S>
bool ColorTable::MapMagnitude(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, S>& values,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>& rgbaOut) const
{
using namespace vtkm::worklet::colorconversion;
return this->Map(vtkm::cont::make_ArrayHandleTransform(values, MagnitudePortal()), rgbaOut);
}
//---------------------------------------------------------------------------
template <typename T, int N, typename S>
bool ColorTable::MapMagnitude(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, S>& values,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>& rgbOut) const
{
using namespace vtkm::worklet::colorconversion;
return this->Map(vtkm::cont::make_ArrayHandleTransform(values, MagnitudePortal()), rgbOut);
}
//---------------------------------------------------------------------------
template <typename T, int N, typename S>
bool ColorTable::MapComponent(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, S>& values,
vtkm::IdComponent comp,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 4>>& rgbaOut) const
{
using namespace vtkm::worklet::colorconversion;
return this->Map(vtkm::cont::make_ArrayHandleTransform(values, ComponentPortal(comp)), rgbaOut);
}
//---------------------------------------------------------------------------
template <typename T, int N, typename S>
bool ColorTable::MapComponent(const vtkm::cont::ArrayHandle<vtkm::Vec<T, N>, S>& values,
vtkm::IdComponent comp,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::UInt8, 3>>& rgbOut) const
{
using namespace vtkm::worklet::colorconversion;
return this->Map(vtkm::cont::make_ArrayHandleTransform(values, ComponentPortal(comp)), rgbOut);
}
}
}
//============================================================================
// 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 <vtkm/Range.h>
#include <vtkm/Types.h>
#include <vtkm/cont/VirtualObjectHandle.h>
#include <vtkm/exec/ColorTable.h>
#include <limits>
#include <vector>
namespace vtkm
{
namespace cont
{
namespace detail
{
struct ColorTableInternals
{
ColorSpace CSpace = ColorSpace::RGB;
vtkm::Range TableRange = { 1.0, 0.0 };
//Host side version of the ColorTableBase. This holds data such as:
// NanColor
// BelowRangeColor
// AboveRangeColor
// UseClamping
// BelowRangeColor
// AboveRangeColor
//Note the pointers inside the host side portal are not valid, as they
//are execution
vtkm::exec::ColorTableBase* HostSideCache = nullptr;
//Execution side version of the ColorTableBase.
vtkm::cont::VirtualObjectHandle<vtkm::exec::ColorTableBase>* ExecHandle = nullptr;
std::vector<double> ColorNodePos;
std::vector<vtkm::Vec<float, 3>> ColorRGB;
std::vector<double> OpacityNodePos;
std::vector<float> OpacityAlpha;
std::vector<vtkm::Vec<float, 2>> OpacityMidSharp;
vtkm::cont::ArrayHandle<double> ColorPosHandle;
vtkm::cont::ArrayHandle<vtkm::Vec<float, 3>> ColorRGBHandle;
vtkm::cont::ArrayHandle<double> OpacityPosHandle;
vtkm::cont::ArrayHandle<float> OpacityAlphaHandle;
vtkm::cont::ArrayHandle<vtkm::Vec<float, 2>> OpacityMidSharpHandle;
bool ColorArraysChanged = true;
bool OpacityArraysChanged = true;
void RecalculateRange()
{
vtkm::Range r;
if (this->ColorNodePos.size() > 0)
{
r.Include(this->ColorNodePos.front());
r.Include(this->ColorNodePos.back());
}
if (this->OpacityNodePos.size() > 0)
{
r.Include(this->OpacityNodePos.front());
r.Include(this->OpacityNodePos.back());
}
this->TableRange = r;
}
};
} //namespace detail
namespace
{
template <typename T>
struct MinDelta
{
};
// This value seems to work well for float ranges we have tested
template <>
struct MinDelta<float>
{
static constexpr int value = 2048;
};
template <>
struct MinDelta<double>
{
static constexpr vtkm::Int64 value = 2048L;
};
// Reperesents the following:
// T m = std::numeric_limits<T>::min();
// EquivSizeIntT im;
// std::memcpy(&im, &m, sizeof(T));
//
template <typename EquivSizeIntT>
struct MinRepresentable
{
};
template <>
struct MinRepresentable<float>
{
static constexpr int value = 8388608;
};
template <>
struct MinRepresentable<double>
{
static constexpr vtkm::Int64 value = 4503599627370496L;
};
bool rangeAlmostEqual(const vtkm::Range& r)
{
vtkm::Int64 irange[2];
// needs to be a memcpy to avoid strict aliasing issues, doing a count
// of 2*sizeof(T) to couple both values at the same time
std::memcpy(irange, &r.Min, sizeof(vtkm::Int64));
std::memcpy(irange, &r.Max, sizeof(vtkm::Int64));
// determine the absolute delta between these two numbers.
const vtkm::Int64 delta = std::abs(irange[1] - irange[0]);
// If the numbers are not nearly equal, we don't touch them. This avoids running into
// pitfalls like BUG PV #17152.
return (delta < 1024) ? true : false;
}
template <typename T>
double expandRange(T r[2])
{
constexpr bool is_float32_type = std::is_same<T, vtkm::Float32>::value;
using IRange = typename std::conditional<is_float32_type, vtkm::Int32, vtkm::Int64>::type;
IRange irange[2];
// needs to be a memcpy to avoid strict aliasing issues, doing a count
// of 2*sizeof(T) to couple both values at the same time
std::memcpy(irange, r, sizeof(T) * 2);
const bool denormal = !std::isnormal(r[0]);
const IRange minInt = MinRepresentable<T>::value;
const IRange minDelta = denormal ? minInt + MinDelta<T>::value : MinDelta<T>::value;
// determine the absolute delta between these two numbers.
const vtkm::Int64 delta = std::abs(irange[1] - irange[0]);
// if our delta is smaller than the min delta push out the max value
// so that it is equal to minRange + minDelta. When our range is entirely
// negative we should instead subtract from our max, to max a larger negative
// value
if (delta < minDelta)
{
if (irange[0] < 0)
{
irange[1] = irange[0] - minDelta;
}
else
{
irange[1] = irange[0] + minDelta;
}
T result;
std::memcpy(&result, irange + 1, sizeof(T));
return static_cast<double>(result);
}
return static_cast<double>(r[1]);
}
vtkm::Range adjustRange(const vtkm::Range& r)
{
const bool spans_zero_boundary = r.Min < 0 && r.Max > 0;
if (spans_zero_boundary)
{ // nothing needs to be done, but this check is required.
// if we convert into integer space the delta difference will overflow
// an integer
return r;
}
if (rangeAlmostEqual(r))
{
return r;
}
// range should be left untouched as much as possible to
// to avoid loss of precision whenever possible. That is why
// we only modify the Max value
vtkm::Range result = r;
if (r.Min > static_cast<double>(std::numeric_limits<float>::lowest()) &&
r.Max < static_cast<double>(std::numeric_limits<float>::max()))
{ //We've found it best to offset it in float space if the numbers
//lay inside that representable range
float frange[2] = { static_cast<float>(r.Min), static_cast<float>(r.Max) };
result.Max = expandRange(frange);
}
else
{
double drange[2] = { r.Min, r.Max };
result.Max = expandRange(drange);
}
return result;
}
template <typename T>
vtkm::cont::ArrayHandle<T> buildSampleHandle(std::vector<T>& samples,
vtkm::Int32 numSamples,
T start,
T end,
T inc,