// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
// SPDX-License-Identifier: BSD-3-Clause
#include "vtkFitImplicitFunction.h"

#include "vtkArrayDispatch.h"
#include "vtkArrayDispatchDataSetArrayList.h"
#include "vtkDataArrayRange.h"
#include "vtkImplicitFunction.h"
#include "vtkObjectFactory.h"
#include "vtkPointSet.h"
#include "vtkPoints.h"
#include "vtkSMPTools.h"

VTK_ABI_NAMESPACE_BEGIN
vtkStandardNewMacro(vtkFitImplicitFunction);
vtkCxxSetObjectMacro(vtkFitImplicitFunction, ImplicitFunction, vtkImplicitFunction);

//------------------------------------------------------------------------------
// Helper classes to support efficient computing, and threaded execution.
namespace
{

//------------------------------------------------------------------------------
// The threaded core of the algorithm
template <typename TArray>
struct ExtractPointsFunctor
{
  TArray* Points;
  vtkImplicitFunction* Function;
  double Threshold;
  vtkIdType* PointMap;

  ExtractPointsFunctor(TArray* points, vtkImplicitFunction* f, double thresh, vtkIdType* map)
    : Points(points)
    , Function(f)
    , Threshold(thresh)
    , PointMap(map)
  {
  }

  void operator()(vtkIdType ptId, vtkIdType endPtId)
  {
    auto p = vtk::DataArrayTupleRange<3>(this->Points, ptId).begin();
    vtkIdType* map = this->PointMap + ptId;
    vtkImplicitFunction* f = this->Function;
    double x[3], val;
    double tMin = (-this->Threshold);
    double tMax = this->Threshold;

    for (; ptId < endPtId; ++ptId, ++p)
    {
      p->GetTuple(x);
      val = f->FunctionValue(x);
      *map++ = ((val >= tMin && val < tMax) ? 1 : -1);
    }
  }
}; // ExtractPoints

struct ExtractPointsWorker
{
  template <typename TArray>
  void operator()(TArray* inPts, vtkFitImplicitFunction* self, vtkIdType* pointMap)
  {
    ExtractPointsFunctor<TArray> functor(
      inPts, self->GetImplicitFunction(), self->GetThreshold(), pointMap);
    vtkSMPTools::For(0, inPts->GetNumberOfTuples(), functor);
  }
};
} // anonymous namespace

//================= Begin class proper =======================================
//------------------------------------------------------------------------------
vtkFitImplicitFunction::vtkFitImplicitFunction()
{
  this->ImplicitFunction = nullptr;
  this->Threshold = 0.01;
}

//------------------------------------------------------------------------------
vtkFitImplicitFunction::~vtkFitImplicitFunction()
{
  this->SetImplicitFunction(nullptr);
}

//------------------------------------------------------------------------------
// Overload standard modified time function. If implicit function is modified,
// then this object is modified as well.
vtkMTimeType vtkFitImplicitFunction::GetMTime()
{
  vtkMTimeType mTime = this->MTime.GetMTime();
  vtkMTimeType impFuncMTime;

  if (this->ImplicitFunction != nullptr)
  {
    impFuncMTime = this->ImplicitFunction->GetMTime();
    mTime = (impFuncMTime > mTime ? impFuncMTime : mTime);
  }

  return mTime;
}

//------------------------------------------------------------------------------
// Traverse all the input points and extract those that lie near the surface
// of an implicit function.
int vtkFitImplicitFunction::FilterPoints(vtkPointSet* input)
{
  // Check the input.
  if (!this->ImplicitFunction)
  {
    vtkErrorMacro(<< "Implicit function required\n");
    return 0;
  }

  // Determine which points, if any, should be removed. We create a map
  // to keep track. The bulk of the algorithmic work is done in this pass.
  ExtractPointsWorker worker;
  if (!vtkArrayDispatch::DispatchByArray<vtkArrayDispatch::PointArrays>::Execute(
        input->GetPoints()->GetData(), worker, this, this->PointMap))
  {
    worker(input->GetPoints()->GetData(), this, this->PointMap);
  }

  return 1;
}

//------------------------------------------------------------------------------
void vtkFitImplicitFunction::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);

  os << indent << "Implicit Function: " << static_cast<void*>(this->ImplicitFunction) << "\n";
  os << indent << "Threshold: " << this->Threshold << "\n";
}
VTK_ABI_NAMESPACE_END
