Update CrossProduct filter to new filter base class

This also provides a good opportunity to demonstrate and test the
CastAndCallVecField method of the new filter classes.
This commit is contained in:
Kenneth Moreland 2022-01-24 12:03:35 -07:00
parent 72065024aa
commit 32507a5109
13 changed files with 249 additions and 346 deletions

@ -23,6 +23,7 @@ vtkm_add_instantiations(ClipWithImplicitFunctionInstantiations
set(deprecated_headers
CleanGrid.h
CrossProduct.h
DotProduct.h
Entropy.h
ExternalFaces.h
@ -87,7 +88,6 @@ set(extra_headers
ComputeMoments.h
CoordinateSystemTransform.h
CreateResult.h
CrossProduct.h
FieldSelection.h
FieldToColors.h
GhostCellClassify.h
@ -133,7 +133,6 @@ set(extra_header_template_sources
ClipWithImplicitFunctionExternInstantiations.h
ComputeMoments.hxx
CoordinateSystemTransform.hxx
CrossProduct.hxx
FieldToColors.hxx
GhostCellClassify.hxx
ImageConnectivity.hxx

@ -11,133 +11,31 @@
#ifndef vtk_m_filter_CrossProduct_h
#define vtk_m_filter_CrossProduct_h
#include <vtkm/filter/FilterField.h>
#include <vtkm/worklet/CrossProduct.h>
#include <vtkm/Deprecated.h>
#include <vtkm/filter/vector_calculus/CrossProduct.h>
namespace vtkm
{
namespace filter
{
class CrossProduct : public vtkm::filter::FilterField<CrossProduct>
VTKM_DEPRECATED(
1.8,
"Use vtkm/filter/vector_calculus/CrossProduct.h instead of vtkm/filter/CrossProduct.h.")
inline void CrossProduct_deprecated() {}
inline void CrossProduct_deprecated_warning()
{
public:
//CrossProduct filter only works on vec3 data.
using SupportedTypes = vtkm::TypeListFieldVec3;
CrossProduct_deprecated();
}
VTKM_CONT
CrossProduct();
//@{
/// Choose the primary field to operate on. In the cross product operation A x B, A is
/// the primary field.
VTKM_CONT
void SetPrimaryField(
const std::string& name,
vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY)
{
this->SetActiveField(name, association);
}
VTKM_CONT const std::string& GetPrimaryFieldName() const { return this->GetActiveFieldName(); }
VTKM_CONT vtkm::cont::Field::Association GetPrimaryFieldAssociation() const
{
return this->GetActiveFieldAssociation();
}
//@}
//@{
/// When set to true, uses a coordinate system as the primary field instead of the one selected
/// by name. Use SetPrimaryCoordinateSystem to select which coordinate system.
VTKM_CONT
void SetUseCoordinateSystemAsPrimaryField(bool flag)
{
this->SetUseCoordinateSystemAsField(flag);
}
VTKM_CONT
bool GetUseCoordinateSystemAsPrimaryField() const
{
return this->GetUseCoordinateSystemAsField();
}
//@}
//@{
/// Select the coordinate system index to use as the primary field. This only has an effect when
/// UseCoordinateSystemAsPrimaryField is true.
VTKM_CONT
void SetPrimaryCoordinateSystem(vtkm::Id index) { this->SetActiveCoordinateSystem(index); }
VTKM_CONT
vtkm::Id GetPrimaryCoordinateSystemIndex() const
{
return this->GetActiveCoordinateSystemIndex();
}
//@}
//@{
/// Choose the secondary field to operate on. In the cross product operation A x B, B is
/// the secondary field.
VTKM_CONT
void SetSecondaryField(
const std::string& name,
vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY)
{
this->SecondaryFieldName = name;
this->SecondaryFieldAssociation = association;
}
VTKM_CONT const std::string& GetSecondaryFieldName() const { return this->SecondaryFieldName; }
VTKM_CONT vtkm::cont::Field::Association GetSecondaryFieldAssociation() const
{
return this->SecondaryFieldAssociation;
}
//@}
//@{
/// When set to true, uses a coordinate system as the primary field instead of the one selected
/// by name. Use SetPrimaryCoordinateSystem to select which coordinate system.
VTKM_CONT
void SetUseCoordinateSystemAsSecondaryField(bool flag)
{
this->UseCoordinateSystemAsSecondaryField = flag;
}
VTKM_CONT
bool GetUseCoordinateSystemAsSecondaryField() const
{
return this->UseCoordinateSystemAsSecondaryField;
}
//@}
//@{
/// Select the coordinate system index to use as the primary field. This only has an effect when
/// UseCoordinateSystemAsPrimaryField is true.
VTKM_CONT
void SetSecondaryCoordinateSystem(vtkm::Id index)
{
this->SecondaryCoordinateSystemIndex = index;
}
VTKM_CONT
vtkm::Id GetSecondaryCoordinateSystemIndex() const
{
return this->SecondaryCoordinateSystemIndex;
}
//@}
template <typename T, typename StorageType, typename DerivedPolicy>
VTKM_CONT vtkm::cont::DataSet DoExecute(
const vtkm::cont::DataSet& input,
const vtkm::cont::ArrayHandle<vtkm::Vec<T, 3>, StorageType>& field,
const vtkm::filter::FieldMetadata& fieldMeta,
vtkm::filter::PolicyBase<DerivedPolicy> policy);
private:
std::string SecondaryFieldName;
vtkm::cont::Field::Association SecondaryFieldAssociation;
bool UseCoordinateSystemAsSecondaryField;
vtkm::Id SecondaryCoordinateSystemIndex;
class VTKM_DEPRECATED(1.8, "Use vtkm::filter::vector_calculus::CrossProduct.") CrossProduct
: public vtkm::filter::vector_calculus::CrossProduct
{
using vector_calculus::CrossProduct::CrossProduct;
};
}
} // namespace vtkm::filter
#include <vtkm/filter/CrossProduct.hxx>
#endif // vtk_m_filter_CrossProduct_h

@ -1,62 +0,0 @@
//============================================================================
// 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_filter_CrossProduct_hxx
#define vtk_m_filter_CrossProduct_hxx
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/worklet/DispatcherMapField.h>
namespace vtkm
{
namespace filter
{
//-----------------------------------------------------------------------------
inline VTKM_CONT CrossProduct::CrossProduct()
: vtkm::filter::FilterField<CrossProduct>()
, SecondaryFieldName()
, SecondaryFieldAssociation(vtkm::cont::Field::Association::ANY)
, UseCoordinateSystemAsSecondaryField(false)
, SecondaryCoordinateSystemIndex(0)
{
this->SetOutputFieldName("crossproduct");
}
//-----------------------------------------------------------------------------
template <typename T, typename StorageType, typename DerivedPolicy>
inline VTKM_CONT vtkm::cont::DataSet CrossProduct::DoExecute(
const vtkm::cont::DataSet& inDataSet,
const vtkm::cont::ArrayHandle<vtkm::Vec<T, 3>, StorageType>& primary,
const vtkm::filter::FieldMetadata& fieldMetadata,
vtkm::filter::PolicyBase<DerivedPolicy> policy)
{
vtkm::cont::Field secondaryField;
if (this->UseCoordinateSystemAsSecondaryField)
{
secondaryField = inDataSet.GetCoordinateSystem(this->GetSecondaryCoordinateSystemIndex());
}
else
{
secondaryField = inDataSet.GetField(this->SecondaryFieldName, this->SecondaryFieldAssociation);
}
auto secondary =
vtkm::filter::ApplyPolicyFieldOfType<vtkm::Vec<T, 3>>(secondaryField, policy, *this);
vtkm::cont::ArrayHandle<vtkm::Vec<T, 3>> output;
this->Invoke(vtkm::worklet::CrossProduct{}, primary, secondary, output);
return CreateResult(inDataSet, output, this->GetOutputFieldName(), fieldMetadata);
}
}
} // namespace vtkm::filter
#endif

@ -24,7 +24,6 @@ set(unit_tests
UnitTestContourTreeUniformAugmentedFilter.cxx
UnitTestContourTreeUniformDistributedFilter.cxx
UnitTestCoordinateSystemTransform.cxx
UnitTestCrossProductFilter.cxx
UnitTestFieldMetadata.cxx
UnitTestFieldSelection.cxx
UnitTestFieldToColors.cxx

@ -8,9 +8,13 @@
## PURPOSE. See the above copyright notice for more information.
##============================================================================
set(vector_calculus_headers
DotProduct.h)
CrossProduct.h
DotProduct.h
)
set(vector_calculus_sources_device
DotProduct.cxx)
CrossProduct.cxx
DotProduct.cxx
)
vtkm_library(
NAME vtkm_filter_vector_calculus

@ -0,0 +1,91 @@
//============================================================================
// 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/filter/vector_calculus/CrossProduct.h>
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/VectorAnalysis.h>
namespace
{
class CrossProductWorklet : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldIn, FieldOut);
template <typename T>
VTKM_EXEC void operator()(const vtkm::Vec<T, 3>& vec1,
const vtkm::Vec<T, 3>& vec2,
vtkm::Vec<T, 3>& outVec) const
{
outVec = vtkm::Cross(vec1, vec2);
}
};
} // anonymous namespace
namespace vtkm
{
namespace filter
{
namespace vector_calculus
{
//-----------------------------------------------------------------------------
VTKM_CONT CrossProduct::CrossProduct()
{
this->SetOutputFieldName("crossproduct");
}
//-----------------------------------------------------------------------------
VTKM_CONT vtkm::cont::DataSet CrossProduct::DoExecute(const vtkm::cont::DataSet& inDataSet)
{
vtkm::cont::Field primaryField = this->GetFieldFromDataSet(0, inDataSet);
vtkm::cont::UnknownArrayHandle primaryArray = primaryField.GetData();
vtkm::cont::UnknownArrayHandle outArray;
// We are using a C++14 auto lambda here. The advantage over a Functor is obvious, we don't
// need to explicitly pass filter, input/output DataSets etc. thus reduce the impact to
// the legacy code. The lambda can also access the private part of the filter thus reducing
// filter's public interface profile. CastAndCall tries to cast primaryArray of unknown value
// type and storage to a concrete ArrayHandle<T, S> with T from the `TypeList` and S from
// `StorageList`. It then passes the concrete array to the lambda as the first argument.
// We can later recover the concrete ValueType, T, from the concrete array.
auto resolveType = [&, this](const auto& concrete) {
// use std::decay to remove const ref from the decltype of concrete.
using T = typename std::decay_t<decltype(concrete)>::ValueType;
const auto& secondaryField = this->GetFieldFromDataSet(1, inDataSet);
vtkm::cont::ArrayHandle<T> secondaryArray;
vtkm::cont::ArrayCopyShallowIfPossible(secondaryField.GetData(), secondaryArray);
vtkm::cont::ArrayHandle<T> result;
this->Invoke(CrossProductWorklet{}, concrete, secondaryArray, result);
outArray = result;
};
this->CastAndCallVecField<3>(primaryArray, resolveType);
vtkm::cont::DataSet outDataSet;
outDataSet.CopyStructure(inDataSet);
outDataSet.AddField({ this->GetOutputFieldName(), primaryField.GetAssociation(), outArray });
this->MapFieldsOntoOutput(inDataSet, outDataSet);
return outDataSet;
}
}
}
} // namespace vtkm::filter::vector_calculus

@ -0,0 +1,129 @@
//============================================================================
// 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_filter_vector_calculus_CrossProduct_h
#define vtk_m_filter_vector_calculus_CrossProduct_h
#include <vtkm/filter/NewFilterField.h>
#include <vtkm/filter/vector_calculus/vtkm_filter_vector_calculus_export.h>
namespace vtkm
{
namespace filter
{
namespace vector_calculus
{
class VTKM_FILTER_VECTOR_CALCULUS_EXPORT CrossProduct : public vtkm::filter::NewFilterField
{
public:
VTKM_CONT
CrossProduct();
//@{
/// Choose the primary field to operate on. In the cross product operation A x B, A is
/// the primary field.
VTKM_CONT
void SetPrimaryField(
const std::string& name,
vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY)
{
this->SetActiveField(name, association);
}
VTKM_CONT const std::string& GetPrimaryFieldName() const { return this->GetActiveFieldName(); }
VTKM_CONT vtkm::cont::Field::Association GetPrimaryFieldAssociation() const
{
return this->GetActiveFieldAssociation();
}
//@}
//@{
/// When set to true, uses a coordinate system as the primary field instead of the one selected
/// by name. Use SetPrimaryCoordinateSystem to select which coordinate system.
VTKM_CONT
void SetUseCoordinateSystemAsPrimaryField(bool flag)
{
this->SetUseCoordinateSystemAsField(flag);
}
VTKM_CONT
bool GetUseCoordinateSystemAsPrimaryField() const
{
return this->GetUseCoordinateSystemAsField();
}
//@}
//@{
/// Select the coordinate system index to use as the primary field. This only has an effect when
/// UseCoordinateSystemAsPrimaryField is true.
VTKM_CONT
void SetPrimaryCoordinateSystem(vtkm::Id index) { this->SetActiveCoordinateSystem(index); }
VTKM_CONT
vtkm::Id GetPrimaryCoordinateSystemIndex() const
{
return this->GetActiveCoordinateSystemIndex();
}
//@}
//@{
/// Choose the secondary field to operate on. In the dot product operation A . B, B is
/// the secondary field.
VTKM_CONT
void SetSecondaryField(
const std::string& name,
vtkm::cont::Field::Association association = vtkm::cont::Field::Association::ANY)
{
this->SetActiveField(1, name, association);
}
VTKM_CONT const std::string& GetSecondaryFieldName() const { return this->GetActiveFieldName(1); }
VTKM_CONT vtkm::cont::Field::Association GetSecondaryFieldAssociation() const
{
return this->GetActiveFieldAssociation(1);
}
//@}
//@{
/// When set to true, uses a coordinate system as the secondary field instead of the one selected
/// by name. Use SetSecondaryCoordinateSystem to select which coordinate system.
VTKM_CONT
void SetUseCoordinateSystemAsSecondaryField(bool flag)
{
this->SetUseCoordinateSystemAsField(1, flag);
}
VTKM_CONT
bool GetUseCoordinateSystemAsSecondaryField() const
{
return this->GetUseCoordinateSystemAsField(1);
}
//@}
//@{
/// Select the coordinate system index to use as the secondary field. This only has an effect when
/// UseCoordinateSystemAsSecondaryField is true.
VTKM_CONT
void SetSecondaryCoordinateSystem(vtkm::Id index) { this->SetActiveCoordinateSystem(1, index); }
VTKM_CONT
vtkm::Id GetSecondaryCoordinateSystemIndex() const
{
return this->GetActiveCoordinateSystemIndex(1);
}
//@}
private:
VTKM_CONT vtkm::cont::DataSet DoExecute(const vtkm::cont::DataSet& input) override;
};
}
}
} // namespace vtkm::filter::vector_calculus
#endif // vtk_m_filter_vector_calculus_CrossProduct_h

@ -9,6 +9,7 @@
##============================================================================
set(unit_tests
UnitTestCrossProductFilter.cxx
UnitTestDotProductFilter.cxx
)

@ -10,7 +10,9 @@
#include <vtkm/cont/testing/MakeTestDataSet.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/filter/CrossProduct.h>
#include <vtkm/filter/vector_calculus/CrossProduct.h>
#include <vtkm/VectorAnalysis.h>
#include <random>
#include <vector>
@ -139,7 +141,7 @@ void TestCrossProduct()
{
std::cout << " Both vectors as normal fields" << std::endl;
vtkm::filter::CrossProduct filter;
vtkm::filter::vector_calculus::CrossProduct filter;
filter.SetPrimaryField("vec1");
filter.SetSecondaryField("vec2", vtkm::cont::Field::Association::POINTS);
@ -163,7 +165,7 @@ void TestCrossProduct()
{
std::cout << " First field as coordinates" << std::endl;
vtkm::filter::CrossProduct filter;
vtkm::filter::vector_calculus::CrossProduct filter;
filter.SetUseCoordinateSystemAsPrimaryField(true);
filter.SetPrimaryCoordinateSystem(1);
filter.SetSecondaryField("vec2");
@ -184,7 +186,7 @@ void TestCrossProduct()
{
std::cout << " Second field as coordinates" << std::endl;
vtkm::filter::CrossProduct filter;
vtkm::filter::vector_calculus::CrossProduct filter;
filter.SetPrimaryField("vec1");
filter.SetUseCoordinateSystemAsSecondaryField(true);
filter.SetSecondaryCoordinateSystem(2);

@ -20,7 +20,6 @@ set(headers
ContourTreeUniformAugmented.h
CoordinateSystemTransform.h
CosmoTools.h
CrossProduct.h
DispatcherMapField.h
DispatcherMapTopology.h
DispatcherCellNeighborhood.h

@ -1,38 +0,0 @@
//============================================================================
// 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_worklet_CrossProduct_h
#define vtk_m_worklet_CrossProduct_h
#include <vtkm/worklet/WorkletMapField.h>
#include <vtkm/VectorAnalysis.h>
namespace vtkm
{
namespace worklet
{
class CrossProduct : public vtkm::worklet::WorkletMapField
{
public:
using ControlSignature = void(FieldIn, FieldIn, FieldOut);
template <typename T>
VTKM_EXEC void operator()(const vtkm::Vec<T, 3>& vec1,
const vtkm::Vec<T, 3>& vec2,
vtkm::Vec<T, 3>& outVec) const
{
outVec = vtkm::Cross(vec1, vec2);
}
};
}
} // namespace vtkm::worklet
#endif // vtk_m_worklet_CrossProduct_h

@ -29,7 +29,6 @@ set(unit_tests
UnitTestContourTreeUniformDistributed.cxx
UnitTestCoordinateSystemTransform.cxx
UnitTestCosmoTools.cxx
UnitTestCrossProduct.cxx
UnitTestDescriptiveStatistics.cxx
UnitTestDotProduct.cxx
UnitTestFieldStatistics.cxx

@ -1,118 +0,0 @@
//============================================================================
// 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/worklet/CrossProduct.h>
#include <vtkm/worklet/DispatcherMapField.h>
#include <random>
#include <vtkm/cont/testing/Testing.h>
namespace
{
std::mt19937 randGenerator;
template <typename T>
void createVectors(std::vector<vtkm::Vec<T, 3>>& vecs1, std::vector<vtkm::Vec<T, 3>>& vecs2)
{
// First, test the standard directions.
// X x Y
vecs1.push_back(vtkm::make_Vec(1, 0, 0));
vecs2.push_back(vtkm::make_Vec(0, 1, 0));
// Y x Z
vecs1.push_back(vtkm::make_Vec(0, 1, 0));
vecs2.push_back(vtkm::make_Vec(0, 0, 1));
// Z x X
vecs1.push_back(vtkm::make_Vec(0, 0, 1));
vecs2.push_back(vtkm::make_Vec(1, 0, 0));
// Y x X
vecs1.push_back(vtkm::make_Vec(0, 1, 0));
vecs2.push_back(vtkm::make_Vec(1, 0, 0));
// Z x Y
vecs1.push_back(vtkm::make_Vec(0, 0, 1));
vecs2.push_back(vtkm::make_Vec(0, 1, 0));
// X x Z
vecs1.push_back(vtkm::make_Vec(1, 0, 0));
vecs2.push_back(vtkm::make_Vec(0, 0, 1));
//Test some other vector combinations
std::uniform_real_distribution<vtkm::Float64> randomDist(-10.0, 10.0);
for (int i = 0; i < 100; i++)
{
vecs1.push_back(vtkm::make_Vec(
randomDist(randGenerator), randomDist(randGenerator), randomDist(randGenerator)));
vecs2.push_back(vtkm::make_Vec(
randomDist(randGenerator), randomDist(randGenerator), randomDist(randGenerator)));
}
}
template <typename T>
void TestCrossProduct()
{
std::vector<vtkm::Vec<T, 3>> inputVecs1, inputVecs2;
createVectors(inputVecs1, inputVecs2);
vtkm::cont::ArrayHandle<vtkm::Vec<T, 3>> inputArray1, inputArray2;
vtkm::cont::ArrayHandle<vtkm::Vec<T, 3>> outputArray;
inputArray1 = vtkm::cont::make_ArrayHandle(inputVecs1, vtkm::CopyFlag::Off);
inputArray2 = vtkm::cont::make_ArrayHandle(inputVecs2, vtkm::CopyFlag::Off);
vtkm::worklet::CrossProduct crossProductWorklet;
vtkm::worklet::DispatcherMapField<vtkm::worklet::CrossProduct> dispatcherCrossProduct(
crossProductWorklet);
dispatcherCrossProduct.Invoke(inputArray1, inputArray2, outputArray);
VTKM_TEST_ASSERT(outputArray.GetNumberOfValues() == inputArray1.GetNumberOfValues(),
"Wrong number of results for CrossProduct worklet");
//Test the canonical cases.
VTKM_TEST_ASSERT(test_equal(outputArray.ReadPortal().Get(0), vtkm::make_Vec(0, 0, 1)) &&
test_equal(outputArray.ReadPortal().Get(1), vtkm::make_Vec(1, 0, 0)) &&
test_equal(outputArray.ReadPortal().Get(2), vtkm::make_Vec(0, 1, 0)) &&
test_equal(outputArray.ReadPortal().Get(3), vtkm::make_Vec(0, 0, -1)) &&
test_equal(outputArray.ReadPortal().Get(4), vtkm::make_Vec(-1, 0, 0)) &&
test_equal(outputArray.ReadPortal().Get(5), vtkm::make_Vec(0, -1, 0)),
"Wrong result for CrossProduct worklet");
for (vtkm::Id i = 0; i < inputArray1.GetNumberOfValues(); i++)
{
vtkm::Vec<T, 3> v1 = inputArray1.ReadPortal().Get(i);
vtkm::Vec<T, 3> v2 = inputArray2.ReadPortal().Get(i);
vtkm::Vec<T, 3> res = outputArray.ReadPortal().Get(i);
//Make sure result is orthogonal each input vector. Need to normalize to compare with zero.
vtkm::Vec<T, 3> v1N(vtkm::Normal(v1)), v2N(vtkm::Normal(v1)), resN(vtkm::Normal(res));
VTKM_TEST_ASSERT(test_equal(vtkm::Dot(resN, v1N), T(0.0)), "Wrong result for cross product");
VTKM_TEST_ASSERT(test_equal(vtkm::Dot(resN, v2N), T(0.0)), "Wrong result for cross product");
T sinAngle = vtkm::Magnitude(res) * vtkm::RMagnitude(v1) * vtkm::RMagnitude(v2);
T cosAngle = vtkm::Dot(v1, v2) * vtkm::RMagnitude(v1) * vtkm::RMagnitude(v2);
VTKM_TEST_ASSERT(test_equal(sinAngle * sinAngle + cosAngle * cosAngle, T(1.0)),
"Bad cross product length.");
}
}
void TestCrossProductWorklets()
{
std::cout << "Testing CrossProduct Worklet" << std::endl;
TestCrossProduct<vtkm::Float32>();
TestCrossProduct<vtkm::Float64>();
}
}
int UnitTestCrossProduct(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(TestCrossProductWorklets, argc, argv);
}