// Copyright (c) 2022-2024 INRIA Sophia-Antipolis (France), GeometryFactory (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL: https://github.com/CGAL/cgal/blob/v6.2/Isosurfacing_3/include/CGAL/Isosurfacing_3/marching_cubes_3.h $
// $Id: include/CGAL/Isosurfacing_3/marching_cubes_3.h cac3e9d75e2 $
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s)     : Julian Stahl
//                 Mael Rouxel-Labbé

#ifndef CGAL_ISOSURFACING_3_MARCHING_CUBES_3_H
#define CGAL_ISOSURFACING_3_MARCHING_CUBES_3_H

#include <CGAL/license/Isosurfacing_3.h>

#include <CGAL/Isosurfacing_3/internal/marching_cubes_functors.h>
#include <CGAL/Isosurfacing_3/internal/topologically_correct_marching_cubes_functors.h>

#include <CGAL/Named_function_parameters.h>
#include <CGAL/tags.h>

namespace CGAL {
namespace Isosurfacing {

/**
 * \ingroup IS_Methods_grp
 *
 * \brief creates a triangle soup that represents an isosurface generated by the Marching Cubes algorithm.
 *
 * \tparam ConcurrencyTag enables sequential versus parallel algorithm.
 *                        Possible values are `Sequential_tag`, `Parallel_if_available_tag`, or `Parallel_tag`.
 * \tparam Domain must be a model of `IsosurfacingDomain_3`.
 * \tparam PointRange must be a model of the concepts `RandomAccessContainer` and `BackInsertionSequence`
 *                    whose value type can be constructed from the point type of the domain.
 * \tparam TriangleRange must be a model of the concepts `RandomAccessContainer` and `BackInsertionSequence`
 *                      whose value type is itself a model of the concepts `RandomAccessContainer`
 *                      and `BackInsertionSequence` whose value type is `std::size_t`.
 * \tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
 *
 * \param domain the domain providing the spatial partition and the data
 * \param isovalue the value defining the isosurface
 * \param points the points of the triangles in the output triangle soup
 * \param triangles the faces of the output triangle soup. Each element in the vector describes a triangle using the indices of the points in `points`.
 * \param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
 *
 * \cgalNamedParamsBegin
 *   \cgalParamNBegin{use_topologically_correct_marching_cubes}
 *     \cgalParamDescription{whether the topologically correct variant of Marching Cubes \cgalCite{cgal:g-ctcmi-16} should be used, or the base version \cgalCite{cgal:c-mcctci-95}.}
 *     \cgalParamType{Boolean}
 *     \cgalParamDefault{`true`}
 *   \cgalParamNEnd
 *
 * \cond SKIP_IN_MANUAL
 *   \cgalParamNBegin{isovalue_nudging}
 *     \cgalParamDescription{snapping of function value at corner points to an epsilon below the isovalue if the function value is within an epsilon range around the isovalue}
 *     \cgalParamType{Boolean}
 *     \cgalParamDefault{`true`}
 *     \cgalParamExtra{Snapping the function value at corner points prevents singular cases. The geometry is changed only in case of the function value being within a small epsilon range. The potential change is small. }
 *   \cgalParamNEnd
 *
 *   \cgalParamNBegin{constrain_to_cell}
 *     \cgalParamDescription{whether to exclude the cell boundary from the interior vertex placement. }
 *     \cgalParamType{Boolean}
 *     \cgalParamDefault{`true`}
 *     \cgalParamExtra{Prevents degenerate or duplicate triangles.}
 *   \cgalParamNEnd
 * \endcond
 *
 * \cgalNamedParamsEnd
 *
 * \sa `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
 */
template <typename ConcurrencyTag = CGAL::Sequential_tag,
          typename Domain,
          typename PointRange,
          typename TriangleRange,
          typename NamedParameters = parameters::Default_named_parameters>
void marching_cubes(const Domain& domain,
                    const typename Domain::Geom_traits::FT isovalue,
                    PointRange& points,
                    TriangleRange& triangles,
                    const NamedParameters& np = parameters::default_values())
{
  using parameters::choose_parameter;
  using parameters::get_parameter;

  const bool use_tmc = choose_parameter(get_parameter(np, internal_np::use_topologically_correct_marching_cubes), true);
  const bool isovalue_nudging = choose_parameter(get_parameter(np, internal_np::isovalue_nudging), true);
  const bool constrain_to_cell = choose_parameter(get_parameter(np, internal_np::constrain_to_cell), true);

  if(use_tmc)
  {
    internal::TMC_functor<Domain, PointRange, TriangleRange> functor(domain, isovalue, isovalue_nudging, constrain_to_cell);
    domain.template for_each_cell<ConcurrencyTag>(functor);
    internal::triangles_to_polygon_soup(functor.triangles(), points, triangles);
  }
  else
  {
    // run marching cubes
    internal::Marching_cubes_3<Domain> functor(domain, isovalue, isovalue_nudging);
    domain.template for_each_cell<ConcurrencyTag>(functor);
    internal::triangles_to_polygon_soup(functor.triangles(), points, triangles);
  }
}

} // namespace Isosurfacing
} // namespace CGAL

#endif // CGAL_ISOSURFACING_3_MARCHING_CUBES_3_H
