//#####################################################################
// Copyright 2006-2007, Geoffrey Irving, Andrew Selle.
// This file is part of PhysBAM whose distribution is governed by the license contained in the accompanying file PHYSBAM_COPYRIGHT.txt.
//#####################################################################
// Class LEVELSET_IMPLICIT_OBJECT
//#####################################################################
#include <Core/Implicit_Objects/LEVELSET_IMPLICIT_OBJECT.h>
#include <Core/Basic_Geometry/RAY_HIT.h>
#include <Core/Interpolation/LINEAR_INTERPOLATION.h>
#include <Core/Level_Sets/LEVELSET.h>
#include <Core/Matrices/MATRIX.h>
#include <Core/Matrices/SYMMETRIC_MATRIX.h>
#include <Core/Read_Write/READ_WRITE.h>
#include <Core/Registry/STRUCTURE_REGISTRY.h>
using namespace PhysBAM;
//#####################################################################
// Constructor
//#####################################################################
template<class TV> LEVELSET_IMPLICIT_OBJECT<TV>::
LEVELSET_IMPLICIT_OBJECT(const GRID<TV>& grid_input,AT_NODES,ARRAY<T,IV>& phi_input)
    :levelset(grid_input,at_nodes,phi_input),need_destroy_data(false)
{
    Update_Box();
    Update_Minimum_Cell_Size();
}
//#####################################################################
// Destructor
//#####################################################################
template<class TV> LEVELSET_IMPLICIT_OBJECT<TV>::
~LEVELSET_IMPLICIT_OBJECT()
{
    if(need_destroy_data) delete &levelset.phi;
}
//#####################################################################
// Function Create
//#####################################################################
template<class TV> LEVELSET_IMPLICIT_OBJECT<TV>* LEVELSET_IMPLICIT_OBJECT<TV>::
Create()
{
    LEVELSET_IMPLICIT_OBJECT* levelset_implicit_object=new LEVELSET_IMPLICIT_OBJECT(GRID<TV>(),at_nodes,*new ARRAY<T,IV>);
    levelset_implicit_object->need_destroy_data=true;
    return levelset_implicit_object;
}
//#####################################################################
// Function Update_Minimum_Cell_Size
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Set_Custom_Secondary_Interpolation(INTERPOLATION_NODE<TV,T>& interpolation)
{
    levelset.Set_Custom_Secondary_Interpolation(interpolation);
}
//#####################################################################
// Function Update_Minimum_Cell_Size
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Set_Custom_Normal_Interpolation(INTERPOLATION_NODE<TV,TV>& interpolation)
{
    levelset.Set_Custom_Normal_Interpolation(interpolation);
}
//#####################################################################
// Function Update_Box
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Update_Box()
{
    box=levelset.Reg_Grid().domain; // workaround bug in gcc 4.2.4 by double assigning - bleurg
    BASE::box=levelset.Reg_Grid().domain; // BASE:: prefix to work around compiler bug in gcc 4.2.4
}
//#####################################################################
// Function Update_Minimum_Cell_Size
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Update_Minimum_Cell_Size(const int maximum_depth)
{
    minimum_cell_size=levelset.Native_Grid().dX.Min();
}
//#####################################################################
// Function Minimum_Cell_Size_Within_Box
//#####################################################################
template<class TV> typename TV::SCALAR LEVELSET_IMPLICIT_OBJECT<TV>::
Minimum_Cell_Size_Within_Box(const RANGE<TV>& box) const
{
    return levelset.Native_Grid().dX.Min();
}
//#####################################################################
// Function operator()
//#####################################################################
template<class TV> typename TV::SCALAR LEVELSET_IMPLICIT_OBJECT<TV>::
operator()(const TV& location) const
{
    return levelset.Phi(location);
}
//#####################################################################
// Function Extended_Phi
//#####################################################################
template<class TV> typename TV::SCALAR LEVELSET_IMPLICIT_OBJECT<TV>::
Extended_Phi(const TV& location) const
{
    return levelset.Extended_Phi(location);
}
//#####################################################################
// Function Phi_Secondary
//#####################################################################
template<class TV> typename TV::SCALAR LEVELSET_IMPLICIT_OBJECT<TV>::
Phi_Secondary(const TV& location) const
{
    return levelset.Phi_Secondary(location);
}
//#####################################################################
// Function Normal
//#####################################################################
template<class TV> TV LEVELSET_IMPLICIT_OBJECT<TV>::
Normal(const TV& location,const int aggregate) const
{
    return aggregate==-1?levelset.Normal(location):box.Normal(aggregate);
}
//#####################################################################
// Function Extended_Normal
//#####################################################################
template<class TV> TV LEVELSET_IMPLICIT_OBJECT<TV>::
Extended_Normal(const TV& location,const int aggregate) const
{
    return aggregate==-1?levelset.Extended_Normal(location):box.Normal(aggregate);
}
//#####################################################################
// Function Compute_Normals
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Compute_Normals()
{
    levelset.Compute_Normals();
}
//#####################################################################
// Function Compute_Cell_Minimum_And_Maximum
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Compute_Cell_Minimum_And_Maximum(const bool recompute_if_exists)
{
    levelset.Compute_Cell_Minimum_And_Maximum(recompute_if_exists);
}
//#####################################################################
// Function Inflate
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Inflate(const T inflation_distance)
{
    levelset.phi.array-=inflation_distance;
}
//#####################################################################
// Function Lazy_Inside
//#####################################################################
template<class TV> bool LEVELSET_IMPLICIT_OBJECT<TV>::
Lazy_Inside(const TV& location,const T contour_value) const
{
    return box.Lazy_Inside(location) && levelset.Lazy_Inside(location,contour_value);
}
//#####################################################################
// Function Lazy_Inside_And_Value
//#####################################################################
template<class TV> bool LEVELSET_IMPLICIT_OBJECT<TV>::
Lazy_Inside_And_Value(const TV& location,T& phi_value,const T contour_value) const
{
    return box.Lazy_Inside(location) && levelset.Lazy_Inside_And_Value(location,phi_value,contour_value);
}
//#####################################################################
// Function Lazy_Inside_Extended_Levelset
//#####################################################################
template<class TV> bool LEVELSET_IMPLICIT_OBJECT<TV>::
Lazy_Inside_Extended_Levelset(const TV& unclamped_X,const T contour_value) const
{
    return levelset.Lazy_Inside_Extended_Levelset(unclamped_X,contour_value);
}
//#####################################################################
// Function Lazy_Inside_Extended_Levelset_And_Value
//#####################################################################
template<class TV> bool LEVELSET_IMPLICIT_OBJECT<TV>::
Lazy_Inside_Extended_Levelset_And_Value(const TV& unclamped_X,T& phi_value,const T contour_value) const
{
    return levelset.Lazy_Inside_Extended_Levelset_And_Value(unclamped_X,phi_value,contour_value);
}
//#####################################################################
// Function Lazy_Outside
//#####################################################################
template<class TV> bool LEVELSET_IMPLICIT_OBJECT<TV>::
Lazy_Outside(const TV& location,const T contour_value) const
{
    return box.Lazy_Outside(location) || levelset.Lazy_Outside(location,contour_value);
}
//#####################################################################
// Function Lazy_Outside_Extended_Levelset
//#####################################################################
template<class TV> bool LEVELSET_IMPLICIT_OBJECT<TV>::
Lazy_Outside_Extended_Levelset(const TV& unclamped_X,const T contour_value) const
{
    return levelset.Lazy_Outside_Extended_Levelset(unclamped_X,contour_value);
}
//#####################################################################
// Function Lazy_Outside_Extended_Levelset_And_Value
//#####################################################################
template<class TV> bool LEVELSET_IMPLICIT_OBJECT<TV>::
Lazy_Outside_Extended_Levelset_And_Value(const TV& unclamped_X,T& phi_value,const T contour_value) const
{
    return levelset.Lazy_Outside_Extended_Levelset_And_Value(unclamped_X,phi_value,contour_value);
}
//#####################################################################
// Function Hessian
//#####################################################################
template<class TV> SYMMETRIC_MATRIX<typename TV::SCALAR,TV::m> LEVELSET_IMPLICIT_OBJECT<TV>::
Hessian(const TV& X) const
{
    return levelset.Hessian(X);
}
//#####################################################################
// Function Rescale
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Rescale(const T scaling_factor)
{
    PHYSBAM_ASSERT(scaling_factor>0,"Attempting to scale by negative factor");
    levelset.Set_Grid(GRID<TV>(levelset.Native_Grid().Node_Indices().Maximum_Corner()-1,scaling_factor*levelset.Native_Grid().domain));
    levelset.phi.array*=scaling_factor;
    Update_Box();
    Update_Minimum_Cell_Size();
}
//#####################################################################
// Function Translate
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Translate(const TV& translation)
{
    levelset.Set_Grid(GRID<TV>(levelset.Native_Grid().Node_Indices().Minimum_Corner()-1,levelset.Native_Grid().domain+translation));
    Update_Box();
    Update_Minimum_Cell_Size();
}
//#####################################################################
// Function Name
//#####################################################################
template<class TV> std::string LEVELSET_IMPLICIT_OBJECT<TV>::
Static_Name()
{
    return LOG::sprintf("LEVELSET_IMPLICIT_OBJECT<T,VECTOR<T,%d>,%d>",TV::m,0);
}
//#####################################################################
// Function Extension
//#####################################################################
template<class TV> std::string LEVELSET_IMPLICIT_OBJECT<TV>::
Static_Extension()
{
    return 0?(TV::m==2?"phi2d_mac":"phi_mac"):(TV::m==2?"phi2d":"phi");
}
//#####################################################################
// Function Integration_Step
//#####################################################################
template<class TV> typename TV::SCALAR LEVELSET_IMPLICIT_OBJECT<TV>::
Integration_Step(const T phi) const
{
    T distance=abs(phi);
    if(distance > 3*minimum_cell_size) return (T).5*distance;
    else if(distance > minimum_cell_size) return (T).25*distance;
    else return (T).1*minimum_cell_size;
}
//#####################################################################
// Function Minimum_Cell_Size
//#####################################################################
template<class TV> typename TV::SCALAR LEVELSET_IMPLICIT_OBJECT<TV>::
Minimum_Cell_Size() const
{
    return minimum_cell_size;
}
//#####################################################################
// Function Intersects
//#####################################################################
template<class TV> auto LEVELSET_IMPLICIT_OBJECT<TV>::
Intersects(const RAY<TV>& ray,const T isocontour) const -> RAY_HIT<T>
{
    using ::PhysBAM::Intersects; // unqualified lookup + ADL
    return Intersects(ray,levelset,isocontour);
}
//#####################################################################
// Function All_Intersections
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
All_Intersections(ARRAY<RAY_HIT<T> >& hits,const RAY<TV>& ray,const T isocontour) const
{
    using ::PhysBAM::All_Intersections; // unqualified lookup + ADL
    All_Intersections(hits,ray,levelset,isocontour);
}
//#####################################################################
// Function Read
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Read(TYPED_ISTREAM& input)
{
    Read_Binary(input,levelset);
    Update_Box();
    Update_Minimum_Cell_Size();
}
//#####################################################################
// Function Write
//#####################################################################
template<class TV> void LEVELSET_IMPLICIT_OBJECT<TV>::
Write(TYPED_OSTREAM& output) const
{
    Write_Binary(output,levelset);
}
//#####################################################################
// Function Name
//#####################################################################
template<class TV> std::string LEVELSET_IMPLICIT_OBJECT<TV>::
Name() const
{
    return Static_Name();
}
//#####################################################################
// Function Extension
//#####################################################################
template<class TV> std::string LEVELSET_IMPLICIT_OBJECT<TV>::
Extension() const
{
    return Static_Extension();
}
//#####################################################################
namespace PhysBAM{
template class LEVELSET_IMPLICIT_OBJECT<VECTOR<float,1> >;
template class LEVELSET_IMPLICIT_OBJECT<VECTOR<float,2> >;
template class LEVELSET_IMPLICIT_OBJECT<VECTOR<float,3> >;
template class LEVELSET_IMPLICIT_OBJECT<VECTOR<double,1> >;
template class LEVELSET_IMPLICIT_OBJECT<VECTOR<double,2> >;
template class LEVELSET_IMPLICIT_OBJECT<VECTOR<double,3> >;
}
