diff --git a/vtkm/cont/ColorTable.cxx b/vtkm/cont/ColorTable.cxx index 7bc4bc0d0..5046471d4 100644 --- a/vtkm/cont/ColorTable.cxx +++ b/vtkm/cont/ColorTable.cxx @@ -852,6 +852,12 @@ vtkm::cont::VirtualObjectHandle* ColorTable::GetHand return this->Impl->ExecHandle; } +//--------------------------------------------------------------------------- +vtkm::Id ColorTable::GetModifiedCount() const +{ + return this->Impl->HostSideCache->GetModifiedCount(); +} + /* #define ColorTableExportMapFunctions(T) \ diff --git a/vtkm/cont/ColorTable.h b/vtkm/cont/ColorTable.h index fa9e6c6ad..50493b23d 100644 --- a/vtkm/cont/ColorTable.h +++ b/vtkm/cont/ColorTable.h @@ -100,11 +100,8 @@ enum struct ColorSpace /// mode where colors will pass through white when interpolating between two /// saturated colors. /// -/// To map an vtkm::cont::ArrayHandle through the color and opacity transfer -/// functions and into a RGB or RGBA array you will need to use -/// vtkm::worklet::ColorTransferFunction. ColorTransferFunction has -/// controls if you want to modify which ColorSpace (RGB, HSV, LAB, Diverging) you want to -/// interpolate through. +/// To map a field from a vtkm::cont::DataSet through the color and opacity transfer +/// functions and into a RGB or RGBA array you should use vtkm::filter::FieldToColor. /// class VTKM_CONT_EXPORT ColorTable { @@ -635,9 +632,12 @@ public: /// This object is only valid as long as the ColorTable is unmodified vtkm::cont::VirtualObjectHandle* GetHandleForExecution() const; - //Todo: - // - // 1. Implement Preset Methods + + /// \brief returns the modified count for the virtual object handle of the exec color table + /// + /// The modified count allows consumers of a shared color table to keep track + /// if the color table has been modified since the last time they used it. + vtkm::Id GetModifiedCount() const; }; } } //namespace vtkm::cont diff --git a/vtkm/filter/CMakeLists.txt b/vtkm/filter/CMakeLists.txt index 7a15a2ee2..5fd5341fd 100644 --- a/vtkm/filter/CMakeLists.txt +++ b/vtkm/filter/CMakeLists.txt @@ -33,6 +33,7 @@ set(headers ExtractStructured.h FieldMetadata.h FieldSelection.h + FieldToColors.h Filter.h FilterCell.h FilterDataSet.h @@ -75,6 +76,7 @@ set(header_template_sources ExtractGeometry.hxx ExtractPoints.hxx ExtractStructured.hxx + FieldToColors.hxx Filter.hxx FilterCell.hxx FilterDataSet.hxx diff --git a/vtkm/filter/FieldToColors.h b/vtkm/filter/FieldToColors.h new file mode 100644 index 000000000..c34ed24f9 --- /dev/null +++ b/vtkm/filter/FieldToColors.h @@ -0,0 +1,98 @@ +//============================================================================ +// 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 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS). +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014 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. +//============================================================================ + +#ifndef vtk_m_filter_FieldToColors_h +#define vtk_m_filter_FieldToColors_h + +#include +#include + +namespace vtkm +{ +namespace filter +{ + +/// \brief Convert an arbitrary field to an RGB or RGBA field +/// +class FieldToColors : public vtkm::filter::FilterField +{ +public: + VTKM_CONT + FieldToColors(const vtkm::cont::ColorTable& table); + + enum FieldToColorsInputMode + { + SCALAR, + MAGNITUDE, + COMPONENT + }; + + enum FieldToColorsOutputMode + { + RGB, + RGBA + }; + + void SetMappingMode(FieldToColorsInputMode mode) { this->InputMode = mode; } + void SetMappingToScalar() { this->InputMode = FieldToColorsInputMode::SCALAR; } + void SetMappingToMagnitude() { this->InputMode = FieldToColorsInputMode::MAGNITUDE; } + void SetMappingToComponent() { this->InputMode = FieldToColorsInputMode::COMPONENT; } + FieldToColorsInputMode GetMappingMode() const { return this->InputMode; } + bool IsMappingScalar() const { return this->InputMode == FieldToColorsInputMode::SCALAR; } + bool IsMappingMagnitude() const { return this->InputMode == FieldToColorsInputMode::MAGNITUDE; } + bool IsMappingComponent() const { return this->InputMode == FieldToColorsInputMode::COMPONENT; } + + void SetMappingComponent(vtkm::Int32 comp) { this->Component = comp; } + vtkm::Int32 GetMappingComponent() const { return this->Component; } + + void SetOutputMode(FieldToColorsOutputMode mode) { this->OutputMode = mode; } + void SetOutputToRGB() { this->OutputMode = FieldToColorsOutputMode::RGB; } + void SetOutputToRGBA() { this->OutputMode = FieldToColorsOutputMode::RGBA; } + FieldToColorsOutputMode GetOutputMode() const { return this->OutputMode; } + bool IsMappingRGB() const { return this->OutputMode == FieldToColorsOutputMode::RGB; } + bool IsMappingRGBA() const { return this->OutputMode == FieldToColorsOutputMode::RGBA; } + + + void SetNumberOfSamplingPoints(vtkm::Int32 count); + vtkm::Int32 GetNumberOfSamplingPoints() const { return this->SampleCount; } + + template + VTKM_CONT vtkm::filter::Result DoExecute(const vtkm::cont::DataSet& input, + const vtkm::cont::ArrayHandle& field, + const vtkm::filter::FieldMetadata& fieldMeta, + const vtkm::filter::PolicyBase& policy, + const DeviceAdapter& tag); + +private: + vtkm::cont::ColorTable Table; + FieldToColorsInputMode InputMode; + FieldToColorsOutputMode OutputMode; + vtkm::cont::ColorTableSamplesRGB SamplesRGB; + vtkm::cont::ColorTableSamplesRGBA SamplesRGBA; + vtkm::Int32 Component; + vtkm::Int32 SampleCount; + vtkm::Id ModifiedCount; +}; +} +} // namespace vtkm::filter + +#include + +#endif // vtk_m_filter_FieldToColors_h diff --git a/vtkm/filter/FieldToColors.hxx b/vtkm/filter/FieldToColors.hxx new file mode 100644 index 000000000..a48ebf577 --- /dev/null +++ b/vtkm/filter/FieldToColors.hxx @@ -0,0 +1,270 @@ +//============================================================================ +// 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 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS). +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014 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 +#include + + +namespace vtkm +{ +namespace filter +{ + +namespace +{ +struct ScalarInputMode +{ +}; +struct MagnitudeInputMode +{ +}; +struct ComponentInputMode +{ +}; +} + +template +inline bool execute(const vtkm::cont::ColorTable& table, + ScalarInputMode, + int, + const T& input, + const S& samples, + U& output, + vtkm::VecTraitsTagSingleComponent) +{ + return table.Map(input, samples, output); +} + +template +inline bool execute(const vtkm::cont::ColorTable& table, + MagnitudeInputMode, + int, + const T& input, + const S& samples, + U& output, + vtkm::VecTraitsTagMultipleComponents) +{ + return table.MapMagnitude(input, samples, output); +} + +template +inline bool execute(const vtkm::cont::ColorTable& table, + ComponentInputMode, + int comp, + const T& input, + const S& samples, + U& output, + vtkm::VecTraitsTagMultipleComponents) +{ + return table.MapComponent(input, comp, samples, output); +} + +//error cases +template +inline bool execute(const vtkm::cont::ColorTable& table, + ScalarInputMode, + int, + const T& input, + const S& samples, + U& output, + vtkm::VecTraitsTagMultipleComponents) +{ //vector input in scalar mode so do magnitude + return table.MapMagnitude(input, samples, output); +} +template +inline bool execute(const vtkm::cont::ColorTable& table, + MagnitudeInputMode, + int, + const T& input, + const S& samples, + U& output, + vtkm::VecTraitsTagSingleComponent) +{ //is a scalar array so ignore Magnitude mode + return table.Map(input, samples, output); +} +template +inline bool execute(const vtkm::cont::ColorTable& table, + ComponentInputMode, + int, + const T& input, + const S& samples, + U& output, + vtkm::VecTraitsTagSingleComponent) +{ //is a scalar array so ignore COMPONENT mode + return table.Map(input, samples, output); +} + + +//----------------------------------------------------------------------------- +inline VTKM_CONT FieldToColors::FieldToColors(const vtkm::cont::ColorTable& table) + : vtkm::filter::FilterField() + , Table(table) + , InputMode(SCALAR) + , OutputMode(RGBA) + , SamplesRGB() + , SamplesRGBA() + , Component(0) + , SampleCount(256) + , ModifiedCount(-1) +{ +} + +//----------------------------------------------------------------------------- +inline VTKM_CONT void FieldToColors::SetNumberOfSamplingPoints(vtkm::Int32 count) +{ + if (this->SampleCount != count && count > 0) + { + this->ModifiedCount = -1; + this->SampleCount = count; + } +} + +//----------------------------------------------------------------------------- +template +inline VTKM_CONT vtkm::filter::Result FieldToColors::DoExecute( + const vtkm::cont::DataSet& input, + const vtkm::cont::ArrayHandle& inField, + const vtkm::filter::FieldMetadata& fieldMetadata, + const vtkm::filter::PolicyBase&, + const DeviceAdapter&) +{ + //If the table has been modified we need to rebuild our + //sample tables + if (this->Table.GetModifiedCount() > this->ModifiedCount) + { + this->Table.Sample(this->SampleCount, this->SamplesRGB); + this->Table.Sample(this->SampleCount, this->SamplesRGBA); + this->ModifiedCount = this->Table.GetModifiedCount(); + } + + + std::string outputName = this->GetOutputFieldName(); + if (outputName == "") + { + // Default name is name of input_colors. + outputName = fieldMetadata.GetName() + "_colors"; + } + vtkm::cont::Field outField; + + //We need to verify if the array is a vtkm::Vec + + using IsVec = typename vtkm::VecTraits::HasMultipleComponents; + if (this->OutputMode == RGBA) + { + vtkm::cont::ArrayHandle> output; + + bool ran = false; + switch (this->InputMode) + { + case SCALAR: + { + ran = execute(this->Table, + ScalarInputMode{}, + this->Component, + inField, + this->SamplesRGBA, + output, + IsVec{}); + break; + } + case MAGNITUDE: + { + ran = execute(this->Table, + MagnitudeInputMode{}, + this->Component, + inField, + this->SamplesRGBA, + output, + IsVec{}); + break; + } + case COMPONENT: + { + ran = execute(this->Table, + ComponentInputMode{}, + this->Component, + inField, + this->SamplesRGBA, + output, + IsVec{}); + break; + } + } + + if (!ran) + { + return vtkm::filter::Result(); + } + outField = vtkm::cont::Field(outputName, vtkm::cont::Field::ASSOC_POINTS, output); + } + else + { + vtkm::cont::ArrayHandle> output; + + bool ran = false; + switch (this->InputMode) + { + case SCALAR: + { + ran = execute(this->Table, + ScalarInputMode{}, + this->Component, + inField, + this->SamplesRGB, + output, + IsVec{}); + break; + } + case MAGNITUDE: + { + ran = execute(this->Table, + MagnitudeInputMode{}, + this->Component, + inField, + this->SamplesRGB, + output, + IsVec{}); + break; + } + case COMPONENT: + { + ran = execute(this->Table, + ComponentInputMode{}, + this->Component, + inField, + this->SamplesRGB, + output, + IsVec{}); + break; + } + } + + if (!ran) + { + return vtkm::filter::Result(); + } + outField = vtkm::cont::Field(outputName, vtkm::cont::Field::ASSOC_POINTS, output); + } + + + return vtkm::filter::Result(input, outField); +} +} +} // namespace vtkm::filter diff --git a/vtkm/filter/testing/CMakeLists.txt b/vtkm/filter/testing/CMakeLists.txt index f5607e6e0..56301eead 100644 --- a/vtkm/filter/testing/CMakeLists.txt +++ b/vtkm/filter/testing/CMakeLists.txt @@ -33,6 +33,7 @@ set(unit_tests UnitTestExtractStructuredFilter.cxx UnitTestFieldMetadata.cxx UnitTestFieldSelection.cxx + UnitTestFieldToColors.cxx UnitTestGradient.cxx UnitTestHistogramFilter.cxx UnitTestMarchingCubesFilter.cxx diff --git a/vtkm/filter/testing/UnitTestFieldToColors.cxx b/vtkm/filter/testing/UnitTestFieldToColors.cxx new file mode 100644 index 000000000..dc975302c --- /dev/null +++ b/vtkm/filter/testing/UnitTestFieldToColors.cxx @@ -0,0 +1,97 @@ +//============================================================================ +// 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 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS). +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014 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 + +#include +#include +namespace +{ +void TestFieldToColors() +{ + //faux input field + constexpr vtkm::Id nvals = 8; + constexpr int data[nvals] = { -1, 0, 10, 20, 30, 40, 50, 60 }; + + //build a color table with clamping off and verify that sampling works + vtkm::Range range{ 0.0, 50.0 }; + vtkm::cont::ColorTable table; + table.LoadPreset("Cool to Warm"); + table.RescaleToRange(range); + table.SetClampingOff(); + table.SetAboveRangeColor(vtkm::Vec{ 1.0f, 0.0f, 0.0f }); //red + table.SetBelowRangeColor(vtkm::Vec{ 0.0f, 0.0f, 1.0f }); //green + + vtkm::cont::DataSet ds = vtkm::cont::testing::MakeTestDataSet().Make3DExplicitDataSetPolygonal(); + vtkm::cont::DataSetFieldAdd dsf; + dsf.AddPointField(ds, "faux", data, nvals); + + vtkm::filter::FieldToColors ftc(table); + ftc.SetOutputToRGBA(); + ftc.SetActiveField("faux"); + ftc.SetOutputFieldName("colors"); + + auto rgbaResult = ftc.Execute(ds); + VTKM_TEST_ASSERT(rgbaResult.HasField("colors", vtkm::cont::Field::ASSOC_POINTS), + "Field missing."); + vtkm::cont::Field Result = rgbaResult.GetField("colors", vtkm::cont::Field::ASSOC_POINTS); + vtkm::cont::ArrayHandle> resultRGBAHandle; + Result.GetData().CopyTo(resultRGBAHandle); + + //values confirmed with ParaView 5.4 + const vtkm::Vec correct_diverging_rgba_values[nvals] = { + { 0, 0, 255, 255 }, { 59, 76, 192, 255 }, { 122, 157, 248, 255 }, { 191, 211, 246, 255 }, + { 241, 204, 184, 255 }, { 238, 134, 105, 255 }, { 180, 4, 38, 255 }, { 255, 0, 0, 255 } + }; + auto portalRGBA = resultRGBAHandle.GetPortalConstControl(); + for (std::size_t i = 0; i < nvals; ++i) + { + auto result = portalRGBA.Get(static_cast(i)); + VTKM_TEST_ASSERT(result == correct_diverging_rgba_values[i], + "incorrect value when interpolating between values"); + } + + //Now verify that we can switching our output mode + ftc.SetOutputToRGB(); + auto rgbResult = ftc.Execute(ds); + VTKM_TEST_ASSERT(rgbResult.HasField("colors", vtkm::cont::Field::ASSOC_POINTS), "Field missing."); + Result = rgbResult.GetField("colors", vtkm::cont::Field::ASSOC_POINTS); + vtkm::cont::ArrayHandle> resultRGBHandle; + Result.GetData().CopyTo(resultRGBHandle); + + //values confirmed with ParaView 5.4 + const vtkm::Vec correct_diverging_rgb_values[nvals] = { + { 0, 0, 255 }, { 59, 76, 192 }, { 122, 157, 248 }, { 191, 211, 246 }, + { 241, 204, 184 }, { 238, 134, 105 }, { 180, 4, 38 }, { 255, 0, 0 } + }; + auto portalRGB = resultRGBHandle.GetPortalConstControl(); + for (std::size_t i = 0; i < nvals; ++i) + { + auto result = portalRGB.Get(static_cast(i)); + VTKM_TEST_ASSERT(result == correct_diverging_rgb_values[i], + "incorrect value when interpolating between values"); + } +} +} + +int UnitTestFieldToColors(int, char* []) +{ + return vtkm::cont::testing::Testing::Run(TestFieldToColors); +}