Add MapperGlyphScalar and MapperGlyphVector mappers

These mappers can be used to render cell sets as glyphs. The following
glyph types are available for scalar fields:
 - Cube
 - Sphere
 - Axes
 - Quads

For vector fields:
 - Arrows
This commit is contained in:
Manish Mathai 2022-04-01 03:33:57 -07:00
parent 12acf31097
commit 814ec27f1e
31 changed files with 3480 additions and 4 deletions

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b70d68db818220a5c30d34b4dd14db000d6a2d7f4e117c5509f03e236186825b
size 20123

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:8012ca572ca38b64fda0f617a9471a48d268e35c12a1352ce3725e8e27577a58
size 16910

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3ad37f93e562178c5491e06256083630ad6b5d500f9b19683a72ad91c0b4ab05
size 95406

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e500611307400dd0d618a37d4160722d471a971db4f4accb698c207259c38bb7
size 25221

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:91506c6a411f536fcf549ecdb74520d072b184d4b6e168c19f11b864db0f6bb4
size 37260

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bb998a6d7ae70371a82e1de0778fcc0259996d0bb13bbf55bc14a9efd0f663e4
size 131853

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6658a13bb64a4921bda3ed49d2de7021cf2e31257e5b9e5bbbbfd2ad3c85c044
size 21634

@ -83,8 +83,6 @@ Actor::Actor(const vtkm::cont::UnknownCellSet& cells,
void Actor::Init(const vtkm::cont::CoordinateSystem& coordinates,
const vtkm::cont::Field& scalarField)
{
VTKM_ASSERT(scalarField.GetData().GetNumberOfComponentsFlat() == 1);
scalarField.GetRange(&this->Internals->ScalarRange);
this->Internals->SpatialBounds = coordinates.GetBounds();
}

@ -35,12 +35,16 @@ set(headers
Cylinderizer.h
DecodePNG.h # deprecated
EncodePNG.h # deprecated
GlyphType.h
LineRenderer.h
LineRendererBatcher.h
MatrixHelpers.h
Scene.h
Mapper.h
MapperCylinder.h
MapperGlyphBase.h
MapperGlyphScalar.h
MapperGlyphVector.h
MapperPoint.h
MapperQuad.h
MapperRayTracer.h
@ -103,6 +107,9 @@ set(device_sources
ConnectivityProxy.cxx
LineRendererBatcher.cxx
MapperCylinder.cxx
MapperGlyphBase.cxx
MapperGlyphScalar.cxx
MapperGlyphVector.cxx
MapperPoint.cxx
MapperQuad.cxx
MapperRayTracer.cxx
@ -119,6 +126,10 @@ set(device_sources
raytracing/ConnectivityTracer.cxx
raytracing/CylinderExtractor.cxx
raytracing/CylinderIntersector.cxx
raytracing/GlyphExtractor.cxx
raytracing/GlyphExtractorVector.cxx
raytracing/GlyphIntersector.cxx
raytracing/GlyphIntersectorVector.cxx
raytracing/MeshConnectivityBuilder.cxx
raytracing/QuadExtractor.cxx
raytracing/QuadIntersector.cxx

@ -0,0 +1,30 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_rendering_GlyphType_h
#define vtk_m_rendering_GlyphType_h
namespace vtkm
{
namespace rendering
{
enum struct GlyphType
{
Arrow,
Axes,
Cube,
Quad,
Sphere,
};
}
}
#endif

@ -0,0 +1,161 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/rendering/MapperGlyphBase.h>
#include <vtkm/filter/entity_extraction/MaskPoints.h>
#include <vtkm/rendering/CanvasRayTracer.h>
namespace vtkm
{
namespace rendering
{
MapperGlyphBase::MapperGlyphBase()
: Canvas(nullptr)
, CompositeBackground(true)
, UseNodes(true)
, UseStride(false)
, Stride(1)
, ScaleByValue(false)
, BaseSize(-1.f)
, ScaleDelta(0.5f)
{
}
MapperGlyphBase::~MapperGlyphBase() {}
void MapperGlyphBase::SetCanvas(vtkm::rendering::Canvas* canvas)
{
vtkm::rendering::CanvasRayTracer* canvasRT =
dynamic_cast<vtkm::rendering::CanvasRayTracer*>(canvas);
if (canvasRT == nullptr)
{
throw vtkm::cont::ErrorBadValue("MapperGlyphBase: bad canvas type. Must be CanvasRayTracer");
}
this->Canvas = canvasRT;
}
vtkm::rendering::Canvas* MapperGlyphBase::GetCanvas() const
{
return this->Canvas;
}
bool MapperGlyphBase::GetUseCells() const
{
return !this->UseNodes;
}
void MapperGlyphBase::SetUseCells()
{
this->UseNodes = false;
}
bool MapperGlyphBase::GetUseNodes() const
{
return this->UseNodes;
}
void MapperGlyphBase::SetUseNodes()
{
this->UseNodes = true;
}
vtkm::Float32 MapperGlyphBase::GetBaseSize() const
{
return this->BaseSize;
}
void MapperGlyphBase::SetBaseSize(vtkm::Float32 size)
{
if (size <= 0.f)
{
throw vtkm::cont::ErrorBadValue("MapperGlyphBase: base size must be positive");
}
this->BaseSize = size;
}
bool MapperGlyphBase::GetScaleByValue() const
{
return this->ScaleByValue;
}
void MapperGlyphBase::SetScaleByValue(bool on)
{
this->ScaleByValue = on;
}
vtkm::Float32 MapperGlyphBase::GetScaleDelta() const
{
return this->ScaleDelta;
}
void MapperGlyphBase::SetScaleDelta(vtkm::Float32 delta)
{
if (delta < 0.0f)
{
throw vtkm::cont::ErrorBadValue("MapperGlyphBase: scale delta must be non-negative");
}
this->ScaleDelta = delta;
}
bool MapperGlyphBase::GetUseStride() const
{
return this->UseStride;
}
void MapperGlyphBase::SetUseStride(bool on)
{
this->UseStride = on;
}
vtkm::Id MapperGlyphBase::GetStride() const
{
return this->Stride;
}
void MapperGlyphBase::SetStride(vtkm::Id stride)
{
if (stride < 1)
{
throw vtkm::cont::ErrorBadValue("MapperGlyphBase: stride must be positive");
}
this->Stride = stride;
}
void MapperGlyphBase::SetCompositeBackground(bool on)
{
this->CompositeBackground = on;
}
vtkm::cont::DataSet MapperGlyphBase::FilterPoints(const vtkm::cont::UnknownCellSet& cellSet,
const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& field) const
{
vtkm::cont::DataSet result;
result.SetCellSet(cellSet);
result.AddCoordinateSystem(coords);
result.AddField(field);
if (this->UseStride)
{
vtkm::filter::entity_extraction::MaskPoints pointMasker;
pointMasker.SetCompactPoints(true);
pointMasker.SetStride(this->Stride);
result = pointMasker.Execute(result);
}
return result;
}
}
} // namespace vtkm::rendering

@ -0,0 +1,76 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_rendering_MapperGlyphBase_h
#define vtk_m_rendering_MapperGlyphBase_h
#include <vtkm/cont/ColorTable.h>
#include <vtkm/rendering/Camera.h>
#include <vtkm/rendering/Mapper.h>
#include <memory>
namespace vtkm
{
namespace rendering
{
class CanvasRayTracer;
class VTKM_RENDERING_EXPORT MapperGlyphBase : public Mapper
{
public:
MapperGlyphBase();
virtual ~MapperGlyphBase();
void SetCanvas(vtkm::rendering::Canvas* canvas) override;
virtual vtkm::rendering::Canvas* GetCanvas() const override;
virtual bool GetUseCells() const;
virtual void SetUseCells();
virtual bool GetUseNodes() const;
virtual void SetUseNodes();
virtual bool GetUseStride() const;
virtual void SetUseStride(bool on);
virtual vtkm::Id GetStride() const;
virtual void SetStride(vtkm::Id stride);
virtual vtkm::Float32 GetBaseSize() const;
virtual void SetBaseSize(vtkm::Float32 size);
virtual bool GetScaleByValue() const;
virtual void SetScaleByValue(bool on);
virtual vtkm::Float32 GetScaleDelta() const;
virtual void SetScaleDelta(vtkm::Float32 delta);
virtual void SetCompositeBackground(bool on);
protected:
virtual vtkm::cont::DataSet FilterPoints(const vtkm::cont::UnknownCellSet& cellSet,
const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& scalarField) const;
vtkm::rendering::CanvasRayTracer* Canvas;
bool CompositeBackground;
bool UseNodes;
bool UseStride;
vtkm::Id Stride;
bool ScaleByValue;
vtkm::Float32 BaseSize;
vtkm::Float32 ScaleDelta;
};
}
} //namespace vtkm::rendering
#endif //vtk_m_rendering_MapperGlyphBase_h

@ -0,0 +1,494 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/rendering/MapperGlyphScalar.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/rendering/CanvasRayTracer.h>
#include <vtkm/rendering/raytracing/Camera.h>
#include <vtkm/rendering/raytracing/GlyphExtractor.h>
#include <vtkm/rendering/raytracing/GlyphIntersector.h>
#include <vtkm/rendering/raytracing/Logger.h>
#include <vtkm/rendering/raytracing/RayOperations.h>
#include <vtkm/rendering/raytracing/RayTracer.h>
namespace vtkm
{
namespace rendering
{
namespace
{
// Packed frame buffer value with color set as black and depth as 1.0f
constexpr vtkm::Int64 ClearValue = 0x3F800000000000FF;
union PackedValue {
struct PackedFloats
{
vtkm::Float32 Color;
vtkm::Float32 Depth;
} Floats;
struct PackedInts
{
vtkm::UInt32 Color;
vtkm::UInt32 Depth;
} Ints;
vtkm::Int64 Raw;
}; // union PackedValue
VTKM_EXEC_CONT
vtkm::UInt32 ScaleColorComponent(vtkm::Float32 c)
{
vtkm::Int32 t = vtkm::Int32(c * 256.0f);
return vtkm::UInt32(t < 0 ? 0 : (t > 255 ? 255 : t));
}
VTKM_EXEC_CONT
vtkm::UInt32 PackColor(vtkm::Float32 r, vtkm::Float32 g, vtkm::Float32 b, vtkm::Float32 a);
VTKM_EXEC_CONT
vtkm::UInt32 PackColor(const vtkm::Vec4f_32& color)
{
return PackColor(color[0], color[1], color[2], color[3]);
}
VTKM_EXEC_CONT
vtkm::UInt32 PackColor(vtkm::Float32 r, vtkm::Float32 g, vtkm::Float32 b, vtkm::Float32 a)
{
vtkm::UInt32 packed = (ScaleColorComponent(r) << 24);
packed |= (ScaleColorComponent(g) << 16);
packed |= (ScaleColorComponent(b) << 8);
packed |= ScaleColorComponent(a);
return packed;
}
VTKM_EXEC_CONT
void UnpackColor(vtkm::UInt32 color,
vtkm::Float32& r,
vtkm::Float32& g,
vtkm::Float32& b,
vtkm::Float32& a);
VTKM_EXEC_CONT
void UnpackColor(vtkm::UInt32 packedColor, vtkm::Vec4f_32& color)
{
UnpackColor(packedColor, color[0], color[1], color[2], color[3]);
}
VTKM_EXEC_CONT
void UnpackColor(vtkm::UInt32 color,
vtkm::Float32& r,
vtkm::Float32& g,
vtkm::Float32& b,
vtkm::Float32& a)
{
r = vtkm::Float32((color & 0xFF000000) >> 24) / 255.0f;
g = vtkm::Float32((color & 0x00FF0000) >> 16) / 255.0f;
b = vtkm::Float32((color & 0x0000FF00) >> 8) / 255.0f;
a = vtkm::Float32((color & 0x000000FF)) / 255.0f;
}
class PackIntoFrameBuffer : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldIn, FieldOut);
using ExecutionSignature = void(_1, _2, _3);
VTKM_CONT
PackIntoFrameBuffer() {}
VTKM_EXEC
void operator()(const vtkm::Vec4f_32& color,
const vtkm::Float32& depth,
vtkm::Int64& outValue) const
{
PackedValue packed;
packed.Ints.Color = PackColor(color);
packed.Floats.Depth = depth;
outValue = packed.Raw;
}
}; //class PackIntoFrameBuffer
class UnpackFromFrameBuffer : public vtkm::worklet::WorkletMapField
{
public:
VTKM_CONT
UnpackFromFrameBuffer() {}
using ControlSignature = void(FieldIn, WholeArrayOut, WholeArrayOut);
using ExecutionSignature = void(_1, _2, _3, WorkIndex);
template <typename ColorBufferPortal, typename DepthBufferPortal>
VTKM_EXEC void operator()(const vtkm::Int64& packedValue,
ColorBufferPortal& colorBuffer,
DepthBufferPortal& depthBuffer,
const vtkm::Id& index) const
{
PackedValue packed;
packed.Raw = packedValue;
float depth = packed.Floats.Depth;
if (depth <= depthBuffer.Get(index))
{
vtkm::Vec4f_32 color;
UnpackColor(packed.Ints.Color, color);
colorBuffer.Set(index, color);
depthBuffer.Set(index, depth);
}
}
}; // class UnpackFromFrameBuffer
class GetNormalizedScalars : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn pointIds, FieldOut normalizedScalars, WholeArrayIn field);
using ExecutionSignature = void(_1, _2, _3);
VTKM_CONT GetNormalizedScalars(vtkm::Float32 minScalar, vtkm::Float32 maxScalar)
: MinScalar(minScalar)
{
if (minScalar >= maxScalar)
{
this->InverseScalarDelta = 0.0f;
}
else
{
this->InverseScalarDelta = 1.0f / (maxScalar - minScalar);
}
}
template <typename FieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& pointId,
vtkm::Float32& normalizedScalar,
const FieldPortalType& field) const
{
normalizedScalar = static_cast<vtkm::Float32>(field.Get(pointId));
normalizedScalar = (normalizedScalar - this->MinScalar) * this->InverseScalarDelta;
}
private:
vtkm::Float32 MinScalar;
vtkm::Float32 InverseScalarDelta;
}; // class GetNormalizedScalars
class BillboardGlyphPlotter : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn pointIds,
FieldIn sizes,
FieldIn normalizedScalar,
WholeArrayIn coords,
WholeArrayIn colorMap,
AtomicArrayInOut frameBuffer);
using ExecutionSignature = void(_1, _2, _3, _4, _5, _6);
VTKM_CONT
BillboardGlyphPlotter(const vtkm::Matrix<vtkm::Float32, 4, 4>& worldToProjection,
vtkm::Id width,
vtkm::Id height,
vtkm::Float32 projectionOffset)
: WorldToProjection(worldToProjection)
, Width(width)
, Height(height)
, ProjectionOffset(projectionOffset)
{
}
template <typename CoordinatesPortal, typename ColorMapPortal, typename FrameBuffer>
VTKM_EXEC void operator()(const vtkm::Id& pointId,
const vtkm::Float32& size,
const vtkm::Float32& normalizedScalar,
const CoordinatesPortal& coordsPortal,
const ColorMapPortal& colorMap,
FrameBuffer& frameBuffer) const
{
vtkm::Vec3f_32 point = static_cast<vtkm::Vec3f_32>(coordsPortal.Get(pointId));
point = this->TransformWorldToViewport(point);
vtkm::Vec4f_32 color = this->GetColor(normalizedScalar, colorMap);
vtkm::Float32 halfSize = size / 2.0f;
vtkm::Id x1 = static_cast<vtkm::Id>(vtkm::Round(point[0] - halfSize));
vtkm::Id x2 = static_cast<vtkm::Id>(vtkm::Round(point[0] + halfSize));
vtkm::Id y1 = static_cast<vtkm::Id>(vtkm::Round(point[1] - halfSize));
vtkm::Id y2 = static_cast<vtkm::Id>(vtkm::Round(point[1] + halfSize));
vtkm::Float32 depth = point[2];
for (vtkm::Id x = x1; x <= x2; ++x)
{
for (vtkm::Id y = y1; y <= y2; ++y)
{
this->SetColor(x, y, depth, color, frameBuffer);
}
}
}
private:
VTKM_EXEC
vtkm::Vec3f_32 TransformWorldToViewport(const vtkm::Vec3f_32& point) const
{
vtkm::Vec4f_32 temp(point[0], point[1], point[2], 1.0f);
vtkm::Vec3f_32 result;
temp = vtkm::MatrixMultiply(this->WorldToProjection, temp);
for (vtkm::IdComponent i = 0; i < 3; ++i)
{
result[i] = temp[i] / temp[3];
}
result[0] = (result[0] * 0.5f + 0.5f) * vtkm::Float32(this->Width);
result[1] = (result[1] * 0.5f + 0.5f) * vtkm::Float32(this->Height);
result[2] = result[2] * 0.5f + 0.5f;
// Offset the point to a bit towards the camera. This is to ensure that the front faces of
// the wireframe wins the z-depth check against the surface render, and is in addition to the
// existing camera space offset.
result[2] -= this->ProjectionOffset;
return result;
}
template <typename ColorMapPortal>
VTKM_EXEC vtkm::Vec4f_32 GetColor(vtkm::Float32 normalizedScalar,
const ColorMapPortal& colorMap) const
{
vtkm::Id colorMapSize = colorMap.GetNumberOfValues() - 1;
vtkm::Id colorIdx = static_cast<vtkm::Id>(normalizedScalar * colorMapSize);
colorIdx = vtkm::Min(colorMapSize, vtkm::Max(vtkm::Id(0), colorIdx));
return colorMap.Get(colorIdx);
}
template <typename FrameBuffer>
VTKM_EXEC void SetColor(vtkm::Id x,
vtkm::Id y,
vtkm::Float32 depth,
const vtkm::Vec4f_32& color,
FrameBuffer& frameBuffer) const
{
if (x < 0 || x >= this->Width || y < 0 || y >= this->Height)
{
return;
}
vtkm::Id index = y * this->Width + x;
PackedValue current, next;
current.Raw = ClearValue;
next.Floats.Depth = depth;
vtkm::Vec4f_32 currentColor;
do
{
UnpackColor(current.Ints.Color, currentColor);
next.Ints.Color = PackColor(color);
frameBuffer.CompareExchange(index, &current.Raw, next.Raw);
} while (current.Floats.Depth > next.Floats.Depth);
}
const vtkm::Matrix<vtkm::Float32, 4, 4> WorldToProjection;
const vtkm::Id Width;
const vtkm::Id Height;
const vtkm::Float32 ProjectionOffset;
}; // class BillboardGlyphPlotter
}
MapperGlyphScalar::MapperGlyphScalar()
: MapperGlyphBase()
, GlyphType(vtkm::rendering::GlyphType::Sphere)
{
}
MapperGlyphScalar::~MapperGlyphScalar() {}
vtkm::rendering::GlyphType MapperGlyphScalar::GetGlyphType() const
{
return this->GlyphType;
}
void MapperGlyphScalar::SetGlyphType(vtkm::rendering::GlyphType glyphType)
{
if (!(glyphType == vtkm::rendering::GlyphType::Axes ||
glyphType == vtkm::rendering::GlyphType::Cube ||
glyphType == vtkm::rendering::GlyphType::Quad ||
glyphType == vtkm::rendering::GlyphType::Sphere))
{
throw vtkm::cont::ErrorBadValue("MapperGlyphScalar: bad glyph type");
}
this->GlyphType = glyphType;
}
void MapperGlyphScalar::RenderCells(const vtkm::cont::UnknownCellSet& cellset,
const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& scalarField,
const vtkm::cont::ColorTable& vtkmNotUsed(colorTable),
const vtkm::rendering::Camera& camera,
const vtkm::Range& scalarRange)
{
raytracing::Logger* logger = raytracing::Logger::GetInstance();
vtkm::rendering::raytracing::RayTracer tracer;
tracer.Clear();
logger->OpenLogEntry("mapper_glyph_scalar");
vtkm::cont::Timer tot_timer;
tot_timer.Start();
vtkm::cont::Timer timer;
vtkm::Bounds coordBounds = coords.GetBounds();
vtkm::Float32 baseSize = this->BaseSize;
if (baseSize == -1.f)
{
// set a default size
vtkm::Float64 lx = coordBounds.X.Length();
vtkm::Float64 ly = coordBounds.Y.Length();
vtkm::Float64 lz = coordBounds.Z.Length();
vtkm::Float64 mag = vtkm::Sqrt(lx * lx + ly * ly + lz * lz);
if (this->GlyphType == vtkm::rendering::GlyphType::Quad)
{
baseSize = 20.0f;
}
else
{
// same as used in vtk ospray
constexpr vtkm::Float64 heuristic = 500.;
baseSize = static_cast<vtkm::Float32>(mag / heuristic);
}
}
vtkm::rendering::raytracing::GlyphExtractor glyphExtractor;
vtkm::cont::DataSet processedDataSet = this->FilterPoints(cellset, coords, scalarField);
vtkm::cont::UnknownCellSet processedCellSet = processedDataSet.GetCellSet();
vtkm::cont::CoordinateSystem processedCoords = processedDataSet.GetCoordinateSystem();
vtkm::cont::Field processedField = processedDataSet.GetField(0);
if (this->ScaleByValue)
{
vtkm::Float32 minSize = baseSize - baseSize * this->ScaleDelta;
vtkm::Float32 maxSize = baseSize + baseSize * this->ScaleDelta;
if (this->UseNodes)
{
glyphExtractor.ExtractCoordinates(processedCoords, processedField, minSize, maxSize);
}
else
{
glyphExtractor.ExtractCells(processedCellSet, processedField, minSize, maxSize);
}
}
else
{
if (this->UseNodes)
{
glyphExtractor.ExtractCoordinates(processedCoords, baseSize);
}
else
{
glyphExtractor.ExtractCells(processedCellSet, baseSize);
}
}
if (this->GlyphType == vtkm::rendering::GlyphType::Quad)
{
vtkm::cont::ArrayHandle<vtkm::Id> pointIds = glyphExtractor.GetPointIds();
vtkm::cont::ArrayHandle<vtkm::Float32> sizes = glyphExtractor.GetSizes();
vtkm::cont::ArrayHandle<vtkm::Float32> normalizedScalars;
vtkm::Float32 rangeMin = static_cast<vtkm::Float32>(scalarRange.Min);
vtkm::Float32 rangeMax = static_cast<vtkm::Float32>(scalarRange.Max);
vtkm::cont::Invoker invoker;
invoker(GetNormalizedScalars{ rangeMin, rangeMax },
pointIds,
normalizedScalars,
vtkm::rendering::raytracing::GetScalarFieldArray(scalarField));
vtkm::cont::ArrayHandle<vtkm::Int64> frameBuffer;
invoker(PackIntoFrameBuffer{},
this->Canvas->GetColorBuffer(),
this->Canvas->GetDepthBuffer(),
frameBuffer);
vtkm::Range clippingRange = camera.GetClippingRange();
vtkm::Float64 offset1 = (clippingRange.Max - clippingRange.Min) / 1.0e4;
vtkm::Float64 offset2 = clippingRange.Min / 2.0;
vtkm::Float32 offset = static_cast<vtkm::Float32>(vtkm::Min(offset1, offset2));
vtkm::Matrix<vtkm::Float32, 4, 4> modelMatrix;
vtkm::MatrixIdentity(modelMatrix);
modelMatrix[2][3] = offset;
vtkm::Matrix<vtkm::Float32, 4, 4> worldToCamera =
vtkm::MatrixMultiply(modelMatrix, camera.CreateViewMatrix());
vtkm::Matrix<vtkm::Float32, 4, 4> worldToProjection = vtkm::MatrixMultiply(
camera.CreateProjectionMatrix(this->Canvas->GetWidth(), this->Canvas->GetHeight()),
worldToCamera);
vtkm::Float32 projectionOffset =
vtkm::Max(0.03f / static_cast<vtkm::Float32>(camera.GetClippingRange().Length()), 1e-4f);
invoker(
BillboardGlyphPlotter{
worldToProjection, this->Canvas->GetWidth(), this->Canvas->GetHeight(), projectionOffset },
pointIds,
sizes,
normalizedScalars,
coords,
this->ColorMap,
frameBuffer);
timer.Start();
invoker(UnpackFromFrameBuffer{},
frameBuffer,
this->Canvas->GetColorBuffer(),
this->Canvas->GetDepthBuffer());
}
else
{
vtkm::Bounds shapeBounds;
if (glyphExtractor.GetNumberOfGlyphs() > 0)
{
auto glyphIntersector = std::make_shared<raytracing::GlyphIntersector>(this->GlyphType);
glyphIntersector->SetData(
processedCoords, glyphExtractor.GetPointIds(), glyphExtractor.GetSizes());
tracer.AddShapeIntersector(glyphIntersector);
shapeBounds.Include(glyphIntersector->GetShapeBounds());
}
//
// Create rays
//
vtkm::Int32 width = (vtkm::Int32)this->Canvas->GetWidth();
vtkm::Int32 height = (vtkm::Int32)this->Canvas->GetHeight();
vtkm::rendering::raytracing::Camera RayCamera;
vtkm::rendering::raytracing::Ray<vtkm::Float32> Rays;
RayCamera.SetParameters(camera, width, height);
RayCamera.CreateRays(Rays, shapeBounds);
Rays.Buffers.at(0).InitConst(0.f);
raytracing::RayOperations::MapCanvasToRays(Rays, camera, *this->Canvas);
tracer.SetField(processedField, scalarRange);
tracer.GetCamera() = RayCamera;
tracer.SetColorMap(this->ColorMap);
tracer.Render(Rays);
timer.Start();
this->Canvas->WriteToCanvas(Rays, Rays.Buffers.at(0).Buffer, camera);
}
if (this->CompositeBackground)
{
this->Canvas->BlendBackground();
}
vtkm::Float64 time = timer.GetElapsedTime();
logger->AddLogData("write_to_canvas", time);
time = tot_timer.GetElapsedTime();
logger->CloseLogEntry(time);
}
vtkm::rendering::Mapper* MapperGlyphScalar::NewCopy() const
{
return new vtkm::rendering::MapperGlyphScalar(*this);
}
}
} // vtkm::rendering

@ -0,0 +1,46 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_rendering_MapperGlyphScalar_h
#define vtk_m_rendering_MapperGlyphScalar_h
#include <vtkm/rendering/GlyphType.h>
#include <vtkm/rendering/MapperGlyphBase.h>
namespace vtkm
{
namespace rendering
{
class VTKM_RENDERING_EXPORT MapperGlyphScalar : public vtkm::rendering::MapperGlyphBase
{
public:
MapperGlyphScalar();
~MapperGlyphScalar();
vtkm::rendering::GlyphType GetGlyphType() const;
void SetGlyphType(vtkm::rendering::GlyphType glyphType);
void RenderCells(const vtkm::cont::UnknownCellSet& cellset,
const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& scalarField,
const vtkm::cont::ColorTable& colorTable,
const vtkm::rendering::Camera& camera,
const vtkm::Range& scalarRange) override;
vtkm::rendering::Mapper* NewCopy() const override;
protected:
vtkm::rendering::GlyphType GlyphType;
};
}
} //namespace vtkm::rendering
#endif //vtk_m_rendering_MapperGlyphScalar_h

@ -0,0 +1,174 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/rendering/MapperGlyphVector.h>
#include <vtkm/cont/Timer.h>
#include <vtkm/rendering/CanvasRayTracer.h>
#include <vtkm/rendering/raytracing/Camera.h>
#include <vtkm/rendering/raytracing/GlyphExtractorVector.h>
#include <vtkm/rendering/raytracing/GlyphIntersectorVector.h>
#include <vtkm/rendering/raytracing/Logger.h>
#include <vtkm/rendering/raytracing/RayOperations.h>
#include <vtkm/rendering/raytracing/RayTracer.h>
#include <vtkm/rendering/raytracing/SphereExtractor.h>
#include <vtkm/rendering/raytracing/SphereIntersector.h>
namespace vtkm
{
namespace rendering
{
MapperGlyphVector::MapperGlyphVector()
: MapperGlyphBase()
, GlyphType(vtkm::rendering::GlyphType::Arrow)
{
}
MapperGlyphVector::~MapperGlyphVector() {}
vtkm::rendering::GlyphType MapperGlyphVector::GetGlyphType() const
{
return this->GlyphType;
}
void MapperGlyphVector::SetGlyphType(vtkm::rendering::GlyphType glyphType)
{
if (!(glyphType == vtkm::rendering::GlyphType::Arrow))
{
throw vtkm::cont::ErrorBadValue("MapperGlyphVector: bad glyph type");
}
this->GlyphType = glyphType;
}
void MapperGlyphVector::RenderCells(const vtkm::cont::UnknownCellSet& cellset,
const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& field,
const vtkm::cont::ColorTable& vtkmNotUsed(colorTable),
const vtkm::rendering::Camera& camera,
const vtkm::Range& vtkmNotUsed(fieldRange))
{
raytracing::Logger* logger = raytracing::Logger::GetInstance();
vtkm::rendering::raytracing::RayTracer tracer;
tracer.Clear();
logger->OpenLogEntry("mapper_glyph_vector");
vtkm::cont::Timer tot_timer;
tot_timer.Start();
vtkm::cont::Timer timer;
vtkm::Bounds coordBounds = coords.GetBounds();
vtkm::Float32 baseSize = this->BaseSize;
if (baseSize == -1.f)
{
// set a default size
vtkm::Float64 lx = coordBounds.X.Length();
vtkm::Float64 ly = coordBounds.Y.Length();
vtkm::Float64 lz = coordBounds.Z.Length();
vtkm::Float64 mag = vtkm::Sqrt(lx * lx + ly * ly + lz * lz);
// same as used in vtk ospray
constexpr vtkm::Float64 heuristic = 500.;
baseSize = static_cast<vtkm::Float32>(mag / heuristic);
}
vtkm::rendering::raytracing::GlyphExtractorVector glyphExtractor;
vtkm::cont::DataSet processedDataSet = this->FilterPoints(cellset, coords, field);
vtkm::cont::UnknownCellSet processedCellSet = processedDataSet.GetCellSet();
vtkm::cont::CoordinateSystem processedCoords = processedDataSet.GetCoordinateSystem();
vtkm::cont::Field processedField = processedDataSet.GetField(0);
if (this->ScaleByValue)
{
vtkm::Float32 minSize = baseSize - baseSize * this->ScaleDelta;
vtkm::Float32 maxSize = baseSize + baseSize * this->ScaleDelta;
if (this->UseNodes)
{
glyphExtractor.ExtractCoordinates(processedCoords, processedField, minSize, maxSize);
}
else
{
glyphExtractor.ExtractCells(processedCellSet, processedField, minSize, maxSize);
}
}
else
{
if (this->UseNodes)
{
glyphExtractor.ExtractCoordinates(processedCoords, processedField, baseSize);
}
else
{
glyphExtractor.ExtractCells(processedCellSet, processedField, baseSize);
}
}
vtkm::Bounds shapeBounds;
if (glyphExtractor.GetNumberOfGlyphs() > 0)
{
auto glyphIntersector = std::make_shared<raytracing::GlyphIntersectorVector>(this->GlyphType);
if (this->GlyphType == vtkm::rendering::GlyphType::Arrow)
{
vtkm::Float32 arrowBodyRadius = 0.08f * baseSize;
vtkm::Float32 arrowHeadRadius = 0.16f * baseSize;
glyphIntersector->SetArrowRadii(arrowBodyRadius, arrowHeadRadius);
}
glyphIntersector->SetData(
processedCoords, glyphExtractor.GetPointIds(), glyphExtractor.GetSizes());
tracer.AddShapeIntersector(glyphIntersector);
shapeBounds.Include(glyphIntersector->GetShapeBounds());
}
//
// Create rays
//
vtkm::Int32 width = (vtkm::Int32)this->Canvas->GetWidth();
vtkm::Int32 height = (vtkm::Int32)this->Canvas->GetHeight();
vtkm::rendering::raytracing::Camera RayCamera;
vtkm::rendering::raytracing::Ray<vtkm::Float32> Rays;
RayCamera.SetParameters(camera, width, height);
RayCamera.CreateRays(Rays, shapeBounds);
Rays.Buffers.at(0).InitConst(0.f);
raytracing::RayOperations::MapCanvasToRays(Rays, camera, *this->Canvas);
auto magnitudeField = glyphExtractor.GetMagnitudeField();
auto magnitudeFieldRange = magnitudeField.GetRange().ReadPortal().Get(0);
tracer.SetField(magnitudeField, magnitudeFieldRange);
tracer.GetCamera() = RayCamera;
tracer.SetColorMap(this->ColorMap);
tracer.Render(Rays);
timer.Start();
this->Canvas->WriteToCanvas(Rays, Rays.Buffers.at(0).Buffer, camera);
if (this->CompositeBackground)
{
this->Canvas->BlendBackground();
}
vtkm::Float64 time = timer.GetElapsedTime();
logger->AddLogData("write_to_canvas", time);
time = tot_timer.GetElapsedTime();
logger->CloseLogEntry(time);
}
vtkm::rendering::Mapper* MapperGlyphVector::NewCopy() const
{
return new vtkm::rendering::MapperGlyphVector(*this);
}
}
} // vtkm::rendering

@ -0,0 +1,46 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_rendering_MapperGlyphVector_h
#define vtk_m_rendering_MapperGlyphVector_h
#include <vtkm/rendering/GlyphType.h>
#include <vtkm/rendering/MapperGlyphBase.h>
namespace vtkm
{
namespace rendering
{
class VTKM_RENDERING_EXPORT MapperGlyphVector : public vtkm::rendering::MapperGlyphBase
{
public:
MapperGlyphVector();
~MapperGlyphVector();
vtkm::rendering::GlyphType GetGlyphType() const;
void SetGlyphType(vtkm::rendering::GlyphType glyphType);
void RenderCells(const vtkm::cont::UnknownCellSet& cellset,
const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& scalarField,
const vtkm::cont::ColorTable& colorTable,
const vtkm::rendering::Camera& camera,
const vtkm::Range& scalarRange) override;
vtkm::rendering::Mapper* NewCopy() const override;
protected:
vtkm::rendering::GlyphType GlyphType;
};
}
} //namespace vtkm::rendering
#endif //vtk_m_rendering_MapperGlyphVector_h

@ -20,6 +20,10 @@ set(headers
ConnectivityTracer.h
CylinderExtractor.h
CylinderIntersector.h
GlyphExtractor.h
GlyphExtractorVector.h
GlyphIntersector.h
GlyphIntersectorVector.h
Logger.h
MeshConnectivityBuilder.h
MeshConnectivityContainers.h

@ -0,0 +1,223 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/rendering/raytracing/GlyphExtractor.h>
#include <vtkm/rendering/raytracing/RayTracingTypeDefs.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletMapTopology.h>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
namespace
{
class CountPoints : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
VTKM_CONT
CountPoints() {}
typedef void ControlSignature(CellSetIn cellset, FieldOut);
typedef void ExecutionSignature(CellShape, _2);
template <typename ShapeType>
VTKM_EXEC void operator()(ShapeType shape, vtkm::Id& points) const
{
points = (shape.Id == vtkm::CELL_SHAPE_VERTEX) ? 1 : 0;
}
}; // class CountPoints
class Pointify : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
VTKM_CONT
Pointify() {}
typedef void ControlSignature(CellSetIn cellset, FieldInCell, WholeArrayOut);
typedef void ExecutionSignature(_2, CellShape, PointIndices, WorkIndex, _3);
template <typename ShapeType, typename VecType, typename OutputPortal>
VTKM_EXEC void operator()(const vtkm::Id& pointOffset,
ShapeType shape,
const VecType& vtkmNotUsed(cellIndices),
const vtkm::Id& cellId,
OutputPortal& outputIndices) const
{
if (shape.Id == vtkm::CELL_SHAPE_VERTEX)
{
outputIndices.Set(pointOffset, cellId);
}
}
}; //class Pointify
class GetFieldSize : public vtkm::worklet::WorkletMapField
{
protected:
// vtkm::Float64 is used to handle field values that are very small or very large
// and could loose precision if vtkm::Float32 is used.
vtkm::Float64 MinSize;
vtkm::Float64 SizeDelta;
vtkm::Float64 MinValue;
vtkm::Float64 InverseDelta;
public:
VTKM_CONT
GetFieldSize(vtkm::Float64 minSize, vtkm::Float64 maxSize, vtkm::Range scalarRange)
: MinSize(minSize)
, SizeDelta(maxSize - minSize)
, MinValue(scalarRange.Min)
{
vtkm::Float64 delta = scalarRange.Max - scalarRange.Min;
if (delta != 0.)
InverseDelta = 1. / (delta);
else
InverseDelta = 0.; // just map scalar to 0;
}
typedef void ControlSignature(FieldIn, FieldOut, WholeArrayIn);
typedef void ExecutionSignature(_1, _2, _3);
template <typename ScalarPortalType>
VTKM_EXEC void operator()(const vtkm::Id& pointId,
vtkm::Float32& size,
const ScalarPortalType& scalars) const
{
vtkm::Float64 scalar = vtkm::Float64(scalars.Get(pointId));
vtkm::Float64 t = (scalar - this->MinValue) * this->InverseDelta;
size = static_cast<vtkm::Float32>(this->MinSize + t * this->SizeDelta);
}
}; //class GetFieldSize
} //namespace
GlyphExtractor::GlyphExtractor() = default;
void GlyphExtractor::ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords,
const vtkm::Float32 size)
{
this->SetPointIdsFromCoords(coords);
this->SetUniformSize(size);
}
void GlyphExtractor::ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& field,
const vtkm::Float32 minSize,
const vtkm::Float32 maxSize)
{
this->SetPointIdsFromCoords(coords);
this->SetVaryingSize(minSize, maxSize, field);
}
void GlyphExtractor::ExtractCells(const vtkm::cont::UnknownCellSet& cells, const vtkm::Float32 size)
{
this->SetPointIdsFromCells(cells);
this->SetUniformSize(size);
}
void GlyphExtractor::ExtractCells(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::Field& field,
const vtkm::Float32 minSize,
const vtkm::Float32 maxSize)
{
this->SetPointIdsFromCells(cells);
this->SetVaryingSize(minSize, maxSize, field);
}
void GlyphExtractor::SetUniformSize(const vtkm::Float32 size)
{
const vtkm::Id numValues = this->PointIds.GetNumberOfValues();
Sizes.AllocateAndFill(numValues, size);
}
void GlyphExtractor::SetPointIdsFromCoords(const vtkm::cont::CoordinateSystem& coords)
{
vtkm::Id size = coords.GetNumberOfPoints();
vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(size), this->PointIds);
}
void GlyphExtractor::SetPointIdsFromCells(const vtkm::cont::UnknownCellSet& cells)
{
using SingleType = vtkm::cont::CellSetSingleType<>;
vtkm::Id numCells = cells.GetNumberOfCells();
if (numCells == 0)
{
return;
}
//
// look for points in the cell set
//
if (cells.CanConvert<vtkm::cont::CellSetExplicit<>>())
{
auto cellsExplicit = cells.AsCellSet<vtkm::cont::CellSetExplicit<>>();
vtkm::cont::ArrayHandle<vtkm::Id> points;
vtkm::worklet::DispatcherMapTopology<CountPoints>(CountPoints()).Invoke(cellsExplicit, points);
vtkm::Id totalPoints = 0;
totalPoints = vtkm::cont::Algorithm::Reduce(points, vtkm::Id(0));
vtkm::cont::ArrayHandle<vtkm::Id> cellOffsets;
vtkm::cont::Algorithm::ScanExclusive(points, cellOffsets);
PointIds.Allocate(totalPoints);
vtkm::worklet::DispatcherMapTopology<Pointify>(Pointify())
.Invoke(cellsExplicit, cellOffsets, this->PointIds);
}
else if (cells.CanConvert<SingleType>())
{
SingleType pointCells = cells.AsCellSet<SingleType>();
vtkm::UInt8 shape_id = pointCells.GetCellShape(0);
if (shape_id == vtkm::CELL_SHAPE_VERTEX)
{
vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(numCells), this->PointIds);
}
}
}
void GlyphExtractor::SetVaryingSize(const vtkm::Float32 minSize,
const vtkm::Float32 maxSize,
const vtkm::cont::Field& field)
{
vtkm::cont::ArrayHandle<vtkm::Range> rangeArray = field.GetRange();
if (rangeArray.GetNumberOfValues() != 1)
{
throw vtkm::cont::ErrorBadValue("Glyph Extractor: scalar field must have one component");
}
vtkm::Range range = rangeArray.ReadPortal().Get(0);
Sizes.Allocate(this->PointIds.GetNumberOfValues());
vtkm::worklet::DispatcherMapField<GetFieldSize>(GetFieldSize(minSize, maxSize, range))
.Invoke(this->PointIds, this->Sizes, vtkm::rendering::raytracing::GetScalarFieldArray(field));
}
vtkm::cont::ArrayHandle<vtkm::Id> GlyphExtractor::GetPointIds()
{
return this->PointIds;
}
vtkm::cont::ArrayHandle<vtkm::Float32> GlyphExtractor::GetSizes()
{
return this->Sizes;
}
vtkm::Id GlyphExtractor::GetNumberOfGlyphs() const
{
return this->PointIds.GetNumberOfValues();
}
}
}
} //namespace vtkm::rendering::raytracing

@ -0,0 +1,74 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_rendering_raytracing_Glyph_Extractor_h
#define vtk_m_rendering_raytracing_Glyph_Extractor_h
#include <vtkm/cont/DataSet.h>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
class GlyphExtractor
{
public:
GlyphExtractor();
//
// Extract all nodes using a constant size
//
void ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords, const vtkm::Float32 size);
//
// Set size based on scalar field values. Each is interpolated from min to max
//
void ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& field,
const vtkm::Float32 minSize,
const vtkm::Float32 maxSize);
//
// Extract all vertex shapes with constant size
//
void ExtractCells(const vtkm::cont::UnknownCellSet& cells, vtkm::Float32 size);
//
// Extract all vertex elements with size based on scalar values
//
void ExtractCells(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::Field& field,
const vtkm::Float32 minSize,
const vtkm::Float32 maxSize);
vtkm::cont::ArrayHandle<vtkm::Id> GetPointIds();
vtkm::cont::ArrayHandle<vtkm::Float32> GetSizes();
vtkm::Id GetNumberOfGlyphs() const;
protected:
void SetUniformSize(const vtkm::Float32 size);
void SetVaryingSize(const vtkm::Float32 minSize,
const vtkm::Float32 maxSize,
const vtkm::cont::Field& field);
void SetPointIdsFromCoords(const vtkm::cont::CoordinateSystem& coords);
void SetPointIdsFromCells(const vtkm::cont::UnknownCellSet& cells);
vtkm::cont::ArrayHandle<vtkm::Id> PointIds;
vtkm::cont::ArrayHandle<vtkm::Float32> Sizes;
}; // class GlyphExtractor
}
}
} //namespace vtkm::rendering::raytracing
#endif //vtk_m_rendering_raytracing_Glyph_Extractor_h

@ -0,0 +1,352 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/VectorAnalysis.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/rendering/raytracing/GlyphExtractorVector.h>
#include <vtkm/rendering/raytracing/RayTracingTypeDefs.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/worklet/WorkletMapTopology.h>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
namespace
{
class CountPoints : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
VTKM_CONT
CountPoints() {}
typedef void ControlSignature(CellSetIn cellset, FieldOut);
typedef void ExecutionSignature(CellShape, _2);
template <typename ShapeType>
VTKM_EXEC void operator()(ShapeType shape, vtkm::Id& points) const
{
points = (shape.Id == vtkm::CELL_SHAPE_VERTEX) ? 1 : 0;
}
}; // class CountPoints
class Pointify : public vtkm::worklet::WorkletVisitCellsWithPoints
{
public:
VTKM_CONT
Pointify() {}
typedef void ControlSignature(CellSetIn cellset, FieldInCell, WholeArrayOut);
typedef void ExecutionSignature(_2, CellShape, PointIndices, WorkIndex, _3);
template <typename ShapeType, typename VecType, typename OutputPortal>
VTKM_EXEC void operator()(const vtkm::Id& pointOffset,
ShapeType shape,
const VecType& vtkmNotUsed(cellIndices),
const vtkm::Id& cellId,
OutputPortal& outputIndices) const
{
if (shape.Id == vtkm::CELL_SHAPE_VERTEX)
{
outputIndices.Set(pointOffset, cellId);
}
}
}; //class Pointify
struct MinFunctor
{
template <typename VecType>
VTKM_EXEC VecType operator()(const VecType& x, const VecType& y) const
{
return (vtkm::MagnitudeSquared(y) < vtkm::MagnitudeSquared(x)) ? y : x;
}
};
struct MaxFunctor
{
template <typename VecType>
VTKM_EXEC VecType operator()(const VecType& x, const VecType& y) const
{
return (vtkm::MagnitudeSquared(x) < vtkm::MagnitudeSquared(y)) ? y : x;
}
};
class GetFieldSize : public vtkm::worklet::WorkletMapField
{
protected:
vtkm::Float64 MinSize;
vtkm::Float64 SizeDelta;
vtkm::Float64 MinValueMagnitude;
vtkm::Float64 InverseDelta;
public:
VTKM_CONT
GetFieldSize(vtkm::Float64 minSize,
vtkm::Float64 maxSize,
vtkm::Vec3f_64 minValue,
vtkm::Vec3f_64 maxValue)
: MinSize(minSize)
, SizeDelta(maxSize - minSize)
{
MinValueMagnitude = vtkm::Magnitude(minValue);
vtkm::Float64 minMag = vtkm::Magnitude(minValue);
vtkm::Float64 maxMag = vtkm::Magnitude(maxValue);
vtkm::Float64 delta = maxMag - minMag;
if (delta != 0.)
InverseDelta = 1. / (delta);
else
InverseDelta = 0.; // just map scalar to 0;
}
typedef void ControlSignature(FieldIn, FieldOut, WholeArrayIn);
typedef void ExecutionSignature(_1, _2, _3);
template <typename FieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& pointId,
vtkm::Vec3f_32& size,
const FieldPortalType& field) const
{
using ValueType = typename FieldPortalType::ValueType;
ValueType fieldVal = field.Get(pointId);
vtkm::Float64 fieldValMag = vtkm::Magnitude(fieldVal);
vtkm::Normalize(fieldVal);
vtkm::Float64 t = (fieldValMag - MinValueMagnitude) * InverseDelta;
vtkm::Float64 sizeMag = MinSize + t * SizeDelta;
vtkm::Vec3f_64 tempSize = fieldVal * sizeMag;
size[0] = static_cast<vtkm::Float32>(tempSize[0]);
size[1] = static_cast<vtkm::Float32>(tempSize[1]);
size[2] = static_cast<vtkm::Float32>(tempSize[2]);
}
}; //class GetFieldSize
class FieldMagnitude : public vtkm::worklet::WorkletMapField
{
public:
VTKM_CONT
FieldMagnitude() {}
typedef void ControlSignature(FieldIn, WholeArrayIn, WholeArrayInOut);
typedef void ExecutionSignature(_1, _2, _3);
template <typename FieldPortalType, typename MagnitudeFieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& pointId,
const FieldPortalType& field,
MagnitudeFieldPortalType& magnitudeField) const
{
using FieldValueType = typename FieldPortalType::ValueType;
FieldValueType fieldVal = field.Get(pointId);
vtkm::Float32 fieldValMag = static_cast<vtkm::Float32>(vtkm::Magnitude(fieldVal));
magnitudeField.Set(pointId, fieldValMag);
}
}; //class FieldMagnitude
class UniformFieldMagnitude : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldOut, WholeArrayIn);
using ExecutionSignature = void(_1, _2, _3);
VTKM_CONT
UniformFieldMagnitude(vtkm::Float32 uniformMagnitude)
: UniformMagnitude(uniformMagnitude)
{
}
template <typename FieldPortalType>
VTKM_EXEC void operator()(const vtkm::Id& pointId,
vtkm::Vec3f_32& size,
const FieldPortalType& field) const
{
vtkm::Vec3f_32 fieldVal = static_cast<vtkm::Vec3f_32>(field.Get(pointId));
size = vtkm::Normal(fieldVal) * this->UniformMagnitude;
}
vtkm::Float32 UniformMagnitude;
}; //class UniformFieldMagnitude
} //namespace
GlyphExtractorVector::GlyphExtractorVector() = default;
void GlyphExtractorVector::ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& field,
const vtkm::Float32 size)
{
this->SetPointIdsFromCoords(coords);
this->SetUniformSize(size, field);
}
void GlyphExtractorVector::ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& field,
const vtkm::Float32 minSize,
const vtkm::Float32 maxSize)
{
this->SetPointIdsFromCoords(coords);
this->SetVaryingSize(minSize, maxSize, field);
}
void GlyphExtractorVector::ExtractCells(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::Field& field,
const vtkm::Float32 size)
{
this->SetPointIdsFromCells(cells);
this->SetUniformSize(size, field);
}
void GlyphExtractorVector::ExtractCells(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::Field& field,
const vtkm::Float32 minSize,
const vtkm::Float32 maxSize)
{
this->SetPointIdsFromCells(cells);
this->SetVaryingSize(minSize, maxSize, field);
}
void GlyphExtractorVector::SetUniformSize(const vtkm::Float32 size, const vtkm::cont::Field& field)
{
this->ExtractMagnitudeField(field);
this->Sizes.Allocate(this->PointIds.GetNumberOfValues());
vtkm::cont::Invoker invoker;
invoker(UniformFieldMagnitude(size),
this->PointIds,
this->Sizes,
vtkm::rendering::raytracing::GetVec3FieldArray(field));
}
void GlyphExtractorVector::ExtractMagnitudeField(const vtkm::cont::Field& field)
{
vtkm::cont::ArrayHandle<vtkm::Float32> magnitudeArray;
magnitudeArray.Allocate(this->PointIds.GetNumberOfValues());
vtkm::worklet::DispatcherMapField<FieldMagnitude>(FieldMagnitude())
.Invoke(this->PointIds, vtkm::rendering::raytracing::GetVec3FieldArray(field), magnitudeArray);
this->MagnitudeField = vtkm::cont::Field(field);
this->MagnitudeField.SetData(magnitudeArray);
}
void GlyphExtractorVector::SetPointIdsFromCoords(const vtkm::cont::CoordinateSystem& coords)
{
vtkm::Id size = coords.GetNumberOfPoints();
vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(size), this->PointIds);
}
void GlyphExtractorVector::SetPointIdsFromCells(const vtkm::cont::UnknownCellSet& cells)
{
using SingleType = vtkm::cont::CellSetSingleType<>;
vtkm::Id numCells = cells.GetNumberOfCells();
if (numCells == 0)
{
return;
}
//
// look for points in the cell set
//
if (cells.CanConvert<vtkm::cont::CellSetExplicit<>>())
{
auto cellsExplicit = cells.AsCellSet<vtkm::cont::CellSetExplicit<>>();
vtkm::cont::ArrayHandle<vtkm::Id> points;
vtkm::worklet::DispatcherMapTopology<CountPoints>(CountPoints()).Invoke(cellsExplicit, points);
vtkm::Id totalPoints = 0;
totalPoints = vtkm::cont::Algorithm::Reduce(points, vtkm::Id(0));
vtkm::cont::ArrayHandle<vtkm::Id> cellOffsets;
vtkm::cont::Algorithm::ScanExclusive(points, cellOffsets);
PointIds.Allocate(totalPoints);
vtkm::worklet::DispatcherMapTopology<Pointify>(Pointify())
.Invoke(cellsExplicit, cellOffsets, this->PointIds);
}
else if (cells.CanConvert<SingleType>())
{
SingleType pointCells = cells.AsCellSet<SingleType>();
vtkm::UInt8 shape_id = pointCells.GetCellShape(0);
if (shape_id == vtkm::CELL_SHAPE_VERTEX)
{
vtkm::cont::ArrayCopy(vtkm::cont::ArrayHandleIndex(numCells), this->PointIds);
}
}
}
void GlyphExtractorVector::SetVaryingSize(const vtkm::Float32 minSize,
const vtkm::Float32 maxSize,
const vtkm::cont::Field& field)
{
vtkm::cont::ArrayHandle<vtkm::Range> rangeArray = field.GetRange();
if (rangeArray.GetNumberOfValues() != 3)
{
throw vtkm::cont::ErrorBadValue(
"Glyph Extractor Vector: vector field must have three components");
}
using Vec3f_32Handle = vtkm::cont::ArrayHandle<vtkm::Vec3f_32>;
using Vec3f_64Handle = vtkm::cont::ArrayHandle<vtkm::Vec3f_64>;
vtkm::cont::UnknownArrayHandle fieldUnknownHandle = field.GetData();
vtkm::Vec3f_32 minFieldValue, maxFieldValue;
if (fieldUnknownHandle.CanConvert<Vec3f_64Handle>())
{
Vec3f_64Handle fieldArray;
field.GetData().AsArrayHandle(fieldArray);
vtkm::Vec3f_64 initVal = vtkm::cont::ArrayGetValue(0, fieldArray);
minFieldValue =
static_cast<vtkm::Vec3f_32>(vtkm::cont::Algorithm::Reduce(fieldArray, initVal, MinFunctor()));
maxFieldValue =
static_cast<vtkm::Vec3f_32>(vtkm::cont::Algorithm::Reduce(fieldArray, initVal, MaxFunctor()));
}
else
{
Vec3f_32Handle fieldArray;
field.GetData().AsArrayHandle(fieldArray);
vtkm::Vec3f_32 initVal = vtkm::cont::ArrayGetValue(0, fieldArray);
minFieldValue =
static_cast<vtkm::Vec3f_32>(vtkm::cont::Algorithm::Reduce(fieldArray, initVal, MinFunctor()));
maxFieldValue =
static_cast<vtkm::Vec3f_32>(vtkm::cont::Algorithm::Reduce(fieldArray, initVal, MaxFunctor()));
}
this->ExtractMagnitudeField(field);
this->Sizes.Allocate(this->PointIds.GetNumberOfValues());
vtkm::worklet::DispatcherMapField<GetFieldSize>(
GetFieldSize(minSize, maxSize, minFieldValue, maxFieldValue))
.Invoke(this->PointIds, this->Sizes, vtkm::rendering::raytracing::GetVec3FieldArray(field));
}
vtkm::cont::ArrayHandle<vtkm::Id> GlyphExtractorVector::GetPointIds()
{
return this->PointIds;
}
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float32, 3>> GlyphExtractorVector::GetSizes()
{
return this->Sizes;
}
vtkm::cont::Field GlyphExtractorVector::GetMagnitudeField()
{
return this->MagnitudeField;
}
vtkm::Id GlyphExtractorVector::GetNumberOfGlyphs() const
{
return this->PointIds.GetNumberOfValues();
}
}
}
} //namespace vtkm::rendering::raytracing

@ -0,0 +1,83 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_rendering_raytracing_Glyph_Extractor_Vector_h
#define vtk_m_rendering_raytracing_Glyph_Extractor_Vector_h
#include <vtkm/cont/DataSet.h>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
class GlyphExtractorVector
{
public:
GlyphExtractorVector();
//
// Extract all nodes using a constant size
//
void ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& field,
const vtkm::Float32 size);
//
// Set size based on scalar field values. Each is interpolated from min to max
//
void ExtractCoordinates(const vtkm::cont::CoordinateSystem& coords,
const vtkm::cont::Field& field,
const vtkm::Float32 minSize,
const vtkm::Float32 maxSize);
//
// Extract all vertex shapes with constant size
//
void ExtractCells(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::Field& field,
vtkm::Float32 size);
//
// Extract all vertex elements with size based on scalar values
//
void ExtractCells(const vtkm::cont::UnknownCellSet& cells,
const vtkm::cont::Field& field,
const vtkm::Float32 minSize,
const vtkm::Float32 maxSize);
vtkm::cont::ArrayHandle<vtkm::Id> GetPointIds();
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float32, 3>> GetSizes();
vtkm::cont::Field GetMagnitudeField();
vtkm::Id GetNumberOfGlyphs() const;
protected:
void SetUniformSize(const vtkm::Float32 size, const vtkm::cont::Field& field);
void SetVaryingSize(const vtkm::Float32 minSize,
const vtkm::Float32 maxSize,
const vtkm::cont::Field& field);
void SetPointIdsFromCoords(const vtkm::cont::CoordinateSystem& coords);
void SetPointIdsFromCells(const vtkm::cont::UnknownCellSet& cells);
void ExtractMagnitudeField(const vtkm::cont::Field& field);
vtkm::cont::ArrayHandle<vtkm::Id> PointIds;
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float32, 3>> Sizes;
vtkm::cont::Field MagnitudeField;
}; // class GlyphExtractorVector
}
}
} //namespace vtkm::rendering::raytracing
#endif //vtk_m_rendering_raytracing_Glyph_Extractor_Vector_h

@ -0,0 +1,637 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/VectorAnalysis.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/rendering/raytracing/BVHTraverser.h>
#include <vtkm/rendering/raytracing/GlyphIntersector.h>
#include <vtkm/rendering/raytracing/RayOperations.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
// This line is at the end to prevent warnings when building for CUDA
#include <vtkm/Swap.h>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
namespace detail
{
class FindGlyphAABBs : public vtkm::worklet::WorkletMapField
{
public:
VTKM_CONT
FindGlyphAABBs() {}
typedef void ControlSignature(FieldIn,
FieldIn,
FieldOut,
FieldOut,
FieldOut,
FieldOut,
FieldOut,
FieldOut,
WholeArrayIn);
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8, _9);
template <typename PointPortalType>
VTKM_EXEC void operator()(const vtkm::Id& pointId,
const vtkm::Float32& size,
vtkm::Float32& xmin,
vtkm::Float32& ymin,
vtkm::Float32& zmin,
vtkm::Float32& xmax,
vtkm::Float32& ymax,
vtkm::Float32& zmax,
const PointPortalType& points) const
{
vtkm::Vec3f_32 point;
point = static_cast<vtkm::Vec3f_32>(points.Get(pointId));
vtkm::Float32 absSize = vtkm::Abs(size);
xmin = point[0] - absSize;
xmax = point[0] + absSize;
ymin = point[1] - absSize;
ymax = point[1] + absSize;
zmin = point[2] - absSize;
zmax = point[2] + absSize;
}
}; //class FindGlyphAABBs
template <typename Device>
class GlyphLeafIntersector
{
public:
using IdHandle = vtkm::cont::ArrayHandle<vtkm::Id>;
using IdArrayPortal = typename IdHandle::ReadPortalType;
using FloatHandle = vtkm::cont::ArrayHandle<vtkm::Float32>;
using VecHandle = vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float32, 3>>;
using FloatPortal = typename FloatHandle::ReadPortalType;
using VecPortal = typename VecHandle::ReadPortalType;
IdArrayPortal PointIds;
FloatPortal Sizes;
vtkm::rendering::GlyphType GlyphType;
GlyphLeafIntersector() {}
GlyphLeafIntersector(const IdHandle& pointIds,
const FloatHandle& sizes,
vtkm::rendering::GlyphType glyphType,
vtkm::cont::Token& token)
: PointIds(pointIds.PrepareForInput(Device(), token))
, Sizes(sizes.PrepareForInput(Device(), token))
, GlyphType(glyphType)
{
}
template <typename PointPortalType, typename LeafPortalType, typename Precision>
VTKM_EXEC inline void IntersectLeaf(const vtkm::Int32& currentNode,
const vtkm::Vec<Precision, 3>& origin,
const vtkm::Vec<Precision, 3>& dir,
const PointPortalType& points,
vtkm::Id& hitIndex,
Precision& closestDistance,
Precision& minU,
Precision& minV,
LeafPortalType leafs,
const Precision& minDistance) const
{
const vtkm::Id glyphCount = leafs.Get(currentNode);
for (vtkm::Id i = 1; i <= glyphCount; ++i)
{
const vtkm::Id idx = leafs.Get(currentNode + i);
vtkm::Id pointIndex = PointIds.Get(idx);
Precision size = Sizes.Get(idx);
vtkm::Vec<Precision, 3> point = vtkm::Vec<Precision, 3>(points.Get(pointIndex));
if (this->GlyphType == vtkm::rendering::GlyphType::Sphere)
{
this->IntersectSphere(
origin, dir, point, size, pointIndex, hitIndex, closestDistance, minU, minV, minDistance);
}
else if (this->GlyphType == vtkm::rendering::GlyphType::Cube)
{
this->IntersectCube(
origin, dir, point, size, pointIndex, hitIndex, closestDistance, minU, minV, minDistance);
}
else if (this->GlyphType == vtkm::rendering::GlyphType::Axes)
{
this->IntersectAxes(
origin, dir, point, size, pointIndex, hitIndex, closestDistance, minU, minV, minDistance);
}
}
}
template <typename Precision>
VTKM_EXEC inline void IntersectSphere(const vtkm::Vec<Precision, 3>& origin,
const vtkm::Vec<Precision, 3>& dir,
const vtkm::Vec<Precision, 3>& point,
const Precision& size,
const vtkm::Id& pointIndex,
vtkm::Id& hitIndex,
Precision& closestDistance,
Precision& vtkmNotUsed(minU),
Precision& vtkmNotUsed(minV),
const Precision& minDistance) const
{
vtkm::Vec<Precision, 3> l = point - origin;
Precision dot1 = vtkm::dot(l, dir);
if (dot1 >= 0)
{
Precision d = vtkm::dot(l, l) - dot1 * dot1;
Precision r2 = size * size;
if (d <= r2)
{
Precision tch = vtkm::Sqrt(r2 - d);
Precision t0 = dot1 - tch;
if (t0 < closestDistance && t0 > minDistance)
{
hitIndex = pointIndex;
closestDistance = t0;
}
}
}
}
template <typename Precision>
VTKM_EXEC inline void IntersectCube(const vtkm::Vec<Precision, 3>& origin,
const vtkm::Vec<Precision, 3>& dir,
const vtkm ::Vec<Precision, 3>& point,
const Precision& size,
const vtkm::Id& pointIndex,
vtkm::Id& hitIndex,
Precision& closestDistance,
Precision& vtkmNotUsed(minU),
Precision& vtkmNotUsed(minV),
const Precision& minDistance) const
{
Precision xmin, xmax, ymin, ymax, zmin, zmax;
this->CalculateAABB(point, size, xmin, ymin, zmin, xmax, ymax, zmax);
Precision tmin = (xmin - origin[0]) / dir[0];
Precision tmax = (xmax - origin[0]) / dir[0];
if (tmin > tmax)
vtkm::Swap(tmin, tmax);
Precision tymin = (ymin - origin[1]) / dir[1];
Precision tymax = (ymax - origin[1]) / dir[1];
if (tymin > tymax)
vtkm::Swap(tymin, tymax);
if ((tmin > tymax) || (tymin > tmax))
return;
if (tymin > tmin)
tmin = tymin;
if (tymax < tmax)
tmax = tymax;
Precision tzmin = (zmin - origin[2]) / dir[2];
Precision tzmax = (zmax - origin[2]) / dir[2];
if (tzmin > tzmax)
vtkm::Swap(tzmin, tzmax);
if ((tmin > tzmax) || (tzmin > tmax))
return;
if (tzmin > tmin)
tmin = tzmin;
if (tzmax < tmax)
tmax = tzmax;
if (tmin < closestDistance && tmin > minDistance)
{
hitIndex = pointIndex;
closestDistance = tmin;
}
}
template <typename Precision>
VTKM_EXEC inline void IntersectAxes(const vtkm::Vec<Precision, 3>& origin,
const vtkm::Vec<Precision, 3>& dir,
const vtkm::Vec<Precision, 3>& point,
const Precision& size,
const vtkm::Id& pointIndex,
vtkm::Id& hitIndex,
Precision& closestDistance,
Precision& vtkmNotUsed(minU),
Precision& vtkmNotUsed(minV),
const Precision& minDistance) const
{
Precision xmin, xmax, ymin, ymax, zmin, zmax;
this->CalculateAABB(point, size, xmin, ymin, zmin, xmax, ymax, zmax);
Precision t = (point[0] - origin[0]) / dir[0];
vtkm::Vec<Precision, 3> intersection = origin + t * dir;
if ((intersection[1] >= ymin && intersection[1] <= ymax) &&
(intersection[2] >= zmin && intersection[2] <= zmax))
{
if (t < closestDistance && t > minDistance)
{
hitIndex = pointIndex;
closestDistance = t;
}
}
t = (point[1] - origin[1]) / dir[1];
intersection = origin + t * dir;
if ((intersection[0] >= xmin && intersection[0] <= xmax) &&
(intersection[2] >= zmin && intersection[2] <= zmax))
{
if (t < closestDistance && t > minDistance)
{
hitIndex = pointIndex;
closestDistance = t;
}
}
t = (point[2] - origin[2]) / dir[2];
intersection = origin + t * dir;
if ((intersection[0] >= xmin && intersection[0] <= xmax) &&
(intersection[1] >= ymin && intersection[1] <= ymax))
{
if (t < closestDistance && t > minDistance)
{
hitIndex = pointIndex;
closestDistance = t;
}
}
}
template <typename Precision>
VTKM_EXEC void CalculateAABB(const vtkm::Vec<Precision, 3>& point,
const Precision& size,
Precision& xmin,
Precision& ymin,
Precision& zmin,
Precision& xmax,
Precision& ymax,
Precision& zmax) const
{
Precision absSize = vtkm::Abs(size);
xmin = point[0] - absSize;
xmax = point[0] + absSize;
ymin = point[1] - absSize;
ymax = point[1] + absSize;
zmin = point[2] - absSize;
zmax = point[2] + absSize;
}
};
class GlyphLeafWrapper : public vtkm::cont::ExecutionObjectBase
{
protected:
using IdHandle = vtkm::cont::ArrayHandle<vtkm::Id>;
using FloatHandle = vtkm::cont::ArrayHandle<vtkm::Float32>;
using VecHandle = vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float32, 3>>;
IdHandle PointIds;
FloatHandle Sizes;
vtkm::rendering::GlyphType GlyphType;
public:
GlyphLeafWrapper(IdHandle& pointIds, FloatHandle sizes, vtkm::rendering::GlyphType glyphType)
: PointIds(pointIds)
, Sizes(sizes)
, GlyphType(glyphType)
{
}
template <typename Device>
VTKM_CONT GlyphLeafIntersector<Device> PrepareForExecution(Device, vtkm::cont::Token& token) const
{
return GlyphLeafIntersector<Device>(this->PointIds, this->Sizes, this->GlyphType, token);
}
}; // class GlyphLeafWrapper
class CalculateGlyphNormals : public vtkm::worklet::WorkletMapField
{
public:
VTKM_CONT
CalculateGlyphNormals(vtkm::rendering::GlyphType glyphType)
: GlyphType(glyphType)
{
}
typedef void ControlSignature(FieldIn,
FieldIn,
FieldIn,
FieldOut,
FieldOut,
FieldOut,
WholeArrayIn,
WholeArrayIn,
WholeArrayIn);
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8, _9);
template <typename Precision,
typename PointPortalType,
typename IndicesPortalType,
typename SizesPortalType>
VTKM_EXEC inline void operator()(const vtkm::Id& hitIndex,
const vtkm::Vec<Precision, 3>& rayDir,
const vtkm::Vec<Precision, 3>& intersection,
Precision& normalX,
Precision& normalY,
Precision& normalZ,
const PointPortalType& points,
const IndicesPortalType& indicesPortal,
const SizesPortalType& sizesPortal) const
{
if (hitIndex < 0)
return;
vtkm::Id pointId = indicesPortal.Get(hitIndex);
vtkm::Vec<Precision, 3> point = points.Get(pointId);
Precision size = sizesPortal.Get(hitIndex);
if (this->GlyphType == vtkm::rendering::GlyphType::Sphere)
{
this->CalculateNormalForSphere(rayDir, intersection, point, size, normalX, normalY, normalZ);
}
else if (this->GlyphType == vtkm::rendering::GlyphType::Cube)
{
this->CalculateNormalForCube(rayDir, intersection, point, size, normalX, normalY, normalZ);
}
else if (this->GlyphType == vtkm::rendering::GlyphType::Axes)
{
this->CalculateNormalForAxes(rayDir, intersection, point, size, normalX, normalY, normalZ);
}
}
template <typename Precision>
VTKM_EXEC inline void CalculateNormalForSphere(const vtkm::Vec<Precision, 3>& rayDir,
const vtkm::Vec<Precision, 3>& intersection,
const vtkm::Vec<Precision, 3>& point,
const Precision& vtkmNotUsed(size),
Precision& normalX,
Precision& normalY,
Precision& normalZ) const
{
vtkm::Vec<Precision, 3> normal = intersection - point;
vtkm::Normalize(normal);
// Flip normal if it is pointing the wrong way
if (vtkm::Dot(normal, rayDir) > 0.0f)
{
normal = -normal;
}
normalX = normal[0];
normalY = normal[1];
normalZ = normal[2];
}
template <typename Precision>
VTKM_EXEC inline void CalculateNormalForCube(const vtkm::Vec<Precision, 3>& rayDir,
const vtkm::Vec<Precision, 3>& intersection,
const vtkm::Vec<Precision, 3>& point,
const Precision& size,
Precision& normalX,
Precision& normalY,
Precision& normalZ) const
{
vtkm::Vec<Precision, 3> lp = intersection - point;
// Localize the intersection point to the surface of the cube.
// One of the components will be 1 or -1 based on the face it lies on
lp = lp * (1.0f / size);
Precision eps = 1e-4f;
vtkm::Vec<Precision, 3> normal{ 0.0f, 0.0f, 0.0f };
normal[0] = (vtkm::Abs(vtkm::Abs(lp[0]) - 1.0f) <= eps) ? lp[0] : 0.0f;
normal[1] = (vtkm::Abs(vtkm::Abs(lp[1]) - 1.0f) <= eps) ? lp[1] : 0.0f;
normal[2] = (vtkm::Abs(vtkm::Abs(lp[2]) - 1.0f) <= eps) ? lp[2] : 0.0f;
vtkm::Normalize(normal);
// Flip normal if it is pointing the wrong way
if (vtkm::Dot(normal, rayDir) > 0.0f)
{
normal = -normal;
}
normalX = normal[0];
normalY = normal[1];
normalZ = normal[2];
}
template <typename Precision>
VTKM_EXEC inline void CalculateNormalForAxes(const vtkm::Vec<Precision, 3>& rayDir,
const vtkm::Vec<Precision, 3>& intersection,
const vtkm::Vec<Precision, 3>& point,
const Precision& vtkmNotUsed(size),
Precision& normalX,
Precision& normalY,
Precision& normalZ) const
{
vtkm::Vec<Precision, 3> normal{ 0.0f, 0.0f, 0.0f };
if (this->ApproxEquals(point[0], intersection[0]))
{
normal[0] = 1.0f;
}
else if (this->ApproxEquals(point[1], intersection[1]))
{
normal[1] = 1.0f;
}
else
{
normal[2] = 1.0f;
}
// Flip normal if it is pointing the wrong way
if (vtkm::Dot(normal, rayDir) > 0.0f)
{
normal = -normal;
}
normalX = normal[0];
normalY = normal[1];
normalZ = normal[2];
}
template <typename Precision>
VTKM_EXEC inline Precision ApproxEquals(Precision x, Precision y, Precision eps = 1e-5f) const
{
return vtkm::Abs(x - y) <= eps;
}
vtkm::rendering::GlyphType GlyphType;
}; //class CalculateGlyphNormals
template <typename Precision>
class GetScalars : public vtkm::worklet::WorkletMapField
{
private:
Precision MinScalar;
Precision InvDeltaScalar;
bool Normalize;
public:
VTKM_CONT
GetScalars(const vtkm::Float32& minScalar, const vtkm::Float32& maxScalar)
: MinScalar(minScalar)
{
Normalize = true;
if (minScalar >= maxScalar)
{
// support the scalar renderer
Normalize = false;
this->InvDeltaScalar = Precision(0.f);
}
else
{
//Make sure the we don't divide by zero on
//something like an iso-surface
this->InvDeltaScalar = 1.f / (maxScalar - this->MinScalar);
}
}
typedef void ControlSignature(FieldIn, FieldOut, WholeArrayIn, WholeArrayIn);
typedef void ExecutionSignature(_1, _2, _3, _4);
template <typename ScalarPortalType, typename IndicesPortalType>
VTKM_EXEC void operator()(const vtkm::Id& hitIndex,
Precision& scalar,
const ScalarPortalType& scalars,
const IndicesPortalType& indicesPortal) const
{
if (hitIndex < 0)
return;
vtkm::Id pointId = indicesPortal.Get(hitIndex);
scalar = Precision(scalars.Get(pointId));
if (Normalize)
{
scalar = (scalar - this->MinScalar) * this->InvDeltaScalar;
}
}
}; //class GetScalars
}
GlyphIntersector::GlyphIntersector(vtkm::rendering::GlyphType glyphType)
: ShapeIntersector()
{
this->SetGlyphType(glyphType);
}
GlyphIntersector::~GlyphIntersector() {}
void GlyphIntersector::SetGlyphType(vtkm::rendering::GlyphType glyphType)
{
this->GlyphType = glyphType;
}
void GlyphIntersector::SetData(const vtkm::cont::CoordinateSystem& coords,
vtkm::cont::ArrayHandle<vtkm::Id> pointIds,
vtkm::cont::ArrayHandle<vtkm::Float32> sizes)
{
this->PointIds = pointIds;
this->Sizes = sizes;
this->CoordsHandle = coords;
AABBs AABB;
vtkm::worklet::DispatcherMapField<detail::FindGlyphAABBs>(detail::FindGlyphAABBs())
.Invoke(PointIds,
Sizes,
AABB.xmins,
AABB.ymins,
AABB.zmins,
AABB.xmaxs,
AABB.ymaxs,
AABB.zmaxs,
CoordsHandle);
this->SetAABBs(AABB);
}
void GlyphIntersector::IntersectRays(Ray<vtkm::Float32>& rays, bool returnCellIndex)
{
IntersectRaysImp(rays, returnCellIndex);
}
void GlyphIntersector::IntersectRays(Ray<vtkm::Float64>& rays, bool returnCellIndex)
{
IntersectRaysImp(rays, returnCellIndex);
}
template <typename Precision>
void GlyphIntersector::IntersectRaysImp(Ray<Precision>& rays, bool vtkmNotUsed(returnCellIndex))
{
detail::GlyphLeafWrapper leafIntersector(this->PointIds, Sizes, this->GlyphType);
BVHTraverser traverser;
traverser.IntersectRays(rays, this->BVH, leafIntersector, this->CoordsHandle);
RayOperations::UpdateRayStatus(rays);
}
template <typename Precision>
void GlyphIntersector::IntersectionDataImp(Ray<Precision>& rays,
const vtkm::cont::Field scalarField,
const vtkm::Range& scalarRange)
{
ShapeIntersector::IntersectionPoint(rays);
const bool isSupportedField = scalarField.IsFieldCell() || scalarField.IsFieldPoint();
if (!isSupportedField)
{
throw vtkm::cont::ErrorBadValue(
"GlyphIntersector: Field not accociated with a cell set or field");
}
vtkm::worklet::DispatcherMapField<detail::CalculateGlyphNormals>(
detail::CalculateGlyphNormals(this->GlyphType))
.Invoke(rays.HitIdx,
rays.Dir,
rays.Intersection,
rays.NormalX,
rays.NormalY,
rays.NormalZ,
CoordsHandle,
PointIds,
Sizes);
vtkm::worklet::DispatcherMapField<detail::GetScalars<Precision>>(
detail::GetScalars<Precision>(vtkm::Float32(scalarRange.Min), vtkm::Float32(scalarRange.Max)))
.Invoke(rays.HitIdx,
rays.Scalar,
vtkm::rendering::raytracing::GetScalarFieldArray(scalarField),
PointIds);
}
void GlyphIntersector::IntersectionData(Ray<vtkm::Float32>& rays,
const vtkm::cont::Field scalarField,
const vtkm::Range& scalarRange)
{
IntersectionDataImp(rays, scalarField, scalarRange);
}
void GlyphIntersector::IntersectionData(Ray<vtkm::Float64>& rays,
const vtkm::cont::Field scalarField,
const vtkm::Range& scalarRange)
{
IntersectionDataImp(rays, scalarField, scalarRange);
}
vtkm::Id GlyphIntersector::GetNumberOfShapes() const
{
return PointIds.GetNumberOfValues();
}
}
}
} //namespace vtkm::rendering::raytracing

@ -0,0 +1,69 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_rendering_raytracing_Glyph_Intersector_h
#define vtk_m_rendering_raytracing_Glyph_Intersector_h
#include <vtkm/rendering/GlyphType.h>
#include <vtkm/rendering/raytracing/ShapeIntersector.h>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
class GlyphIntersector : public ShapeIntersector
{
public:
GlyphIntersector(vtkm::rendering::GlyphType glyphType);
virtual ~GlyphIntersector() override;
void SetGlyphType(vtkm::rendering::GlyphType glyphType);
void SetData(const vtkm::cont::CoordinateSystem& coords,
vtkm::cont::ArrayHandle<vtkm::Id> pointIds,
vtkm::cont::ArrayHandle<vtkm::Float32> sizes);
void IntersectRays(Ray<vtkm::Float32>& rays, bool returnCellIndex = false) override;
void IntersectRays(Ray<vtkm::Float64>& rays, bool returnCellIndex = false) override;
template <typename Precision>
void IntersectRaysImp(Ray<Precision>& rays, bool returnCellIndex);
template <typename Precision>
void IntersectionDataImp(Ray<Precision>& rays,
const vtkm::cont::Field scalarField,
const vtkm::Range& scalarRange);
void IntersectionData(Ray<vtkm::Float32>& rays,
const vtkm::cont::Field scalarField,
const vtkm::Range& scalarRange) override;
void IntersectionData(Ray<vtkm::Float64>& rays,
const vtkm::cont::Field scalarField,
const vtkm::Range& scalarRange) override;
vtkm::Id GetNumberOfShapes() const override;
protected:
vtkm::cont::ArrayHandle<vtkm::Id> PointIds;
vtkm::cont::ArrayHandle<vtkm::Float32> Sizes;
vtkm::rendering::GlyphType GlyphType;
}; // class GlyphIntersector
}
}
} //namespace vtkm::rendering::raytracing
#endif //vtk_m_rendering_raytracing_Glyph_Intersector_h

@ -0,0 +1,721 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/VectorAnalysis.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/rendering/raytracing/BVHTraverser.h>
#include <vtkm/rendering/raytracing/GlyphIntersectorVector.h>
#include <vtkm/rendering/raytracing/RayOperations.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <vtkm/worklet/DispatcherMapTopology.h>
// This line is at the end to prevent warnings when building for CUDA
#include <vtkm/Swap.h>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
namespace detail
{
static constexpr vtkm::Float32 ARROW_BODY_SIZE = 0.75f;
class FindGlyphVectorAABBs : public vtkm::worklet::WorkletMapField
{
vtkm::rendering::GlyphType GlyphType;
vtkm::Float32 ArrowBodyRadius;
vtkm::Float32 ArrowHeadRadius;
public:
using ControlSignature = void(FieldIn,
FieldIn,
FieldOut,
FieldOut,
FieldOut,
FieldOut,
FieldOut,
FieldOut,
WholeArrayIn);
using ExecutionSignature = void(_1, _2, _3, _4, _5, _6, _7, _8, _9);
VTKM_CONT
FindGlyphVectorAABBs(vtkm::rendering::GlyphType glyphType,
vtkm::Float32 bodyRadius,
vtkm::Float32 headRadius)
: GlyphType(glyphType)
, ArrowBodyRadius(bodyRadius)
, ArrowHeadRadius(headRadius)
{
}
template <typename PointPortalType>
VTKM_EXEC void operator()(const vtkm::Id& pointId,
const vtkm::Vec3f_32& size,
vtkm::Float32& xmin,
vtkm::Float32& ymin,
vtkm::Float32& zmin,
vtkm::Float32& xmax,
vtkm::Float32& ymax,
vtkm::Float32& zmax,
const PointPortalType& points) const
{
vtkm::Vec3f_32 point = static_cast<vtkm::Vec3f_32>(points.Get(pointId));
xmin = point[0];
xmax = point[0];
ymin = point[1];
ymax = point[1];
zmin = point[2];
zmax = point[2];
if (this->GlyphType == vtkm::rendering::GlyphType::Arrow)
{
this->CalculateArrowAABB(point, size, xmin, ymin, zmin, xmax, ymax, zmax);
}
}
VTKM_EXEC inline void CalculateArrowAABB(const vtkm::Vec3f_32& point,
const vtkm::Vec3f_32& size,
vtkm::Float32& xmin,
vtkm::Float32& ymin,
vtkm::Float32& zmin,
vtkm::Float32& xmax,
vtkm::Float32& ymax,
vtkm::Float32& zmax) const
{
vtkm::Vec3f_32 body_pa = point;
vtkm::Vec3f_32 body_pb = body_pa + ARROW_BODY_SIZE * size;
vtkm::Vec3f_32 head_pa = body_pb;
vtkm::Vec3f_32 head_pb = point + size;
this->CylinderAABB(body_pa, body_pb, this->ArrowBodyRadius, xmin, ymin, zmin, xmax, ymax, zmax);
this->ConeAABB(
head_pa, head_pb, this->ArrowHeadRadius, 0.0f, xmin, ymin, zmin, xmax, ymax, zmax);
}
VTKM_EXEC inline void CylinderAABB(const vtkm::Vec3f_32& pa,
const vtkm::Vec3f_32& pb,
const vtkm::Float32& ra,
vtkm::Float32& xmin,
vtkm::Float32& ymin,
vtkm::Float32& zmin,
vtkm::Float32& xmax,
vtkm::Float32& ymax,
vtkm::Float32& zmax) const
{
vtkm::Vec3f_32 a = pb - pa;
vtkm::Vec3f_32 e_prime = a * a / vtkm::Dot(a, a);
vtkm::Vec3f_32 e = ra * vtkm::Sqrt(1.0f - e_prime);
vtkm::Vec3f_32 pa1 = pa - e;
vtkm::Vec3f_32 pa2 = pa + e;
vtkm::Vec3f_32 pb1 = pb - e;
vtkm::Vec3f_32 pb2 = pb + e;
xmin = vtkm::Min(xmin, vtkm::Min(pa1[0], pb1[0]));
ymin = vtkm::Min(ymin, vtkm::Min(pa1[1], pb1[1]));
zmin = vtkm::Min(zmin, vtkm::Min(pa1[2], pb1[2]));
xmax = vtkm::Max(xmax, vtkm::Max(pa2[0], pb2[0]));
ymax = vtkm::Max(ymax, vtkm::Max(pa2[1], pb2[1]));
zmax = vtkm::Max(zmax, vtkm::Max(pa2[2], pb2[2]));
}
VTKM_EXEC inline void ConeAABB(const vtkm::Vec3f_32& pa,
const vtkm::Vec3f_32& pb,
const vtkm::Float32& ra,
const vtkm::Float32& rb,
vtkm::Float32& xmin,
vtkm::Float32& ymin,
vtkm::Float32& zmin,
vtkm::Float32& xmax,
vtkm::Float32& ymax,
vtkm::Float32& zmax) const
{
vtkm::Vec3f_32 a = pb - pa;
vtkm::Vec3f_32 e_prime = a * a / vtkm::Dot(a, a);
vtkm::Vec3f_32 e = vtkm::Sqrt(1.0f - e_prime);
vtkm::Vec3f_32 pa1 = pa - e * ra;
vtkm::Vec3f_32 pa2 = pa + e * ra;
vtkm::Vec3f_32 pb1 = pb - e * rb;
vtkm::Vec3f_32 pb2 = pb + e * rb;
xmin = vtkm::Min(xmin, vtkm::Min(pa1[0], pb1[0]));
ymin = vtkm::Min(ymin, vtkm::Min(pa1[1], pb1[1]));
zmin = vtkm::Min(zmin, vtkm::Min(pa1[2], pb1[2]));
xmax = vtkm::Max(xmax, vtkm::Max(pa2[0], pb2[0]));
ymax = vtkm::Max(ymax, vtkm::Max(pa2[1], pb2[1]));
zmax = vtkm::Max(zmax, vtkm::Max(pa2[2], pb2[2]));
}
}; //class FindGlyphVectorAABBs
template <typename Device>
class GlyphVectorLeafIntersector
{
public:
using IdHandle = vtkm::cont::ArrayHandle<vtkm::Id>;
using IdArrayPortal = typename IdHandle::ReadPortalType;
using Vec3f_32Handle = vtkm::cont::ArrayHandle<vtkm::Vec3f_32>;
using Vec3f_32Portal = typename Vec3f_32Handle::ReadPortalType;
vtkm::rendering::GlyphType GlyphType;
IdArrayPortal PointIds;
Vec3f_32Portal Sizes;
vtkm::Float32 ArrowBodyRadius;
vtkm::Float32 ArrowHeadRadius;
GlyphVectorLeafIntersector() = default;
GlyphVectorLeafIntersector(vtkm::rendering::GlyphType glyphType,
const IdHandle& pointIds,
const Vec3f_32Handle& sizes,
vtkm::Float32 bodyRadius,
vtkm::Float32 headRadius,
vtkm::cont::Token& token)
: GlyphType(glyphType)
, PointIds(pointIds.PrepareForInput(Device(), token))
, Sizes(sizes.PrepareForInput(Device(), token))
, ArrowBodyRadius(bodyRadius)
, ArrowHeadRadius(headRadius)
{
}
template <typename PointPortalType, typename LeafPortalType, typename Precision>
VTKM_EXEC inline void IntersectLeaf(const vtkm::Int32& currentNode,
const vtkm::Vec<Precision, 3>& origin,
const vtkm::Vec<Precision, 3>& dir,
const PointPortalType& points,
vtkm::Id& hitIndex,
Precision& closestDistance,
Precision& minU,
Precision& minV,
LeafPortalType leafs,
const Precision& minDistance) const
{
const vtkm::Id glyphCount = leafs.Get(currentNode);
for (vtkm::Id i = 1; i <= glyphCount; ++i)
{
const vtkm::Id idx = leafs.Get(currentNode + i);
vtkm::Id pointIndex = PointIds.Get(idx);
vtkm::Vec<Precision, 3> size = Sizes.Get(idx);
vtkm::Vec<Precision, 3> point = vtkm::Vec<Precision, 3>(points.Get(pointIndex));
if (this->GlyphType == vtkm::rendering::GlyphType::Arrow)
{
this->IntersectArrow(
origin, dir, point, size, pointIndex, hitIndex, closestDistance, minU, minV, minDistance);
}
}
}
template <typename Precision>
VTKM_EXEC inline void IntersectArrow(const vtkm::Vec<Precision, 3>& origin,
const vtkm::Vec<Precision, 3>& dir,
const vtkm::Vec<Precision, 3>& point,
const vtkm::Vec<Precision, 3>& size,
const vtkm::Id& pointIndex,
vtkm::Id& hitIndex,
Precision& closestDistance,
Precision& minU,
Precision& minV,
const Precision& minDistance) const
{
using Vec2 = vtkm::Vec<Precision, 2>;
using Vec3 = vtkm::Vec<Precision, 3>;
using Vec4 = vtkm::Vec<Precision, 4>;
Vec3 body_pa = point;
Vec3 body_pb = body_pa + ARROW_BODY_SIZE * size;
Vec3 head_pa = body_pb;
Vec3 head_pb = point + size;
Vec4 bodyIntersection =
this->IntersectCylinder(origin, dir, body_pa, body_pb, Precision(this->ArrowBodyRadius));
Vec4 headIntersection = this->IntersectCone(
origin, dir, head_pa, head_pb, Precision(this->ArrowHeadRadius), Precision(0.0f));
bool bodyHit = bodyIntersection[0] >= minDistance;
bool headHit = headIntersection[0] >= minDistance;
if (bodyHit && !headHit)
{
Precision t = bodyIntersection[0];
if (t < closestDistance)
{
hitIndex = pointIndex;
closestDistance = t;
minU = bodyIntersection[1];
minV = bodyIntersection[2];
}
}
else if (!bodyHit && headHit)
{
Precision t = headIntersection[0];
if (t < closestDistance)
{
hitIndex = pointIndex;
closestDistance = t;
minU = headIntersection[1];
minV = headIntersection[2];
}
}
else if (bodyHit || headHit)
{
Precision t1 = bodyIntersection[0];
Precision t2 = headIntersection[0];
Precision t = t1;
Vec2 partialNormal = { bodyIntersection[1], bodyIntersection[2] };
if (t2 < t)
{
t = t2;
partialNormal[0] = headIntersection[1];
partialNormal[1] = headIntersection[2];
}
if (t < closestDistance)
{
hitIndex = pointIndex;
closestDistance = t;
minU = partialNormal[0];
minV = partialNormal[1];
}
}
}
template <typename Precision>
VTKM_EXEC vtkm::Vec4f_32 IntersectCylinder(const vtkm::Vec<Precision, 3>& ro,
const vtkm::Vec<Precision, 3>& rd,
const vtkm::Vec<Precision, 3>& pa,
const vtkm::Vec<Precision, 3>& pb,
const Precision& ra) const
{
using Vec3 = vtkm::Vec<Precision, 3>;
using Vec4 = vtkm::Vec<Precision, 4>;
const Vec4 NO_HIT{ -1.0f, -1.0f, -1.0f, -1.0f };
Vec3 cc = 0.5f * (pa + pb);
Precision ch = vtkm::Magnitude(pb - pa);
Vec3 ca = (pb - pa) / ch;
ch *= 0.5f;
Vec3 oc = ro - cc;
Precision card = vtkm::Dot(ca, rd);
Precision caoc = vtkm::Dot(ca, oc);
Precision a = 1.0f - card * card;
Precision b = vtkm::Dot(oc, rd) - caoc * card;
Precision c = vtkm::Dot(oc, oc) - caoc * caoc - ra * ra;
Precision h = b * b - a * c;
if (h < 0.0f)
{
return NO_HIT;
}
h = vtkm::Sqrt(h);
Precision t1 = (-b - h) / a;
/* Precision t2 = (-b + h) / a; // exit point */
Precision y = caoc + t1 * card;
// body
if (vtkm::Abs(y) < ch)
{
vtkm::Vec3f_32 normal = vtkm::Normal(oc + t1 * rd - ca * y);
return vtkm::Vec4f_32(static_cast<vtkm::Float32>(t1), normal[0], normal[1], normal[2]);
}
// bottom cap
Precision sy = -1;
Precision tp = (sy * ch - caoc) / card;
if (vtkm::Abs(b + a * tp) < h)
{
vtkm::Vec3f_32 normal = vtkm::Normal(ca * sy);
return vtkm::Vec4f_32(static_cast<vtkm::Float32>(tp), normal[0], normal[1], normal[2]);
}
// top cap
sy = 1;
tp = (sy * ch - caoc) / card;
if (vtkm::Abs(b + a * tp) < h)
{
vtkm::Vec3f_32 normal = vtkm::Normal(ca * sy);
return vtkm::Vec4f_32(static_cast<vtkm::Float32>(tp), normal[0], normal[1], normal[2]);
}
return NO_HIT;
}
template <typename Precision>
VTKM_EXEC vtkm::Vec4f_32 IntersectCone(const vtkm::Vec<Precision, 3>& ro,
const vtkm::Vec<Precision, 3>& rd,
const vtkm::Vec<Precision, 3>& pa,
const vtkm::Vec<Precision, 3>& pb,
const Precision& ra,
const Precision& rb) const
{
using Vec3 = vtkm::Vec<Precision, 3>;
using Vec4 = vtkm::Vec<Precision, 4>;
const Vec4 NO_HIT{ -1.0f, -1.0f, -1.0f, -1.0f };
Vec3 ba = pb - pa;
Vec3 oa = ro - pa;
Vec3 ob = ro - pb;
Precision m0 = vtkm::Dot(ba, ba);
Precision m1 = vtkm::Dot(oa, ba);
Precision m2 = vtkm::Dot(ob, ba);
Precision m3 = vtkm::Dot(rd, ba);
//caps
if (m1 < 0.0)
{
Vec3 m11 = oa * m3 - rd * m1;
Precision m12 = ra * ra * m3 * m3;
if (vtkm::Dot(m11, m11) < m12)
{
Precision t = -m1 / m3;
Vec3 normal = -ba * 1.0f / vtkm::Sqrt(m0);
return Vec4(t, normal[0], normal[1], normal[2]);
}
}
else if (m2 > 0.0)
{
Vec3 m21 = ob * m3 - rd * m2;
Precision m22 = rb * rb * m3 * m3;
if (vtkm::Dot(m21, m21) < m22)
{
Precision t = -m2 / m3;
Vec3 normal = ba * 1.0f / vtkm::Sqrt(m0);
return Vec4(t, normal[0], normal[1], normal[2]);
}
}
// body
Precision rr = ra - rb;
Precision hy = m0 + rr * rr;
Precision m4 = vtkm::Dot(rd, oa);
Precision m5 = vtkm::Dot(oa, oa);
Precision k2 = m0 * m0 - m3 * m3 * hy;
Precision k1 = m0 * m0 * m4 - m1 * m3 * hy + m0 * ra * (rr * m3 * 1.0f);
Precision k0 = m0 * m0 * m5 - m1 * m1 * hy + m0 * ra * (rr * m1 * 2.0f - m0 * ra);
Precision h = k1 * k1 - k2 * k0;
if (h < 0.0)
{
return NO_HIT;
}
Precision t = (-k1 - sqrt(h)) / k2;
Precision y = m1 + t * m3;
if (y > 0.0 && y < m0)
{
Vec3 normal = vtkm::Normal(m0 * (m0 * (oa + t * rd) + rr * ba * ra) - ba * hy * y);
return Vec4(t, normal[0], normal[1], normal[2]);
}
return NO_HIT;
}
};
class GlyphVectorLeafWrapper : public vtkm::cont::ExecutionObjectBase
{
protected:
using IdHandle = vtkm::cont::ArrayHandle<vtkm::Id>;
using Vec3f_32Handle = vtkm::cont::ArrayHandle<vtkm::Vec3f_32>;
vtkm::rendering::GlyphType GlyphType;
IdHandle PointIds;
Vec3f_32Handle Sizes;
vtkm::Float32 ArrowBodyRadius;
vtkm::Float32 ArrowHeadRadius;
public:
GlyphVectorLeafWrapper(vtkm::rendering::GlyphType glyphType,
IdHandle& pointIds,
Vec3f_32Handle& sizes,
vtkm::Float32 bodyRadius,
vtkm::Float32 headRadius)
: GlyphType(glyphType)
, PointIds(pointIds)
, Sizes(sizes)
, ArrowBodyRadius(bodyRadius)
, ArrowHeadRadius(headRadius)
{
}
template <typename Device>
VTKM_CONT GlyphVectorLeafIntersector<Device> PrepareForExecution(Device,
vtkm::cont::Token& token) const
{
return GlyphVectorLeafIntersector<Device>(this->GlyphType,
this->PointIds,
this->Sizes,
this->ArrowBodyRadius,
this->ArrowHeadRadius,
token);
}
};
class CalculateGlyphVectorNormals : public vtkm::worklet::WorkletMapField
{
public:
VTKM_CONT
CalculateGlyphVectorNormals(vtkm::rendering::GlyphType glyphType)
: GlyphType(glyphType)
{
}
typedef void ControlSignature(FieldIn,
FieldIn,
FieldIn,
FieldIn,
FieldIn,
FieldOut,
FieldOut,
FieldOut,
WholeArrayIn,
WholeArrayIn,
WholeArrayIn);
typedef void ExecutionSignature(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11);
template <typename Precision,
typename PointPortalType,
typename IndicesPortalType,
typename SizesPortalType>
VTKM_EXEC inline void operator()(const vtkm::Id& hitIndex,
const vtkm::Vec<Precision, 3>& rayDir,
const vtkm::Vec<Precision, 3>& intersection,
const Precision& u,
const Precision& v,
Precision& normalX,
Precision& normalY,
Precision& normalZ,
const PointPortalType& vtkmNotUsed(points),
const IndicesPortalType& vtkmNotUsed(indicesPortal),
const SizesPortalType& vtkmNotUsed(sizesPortal)) const
{
if (hitIndex < 0)
return;
if (this->GlyphType == vtkm::rendering::GlyphType::Arrow)
{
this->CalculateArrowNormal(rayDir, intersection, u, v, normalX, normalY, normalZ);
}
}
template <typename Precision>
VTKM_EXEC inline void CalculateArrowNormal(
const vtkm::Vec<Precision, 3>& rayDir,
const vtkm::Vec<Precision, 3>& vtkmNotUsed(intersection),
const Precision& u,
const Precision& v,
Precision& normalX,
Precision& normalY,
Precision& normalZ) const
{
vtkm::Vec<Precision, 3> normal;
normal[0] = u;
normal[1] = v;
normal[2] = 1.0f - (normalX * normalX) - (normalY * normalY);
if (vtkm::Dot(normal, rayDir) > 0.0f)
{
normal = -normal;
}
normalX = normal[0];
normalY = normal[1];
normalZ = normal[2];
}
vtkm::rendering::GlyphType GlyphType;
}; //class CalculateGlyphVectorNormals
template <typename Precision>
class GetScalars : public vtkm::worklet::WorkletMapField
{
private:
Precision MinScalar;
Precision InvDeltaScalar;
bool Normalize;
public:
VTKM_CONT
GetScalars(const vtkm::Float32& minScalar, const vtkm::Float32& maxScalar)
: MinScalar(minScalar)
{
Normalize = true;
if (minScalar >= maxScalar)
{
// support the scalar renderer
Normalize = false;
this->InvDeltaScalar = Precision(0.f);
}
else
{
//Make sure the we don't divide by zero on
//something like an iso-surface
this->InvDeltaScalar = 1.f / (maxScalar - this->MinScalar);
}
}
typedef void ControlSignature(FieldIn, FieldOut, WholeArrayIn, WholeArrayIn);
typedef void ExecutionSignature(_1, _2, _3, _4);
template <typename FieldPortalType, typename IndicesPortalType>
VTKM_EXEC void operator()(const vtkm::Id& hitIndex,
Precision& scalar,
const FieldPortalType& scalars,
const IndicesPortalType& indicesPortal) const
{
if (hitIndex < 0)
return;
vtkm::Id pointId = indicesPortal.Get(hitIndex);
scalar = Precision(scalars.Get(pointId));
if (Normalize)
{
scalar = (scalar - this->MinScalar) * this->InvDeltaScalar;
}
}
}; //class GetScalar
} // namespace
GlyphIntersectorVector::GlyphIntersectorVector(vtkm::rendering::GlyphType glyphType)
: ShapeIntersector()
, ArrowBodyRadius(0.004f)
, ArrowHeadRadius(0.008f)
{
this->SetGlyphType(glyphType);
}
GlyphIntersectorVector::~GlyphIntersectorVector() {}
void GlyphIntersectorVector::SetGlyphType(vtkm::rendering::GlyphType glyphType)
{
this->GlyphType = glyphType;
}
void GlyphIntersectorVector::SetData(const vtkm::cont::CoordinateSystem& coords,
vtkm::cont::ArrayHandle<vtkm::Id> pointIds,
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> sizes)
{
this->PointIds = pointIds;
this->Sizes = sizes;
this->CoordsHandle = coords;
AABBs AABB;
vtkm::cont::Invoker invoker;
invoker(
detail::FindGlyphVectorAABBs{ this->GlyphType, this->ArrowBodyRadius, this->ArrowHeadRadius },
PointIds,
Sizes,
AABB.xmins,
AABB.ymins,
AABB.zmins,
AABB.xmaxs,
AABB.ymaxs,
AABB.zmaxs,
CoordsHandle);
this->SetAABBs(AABB);
}
void GlyphIntersectorVector::IntersectRays(Ray<vtkm::Float32>& rays, bool returnCellIndex)
{
IntersectRaysImp(rays, returnCellIndex);
}
void GlyphIntersectorVector::IntersectRays(Ray<vtkm::Float64>& rays, bool returnCellIndex)
{
IntersectRaysImp(rays, returnCellIndex);
}
template <typename Precision>
void GlyphIntersectorVector::IntersectRaysImp(Ray<Precision>& rays,
bool vtkmNotUsed(returnCellIndex))
{
detail::GlyphVectorLeafWrapper leafIntersector(
this->GlyphType, this->PointIds, this->Sizes, this->ArrowBodyRadius, this->ArrowHeadRadius);
BVHTraverser traverser;
traverser.IntersectRays(rays, this->BVH, leafIntersector, this->CoordsHandle);
RayOperations::UpdateRayStatus(rays);
}
template <typename Precision>
void GlyphIntersectorVector::IntersectionDataImp(Ray<Precision>& rays,
const vtkm::cont::Field field,
const vtkm::Range& range)
{
ShapeIntersector::IntersectionPoint(rays);
const bool isSupportedField = field.IsFieldCell() || field.IsFieldPoint();
if (!isSupportedField)
{
throw vtkm::cont::ErrorBadValue(
"GlyphIntersectorVector: Field not accociated with a cell set or field");
}
vtkm::worklet::DispatcherMapField<detail::CalculateGlyphVectorNormals>(
detail::CalculateGlyphVectorNormals(this->GlyphType))
.Invoke(rays.HitIdx,
rays.Dir,
rays.Intersection,
rays.U,
rays.V,
rays.NormalX,
rays.NormalY,
rays.NormalZ,
CoordsHandle,
PointIds,
Sizes);
vtkm::worklet::DispatcherMapField<detail::GetScalars<Precision>>(
detail::GetScalars<Precision>(vtkm::Float32(range.Min), vtkm::Float32(range.Max)))
.Invoke(
rays.HitIdx, rays.Scalar, vtkm::rendering::raytracing::GetScalarFieldArray(field), PointIds);
}
void GlyphIntersectorVector::IntersectionData(Ray<vtkm::Float32>& rays,
const vtkm::cont::Field field,
const vtkm::Range& range)
{
IntersectionDataImp(rays, field, range);
}
void GlyphIntersectorVector::IntersectionData(Ray<vtkm::Float64>& rays,
const vtkm::cont::Field field,
const vtkm::Range& range)
{
IntersectionDataImp(rays, field, range);
}
vtkm::Id GlyphIntersectorVector::GetNumberOfShapes() const
{
return PointIds.GetNumberOfValues();
}
void GlyphIntersectorVector::SetArrowRadii(vtkm::Float32 bodyRadius, vtkm::Float32 headRadius)
{
this->ArrowHeadRadius = headRadius;
this->ArrowBodyRadius = bodyRadius;
}
}
}
} //namespace vtkm::rendering::raytracing

@ -0,0 +1,74 @@
//============================================================================
// 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.
//============================================================================
#ifndef vtk_m_rendering_raytracing_Glyph_Intersector_Vector_h
#define vtk_m_rendering_raytracing_Glyph_Intersector_Vector_h
#include <vtkm/rendering/GlyphType.h>
#include <vtkm/rendering/raytracing/ShapeIntersector.h>
namespace vtkm
{
namespace rendering
{
namespace raytracing
{
class GlyphIntersectorVector : public ShapeIntersector
{
public:
GlyphIntersectorVector(vtkm::rendering::GlyphType glyphType);
virtual ~GlyphIntersectorVector() override;
void SetGlyphType(vtkm::rendering::GlyphType glyphType);
void SetData(const vtkm::cont::CoordinateSystem& coords,
vtkm::cont::ArrayHandle<vtkm::Id> pointIds,
vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::Float32, 3>> sizes);
void IntersectRays(Ray<vtkm::Float32>& rays, bool returnCellIndex = false) override;
void IntersectRays(Ray<vtkm::Float64>& rays, bool returnCellIndex = false) override;
template <typename Precision>
void IntersectRaysImp(Ray<Precision>& rays, bool returnCellIndex);
template <typename Precision>
void IntersectionDataImp(Ray<Precision>& rays,
const vtkm::cont::Field field,
const vtkm::Range& range);
void IntersectionData(Ray<vtkm::Float32>& rays,
const vtkm::cont::Field field,
const vtkm::Range& range) override;
void IntersectionData(Ray<vtkm::Float64>& rays,
const vtkm::cont::Field field,
const vtkm::Range& range) override;
vtkm::Id GetNumberOfShapes() const override;
void SetArrowRadii(vtkm::Float32 bodyRadius, vtkm::Float32 headRadius);
protected:
vtkm::cont::ArrayHandle<vtkm::Id> PointIds;
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> Sizes;
vtkm::cont::ArrayHandle<vtkm::Vec3f_32> Normals;
vtkm::rendering::GlyphType GlyphType;
vtkm::Float32 ArrowBodyRadius;
vtkm::Float32 ArrowHeadRadius;
}; // class GlyphIntersectorVector
}
}
} //namespace vtkm::rendering::raytracing
#endif //vtk_m_rendering_raytracing_Glyph_Intersector_Vector_h

@ -144,6 +144,12 @@ GetScalarFieldArray(const vtkm::cont::Field& field)
{
return field.GetData().ResetTypes(ScalarRenderingTypes{}, VTKM_DEFAULT_STORAGE_LIST{});
}
VTKM_CONT inline vtkm::cont::UncertainArrayHandle<Vec3RenderingTypes, VTKM_DEFAULT_STORAGE_LIST>
GetVec3FieldArray(const vtkm::cont::Field& field)
{
return field.GetData().ResetTypes(Vec3RenderingTypes{}, VTKM_DEFAULT_STORAGE_LIST{});
}
}
}
} //namespace vtkm::rendering::raytracing

@ -26,6 +26,8 @@ set(unit_tests
UnitTestMapperWireframer.cxx
UnitTestMapperVolume.cxx
UnitTestScalarRenderer.cxx
UnitTestMapperGlyphScalar.cxx
UnitTestMapperGlyphVector.cxx
)
set(library_sources

@ -14,6 +14,8 @@
#include <vtkm/rendering/CanvasRayTracer.h>
#include <vtkm/rendering/MapperConnectivity.h>
#include <vtkm/rendering/MapperCylinder.h>
#include <vtkm/rendering/MapperGlyphScalar.h>
#include <vtkm/rendering/MapperGlyphVector.h>
#include <vtkm/rendering/MapperPoint.h>
#include <vtkm/rendering/MapperQuad.h>
#include <vtkm/rendering/MapperRayTracer.h>
@ -116,6 +118,38 @@ void SetupMapper(vtkm::rendering::MapperPoint& mapper,
}
}
void SetupMapper(vtkm::rendering::MapperGlyphScalar& mapper,
const vtkm::rendering::testing::RenderTestOptions& options)
{
mapper.SetGlyphType(options.GlyphType);
mapper.SetScaleByValue(options.UseVariableRadius);
if (options.Radius >= 0)
{
mapper.SetBaseSize(options.Radius);
}
mapper.SetScaleDelta(0.5);
if (options.RenderCells)
{
mapper.SetUseCells();
}
}
void SetupMapper(vtkm::rendering::MapperGlyphVector& mapper,
const vtkm::rendering::testing::RenderTestOptions& options)
{
mapper.SetGlyphType(options.GlyphType);
mapper.SetScaleByValue(options.UseVariableRadius);
if (options.Radius >= 0)
{
mapper.SetBaseSize(options.Radius);
}
mapper.SetScaleDelta(0.5);
if (options.RenderCells)
{
mapper.SetUseCells();
}
}
template <typename MapperType>
std::unique_ptr<vtkm::rendering::Mapper> MakeMapper(
const vtkm::rendering::testing::RenderTestOptions& options)
@ -222,6 +256,12 @@ void DoRenderTest(vtkm::rendering::CanvasRayTracer& canvas,
case vtkm::rendering::testing::MapperType::Wireframer:
mapper = MakeMapper<vtkm::rendering::MapperWireframer>(options);
break;
case vtkm::rendering::testing::MapperType::GlyphScalar:
mapper = MakeMapper<vtkm::rendering::MapperGlyphScalar>(options);
break;
case vtkm::rendering::testing::MapperType::GlyphVector:
mapper = MakeMapper<vtkm::rendering::MapperGlyphVector>(options);
break;
}
DoRenderTest(canvas, *mapper, dataSetsFields, outputFile, options);
}

@ -19,6 +19,7 @@
#include <vtkm/rendering/Camera.h>
#include <vtkm/rendering/Canvas.h>
#include <vtkm/rendering/Color.h>
#include <vtkm/rendering/GlyphType.h>
#include <vtkm/rendering/Mapper.h>
#include <vtkm/rendering/Scene.h>
#include <vtkm/rendering/TextAnnotationScreen.h>
@ -49,7 +50,9 @@ enum struct MapperType
Point,
Quad,
Volume,
Wireframer
Wireframer,
GlyphScalar,
GlyphVector
};
struct RenderTestOptions
@ -102,7 +105,8 @@ struct RenderTestOptions
// use the best available device for rendering.
bool AllowAnyDevice = true;
// Special options for some glyph-like mappers
// Special options for some glyph and glyph-like mappers
vtkm::rendering::GlyphType GlyphType = vtkm::rendering::GlyphType::Cube;
bool UseVariableRadius = false;
vtkm::Float32 Radius = -1.0f;
vtkm::Float32 RadiusDelta = 0.5f;

@ -0,0 +1,81 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/testing/MakeTestDataSet.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/rendering/Actor.h>
#include <vtkm/rendering/CanvasRayTracer.h>
#include <vtkm/rendering/GlyphType.h>
#include <vtkm/rendering/Scene.h>
#include <vtkm/rendering/View3D.h>
#include <vtkm/rendering/testing/RenderTest.h>
namespace
{
void RenderTests()
{
vtkm::cont::testing::MakeTestDataSet maker;
vtkm::rendering::testing::RenderTestOptions options;
options.Mapper = vtkm::rendering::testing::MapperType::GlyphScalar;
options.AllowAnyDevice = false;
options.ColorTable = vtkm::cont::ColorTable::Preset::Inferno;
options.GlyphType = vtkm::rendering::GlyphType::Cube;
vtkm::rendering::testing::RenderTest(
maker.Make3DUniformDataSet1(), "pointvar", "rendering/glyph_scalar/regular3D.png", options);
options.UseVariableRadius = true;
options.RadiusDelta = 4.0f;
options.Radius = 0.25f;
vtkm::rendering::testing::RenderTest(maker.Make3DUniformDataSet1(),
"pointvar",
"rendering/glyph_scalar/variable_regular3D.png",
options);
options.GlyphType = vtkm::rendering::GlyphType::Sphere;
vtkm::rendering::testing::RenderTest(maker.Make3DUniformDataSet3({ 7 }),
"pointvar",
"rendering/glyph_scalar/variable_spheres_regular3D.png",
options);
options.GlyphType = vtkm::rendering::GlyphType::Axes;
vtkm::rendering::testing::RenderTest(maker.Make3DUniformDataSet3({ 7 }),
"pointvar",
"rendering/glyph_scalar/variable_axes_regular3D.png",
options);
options.GlyphType = vtkm::rendering::GlyphType::Quad;
options.Radius = 5.0f;
options.RadiusDelta = 0.75f;
vtkm::rendering::testing::RenderTest(maker.Make3DUniformDataSet3({ 7 }),
"pointvar",
"rendering/glyph_scalar/variable_quads_regular3D.png",
options);
// restore defaults
options.RadiusDelta = 0.5f;
options.UseVariableRadius = false;
options.GlyphType = vtkm::rendering::GlyphType::Cube;
options.RenderCells = true;
options.Radius = 1.f;
vtkm::rendering::testing::RenderTest(
maker.Make3DExplicitDataSet7(), "cellvar", "rendering/glyph_scalar/cells.png", options);
}
} //namespace
int UnitTestMapperGlyphScalar(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv);
}

@ -0,0 +1,49 @@
//============================================================================
// 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.
//============================================================================
#include <vtkm/cont/DeviceAdapter.h>
#include <vtkm/cont/testing/MakeTestDataSet.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/rendering/Actor.h>
#include <vtkm/rendering/CanvasRayTracer.h>
#include <vtkm/rendering/GlyphType.h>
#include <vtkm/rendering/MapperPoint.h>
#include <vtkm/rendering/Scene.h>
#include <vtkm/rendering/View3D.h>
#include <vtkm/rendering/testing/RenderTest.h>
namespace
{
void RenderTests()
{
vtkm::cont::testing::MakeTestDataSet maker;
vtkm::rendering::testing::RenderTestOptions options;
options.Mapper = vtkm::rendering::testing::MapperType::GlyphVector;
options.AllowAnyDevice = false;
options.ColorTable = vtkm::cont::ColorTable::Preset::Inferno;
options.GlyphType = vtkm::rendering::GlyphType::Arrow;
options.UseVariableRadius = true;
options.RadiusDelta = 4.0f;
options.Radius = 0.02f;
vtkm::rendering::testing::RenderTest(maker.Make3DExplicitDataSetCowNose(),
"point_vectors",
"rendering/glyph_vector/points_arrows_cownose.png",
options);
}
} //namespace
int UnitTestMapperGlyphVector(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(RenderTests, argc, argv);
}