You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

619 lines
16 KiB
Plaintext

/*=========================================================================
Program: Visualization Toolkit
Module: vtkSparseArray.txx
-------------------------------------------------------------------------
Copyright 2008 Sandia Corporation.
Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
the U.S. Government retains certain rights in this software.
-------------------------------------------------------------------------
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm 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 vtkSparseArray_txx
#define vtkSparseArray_txx
#include <algorithm>
#include <limits>
template <typename T>
vtkSparseArray<T>* vtkSparseArray<T>::New()
{
// Don't use object factory macros on templates, it'll confuse the object
// factory.
vtkSparseArray<T>* ret = new vtkSparseArray<T>;
ret->InitializeObjectBase();
return ret;
}
template <typename T>
void vtkSparseArray<T>::PrintSelf(ostream& os, vtkIndent indent)
{
vtkSparseArray<T>::Superclass::PrintSelf(os, indent);
}
template <typename T>
bool vtkSparseArray<T>::IsDense()
{
return false;
}
template <typename T>
const vtkArrayExtents& vtkSparseArray<T>::GetExtents()
{
return this->Extents;
}
template <typename T>
typename vtkSparseArray<T>::SizeT vtkSparseArray<T>::GetNonNullSize()
{
return this->Values.size();
}
template <typename T>
void vtkSparseArray<T>::GetCoordinatesN(const SizeT n, vtkArrayCoordinates& coordinates)
{
coordinates.SetDimensions(this->GetDimensions());
for (DimensionT i = 0; i != this->GetDimensions(); ++i)
coordinates[i] = this->Coordinates[i][n];
}
template <typename T>
vtkArray* vtkSparseArray<T>::DeepCopy()
{
ThisT* const copy = ThisT::New();
copy->SetName(this->GetName());
copy->Extents = this->Extents;
copy->DimensionLabels = this->DimensionLabels;
copy->Coordinates = this->Coordinates;
copy->Values = this->Values;
copy->NullValue = this->NullValue;
return copy;
}
template <typename T>
const T& vtkSparseArray<T>::GetValue(CoordinateT i)
{
if (1 != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return this->NullValue;
}
// Do a naive linear-search for the time-being ...
for (vtkIdType row = 0; row != static_cast<vtkIdType>(this->Values.size()); ++row)
{
if (i != this->Coordinates[0][row])
continue;
return this->Values[row];
}
return this->NullValue;
}
template <typename T>
const T& vtkSparseArray<T>::GetValue(CoordinateT i, CoordinateT j)
{
if (2 != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return this->NullValue;
}
// Do a naive linear-search for the time-being ...
for (vtkIdType row = 0; row != static_cast<vtkIdType>(this->Values.size()); ++row)
{
if (i != this->Coordinates[0][row])
continue;
if (j != this->Coordinates[1][row])
continue;
return this->Values[row];
}
return this->NullValue;
}
template <typename T>
const T& vtkSparseArray<T>::GetValue(CoordinateT i, CoordinateT j, CoordinateT k)
{
if (3 != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return this->NullValue;
}
// Do a naive linear-search for the time-being ...
for (vtkIdType row = 0; row != static_cast<vtkIdType>(this->Values.size()); ++row)
{
if (i != this->Coordinates[0][row])
continue;
if (j != this->Coordinates[1][row])
continue;
if (k != this->Coordinates[2][row])
continue;
return this->Values[row];
}
return this->NullValue;
}
template <typename T>
const T& vtkSparseArray<T>::GetValue(const vtkArrayCoordinates& coordinates)
{
if (coordinates.GetDimensions() != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return this->NullValue;
}
// Do a naive linear-search for the time-being ...
for (vtkIdType row = 0; row != static_cast<vtkIdType>(this->Values.size()); ++row)
{
for (DimensionT column = 0; column != this->GetDimensions(); ++column)
{
if (coordinates[column] != this->Coordinates[column][row])
break;
if (column + 1 == this->GetDimensions())
return this->Values[row];
}
}
return this->NullValue;
}
template <typename T>
const T& vtkSparseArray<T>::GetValueN(const SizeT n)
{
return this->Values[n];
}
template <typename T>
void vtkSparseArray<T>::SetValue(CoordinateT i, const T& value)
{
if (1 != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return;
}
// Do a naive linear-search for the time-being ...
for (vtkIdType row = 0; row != static_cast<vtkIdType>(this->Values.size()); ++row)
{
if (i != this->Coordinates[0][row])
continue;
this->Values[row] = value;
return;
}
// Element doesn't already exist, so add it to the end of the list ...
this->AddValue(i, value);
}
template <typename T>
void vtkSparseArray<T>::SetValue(CoordinateT i, CoordinateT j, const T& value)
{
if (2 != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return;
}
// Do a naive linear-search for the time-being ...
for (vtkIdType row = 0; row != static_cast<vtkIdType>(this->Values.size()); ++row)
{
if (i != this->Coordinates[0][row])
continue;
if (j != this->Coordinates[1][row])
continue;
this->Values[row] = value;
return;
}
// Element doesn't already exist, so add it to the end of the list ...
this->AddValue(i, j, value);
}
template <typename T>
void vtkSparseArray<T>::SetValue(CoordinateT i, CoordinateT j, CoordinateT k, const T& value)
{
if (3 != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return;
}
// Do a naive linear-search for the time-being ...
for (vtkIdType row = 0; row != static_cast<vtkIdType>(this->Values.size()); ++row)
{
if (i != this->Coordinates[0][row])
continue;
if (j != this->Coordinates[1][row])
continue;
if (k != this->Coordinates[2][row])
continue;
this->Values[row] = value;
return;
}
// Element doesn't already exist, so add it to the end of the list ...
this->AddValue(i, j, k, value);
}
template <typename T>
void vtkSparseArray<T>::SetValue(const vtkArrayCoordinates& coordinates, const T& value)
{
if (coordinates.GetDimensions() != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return;
}
// Do a naive linear-search for the time-being ...
for (vtkIdType row = 0; row != static_cast<vtkIdType>(this->Values.size()); ++row)
{
for (DimensionT column = 0; column != this->GetDimensions(); ++column)
{
if (coordinates[column] != this->Coordinates[column][row])
break;
if (column + 1 == this->GetDimensions())
{
this->Values[row] = value;
return;
}
}
}
// Element doesn't already exist, so add it to the end of the list ...
this->AddValue(coordinates, value);
}
template <typename T>
void vtkSparseArray<T>::SetValueN(const SizeT n, const T& value)
{
this->Values[n] = value;
}
template <typename T>
void vtkSparseArray<T>::SetNullValue(const T& value)
{
this->NullValue = value;
}
template <typename T>
const T& vtkSparseArray<T>::GetNullValue()
{
return this->NullValue;
}
template <typename T>
void vtkSparseArray<T>::Clear()
{
for (DimensionT column = 0; column != this->GetDimensions(); ++column)
this->Coordinates[column].clear();
this->Values.clear();
}
/// Predicate object for use with std::sort(). Given a vtkArraySort object that defines which array
/// dimensions will be sorted in what order, SortCoordinates is used to establish a sorted order for
/// the values stored in vtkSparseArray. Note that SortCoordinates never actually modifies its
/// inputs.
struct SortCoordinates
{
SortCoordinates(const vtkArraySort& sort, const std::vector<std::vector<vtkIdType> >& coordinates)
: Sort(&sort)
, Coordinates(&coordinates)
{
}
bool operator()(const vtkIdType lhs, const vtkIdType rhs) const
{
const vtkArraySort& sort = *this->Sort;
const std::vector<std::vector<vtkIdType> >& coordinates = *this->Coordinates;
for (vtkIdType i = 0; i != sort.GetDimensions(); ++i)
{
if (coordinates[sort[i]][lhs] == coordinates[sort[i]][rhs])
continue;
return coordinates[sort[i]][lhs] < coordinates[sort[i]][rhs];
}
return false;
}
const vtkArraySort* Sort;
const std::vector<std::vector<vtkIdType> >* Coordinates;
};
template <typename T>
void vtkSparseArray<T>::Sort(const vtkArraySort& sort)
{
if (sort.GetDimensions() < 1)
{
vtkErrorMacro(<< "Sort must order at least one dimension.");
return;
}
for (DimensionT i = 0; i != sort.GetDimensions(); ++i)
{
if (sort[i] < 0 || sort[i] >= this->GetDimensions())
{
vtkErrorMacro(<< "Sort dimension out-of-bounds.");
return;
}
}
const SizeT count = this->GetNonNullSize();
std::vector<DimensionT> sort_order(count);
for (SizeT i = 0; i != count; ++i)
sort_order[i] = static_cast<DimensionT>(i);
std::sort(sort_order.begin(), sort_order.end(), SortCoordinates(sort, this->Coordinates));
std::vector<DimensionT> temp_coordinates(count);
for (DimensionT j = 0; j != this->GetDimensions(); ++j)
{
for (SizeT i = 0; i != count; ++i)
temp_coordinates[i] = this->Coordinates[j][sort_order[i]];
std::swap(temp_coordinates, this->Coordinates[j]);
}
std::vector<T> temp_values(count);
for (SizeT i = 0; i != count; ++i)
temp_values[i] = this->Values[sort_order[i]];
std::swap(temp_values, this->Values);
}
template <typename T>
std::vector<typename vtkSparseArray<T>::CoordinateT> vtkSparseArray<T>::GetUniqueCoordinates(
DimensionT dimension)
{
if (dimension < 0 || dimension >= this->GetDimensions())
{
vtkErrorMacro(<< "Dimension out-of-bounds.");
return std::vector<CoordinateT>();
}
std::vector<CoordinateT> results(this->Coordinates[dimension]);
std::sort(results.begin(), results.end());
results.erase(std::unique(results.begin(), results.end()), results.end());
return results;
}
template <typename T>
const typename vtkSparseArray<T>::CoordinateT* vtkSparseArray<T>::GetCoordinateStorage(
DimensionT dimension) const
{
if (dimension < 0 || dimension >= this->GetDimensions())
{
vtkErrorMacro(<< "Dimension out-of-bounds.");
return nullptr;
}
return &this->Coordinates[dimension][0];
}
template <typename T>
typename vtkSparseArray<T>::CoordinateT* vtkSparseArray<T>::GetCoordinateStorage(
DimensionT dimension)
{
if (dimension < 0 || dimension >= this->GetDimensions())
{
vtkErrorMacro(<< "Dimension out-of-bounds.");
return nullptr;
}
return &this->Coordinates[dimension][0];
}
template <typename T>
const T* vtkSparseArray<T>::GetValueStorage() const
{
return &(this->Values[0]);
}
template <typename T>
T* vtkSparseArray<T>::GetValueStorage()
{
return &this->Values[0];
}
template <typename T>
void vtkSparseArray<T>::ReserveStorage(const SizeT value_count)
{
for (DimensionT dimension = 0; dimension != this->GetDimensions(); ++dimension)
this->Coordinates[dimension].resize(value_count);
this->Values.resize(value_count);
}
template <typename T>
void vtkSparseArray<T>::SetExtentsFromContents()
{
vtkArrayExtents new_extents;
const vtkIdType row_begin = 0;
const vtkIdType row_end = row_begin + static_cast<vtkIdType>(this->Values.size());
const DimensionT dimension_count = this->GetDimensions();
for (DimensionT dimension = 0; dimension != dimension_count; ++dimension)
{
vtkIdType range_begin = std::numeric_limits<vtkIdType>::max();
vtkIdType range_end = -std::numeric_limits<vtkIdType>::max();
for (vtkIdType row = row_begin; row != row_end; ++row)
{
range_begin = std::min(range_begin, this->Coordinates[dimension][row]);
range_end = std::max(range_end, this->Coordinates[dimension][row] + 1);
}
new_extents.Append(vtkArrayRange(range_begin, range_end));
}
this->Extents = new_extents;
}
template <typename T>
void vtkSparseArray<T>::SetExtents(const vtkArrayExtents& extents)
{
if (extents.GetDimensions() != this->GetDimensions())
{
vtkErrorMacro(<< "Extent-array dimension mismatch.");
return;
}
this->Extents = extents;
}
template <typename T>
void vtkSparseArray<T>::AddValue(CoordinateT i, const T& value)
{
this->AddValue(vtkArrayCoordinates(i), value);
}
template <typename T>
void vtkSparseArray<T>::AddValue(CoordinateT i, CoordinateT j, const T& value)
{
this->AddValue(vtkArrayCoordinates(i, j), value);
}
template <typename T>
void vtkSparseArray<T>::AddValue(CoordinateT i, CoordinateT j, CoordinateT k, const T& value)
{
this->AddValue(vtkArrayCoordinates(i, j, k), value);
}
template <typename T>
void vtkSparseArray<T>::AddValue(const vtkArrayCoordinates& coordinates, const T& value)
{
if (coordinates.GetDimensions() != this->GetDimensions())
{
vtkErrorMacro(<< "Index-array dimension mismatch.");
return;
}
this->Values.push_back(value);
for (DimensionT i = 0; i != coordinates.GetDimensions(); ++i)
this->Coordinates[i].push_back(coordinates[i]);
}
template <typename T>
bool vtkSparseArray<T>::Validate()
{
vtkIdType duplicate_count = 0;
vtkIdType out_of_bound_count = 0;
const vtkIdType dimensions = this->GetDimensions();
const vtkIdType count = this->GetNonNullSize();
// Create an arbitrary sorted order for our coordinates ...
vtkArraySort sort;
sort.SetDimensions(dimensions);
for (vtkIdType i = 0; i != dimensions; ++i)
sort[i] = i;
std::vector<vtkIdType> sort_order(count);
for (vtkIdType i = 0; i != count; ++i)
sort_order[i] = i;
std::sort(sort_order.begin(), sort_order.end(), SortCoordinates(sort, this->Coordinates));
// Now, look for duplicates ...
for (vtkIdType i = 0; i + 1 < count; ++i)
{
vtkIdType j;
for (j = 0; j != dimensions; ++j)
{
if (this->Coordinates[j][sort_order[i]] != this->Coordinates[j][sort_order[i + 1]])
break;
}
if (j == dimensions)
{
duplicate_count += 1;
}
}
// Look for out-of-bound coordinates ...
for (vtkIdType i = 0; i != count; ++i)
{
for (vtkIdType j = 0; j != dimensions; ++j)
{
if (this->Coordinates[j][i] < this->Extents[j].GetBegin() ||
this->Coordinates[j][i] >= this->Extents[j].GetEnd())
{
++out_of_bound_count;
break;
}
}
}
if (duplicate_count)
{
vtkErrorMacro(<< "Array contains " << duplicate_count << " duplicate coordinates.");
}
if (out_of_bound_count)
{
vtkErrorMacro(<< "Array contains " << out_of_bound_count << " out-of-bound coordinates.");
}
return (0 == duplicate_count) && (0 == out_of_bound_count);
}
template <typename T>
vtkSparseArray<T>::vtkSparseArray()
: NullValue(T())
{
}
template <typename T>
vtkSparseArray<T>::~vtkSparseArray()
{
}
template <typename T>
void vtkSparseArray<T>::InternalResize(const vtkArrayExtents& extents)
{
this->Extents = extents;
this->DimensionLabels.resize(extents.GetDimensions(), vtkStdString());
this->Coordinates.resize(extents.GetDimensions());
this->Values.resize(0);
}
template <typename T>
void vtkSparseArray<T>::InternalSetDimensionLabel(DimensionT i, const vtkStdString& label)
{
this->DimensionLabels[i] = label;
}
template <typename T>
vtkStdString vtkSparseArray<T>::InternalGetDimensionLabel(DimensionT i)
{
return this->DimensionLabels[i];
}
#endif