(* ::Package:: *)

(* ::Title:: *)
(*Measure*)


(* ::Subtitle:: *)
(*Compute area, volume, genus, Euler characteristic, ...*)


(* ::Text:: *)
(*Copyright Contributors to the OpenVDB Project*)
(*SPDX-License-Identifier: Apache-2.0*)


(* ::Section:: *)
(*Initialization & Usage*)


Package["OpenVDBLink`"]


PackageExport["OpenVDBArea"]
PackageExport["OpenVDBEulerCharacteristic"]
PackageExport["OpenVDBGenus"]
PackageExport["OpenVDBVolume"]


OpenVDBArea::usage = "OpenVDBArea[expr] finds the surface area of an OpenVDB level set.";
OpenVDBEulerCharacteristic::usage = "OpenVDBEulerCharacteristic[expr] finds the Euler characteristic of an OpenVDB level set.";
OpenVDBGenus::usage = "OpenVDBGenus[expr] finds the genus of an OpenVDB level set.";
OpenVDBVolume::usage = "OpenVDBVolume[expr] finds the volume of an OpenVDB level set.";


(* ::Section:: *)
(*OpenVDBArea*)


(* ::Subsection::Closed:: *)
(*Main*)


OpenVDBArea[args___] /; !CheckArgs[OpenVDBArea[args], {1, 2}] = $Failed;


OpenVDBArea[args___] :=
    With[{res = iOpenVDBArea[args]},
        res /; res =!= $Failed
    ]


OpenVDBArea[args___] := mOpenVDBArea[args]


(* ::Subsection::Closed:: *)
(*iOpenVDBArea*)


iOpenVDBArea[vdb_?OpenVDBScalarGridQ, regime_?regimeQ] :=
    Block[{area, scale},
        area = scalarArea[vdb];
        (
            area = regimeConvert[vdb, area, $worldregime -> regime, 2];

            area

        ) /; area =!= $Failed
    ]


iOpenVDBArea[vdb_] := iOpenVDBArea[vdb, $worldregime]


iOpenVDBArea[___] = $Failed;


(* ::Subsection::Closed:: *)
(*Argument conform & completion*)


registerForLevelSet[iOpenVDBArea, 1];


SyntaxInformation[OpenVDBArea] = {"ArgumentsPattern" -> {_, _.}};


OpenVDBDefaultSpace[OpenVDBArea] = $worldregime;


(* ::Subsection::Closed:: *)
(*Utilities*)


scalarArea[vdb_?emptyVDBQ] = 0.


scalarArea[vdb_?levelSetQ] :=
    With[{vol = vdb["levelSetGridArea"[]]},
        vol /; NumericQ[vol]
    ]


scalarArea[vdb_] /; !levelSetQ[vdb] = Indeterminate;


scalarArea[___] = $Failed;


(* ::Subsection::Closed:: *)
(*Messages*)


mOpenVDBArea[expr_, ___] /; messageScalarGridQ[expr, OpenVDBArea] = $Failed;


mOpenVDBArea[_, regime_] /; messageRegimeSpecQ[regime, OpenVDBArea] = $Failed;


mOpenVDBArea[___] = $Failed;


(* ::Section:: *)
(*OpenVDBEulerCharacteristic*)


(* ::Subsection::Closed:: *)
(*Main*)


OpenVDBEulerCharacteristic[args___] /; !CheckArgs[OpenVDBEulerCharacteristic[args], 1] = $Failed;


OpenVDBEulerCharacteristic[args___] :=
    With[{res = iOpenVDBEulerCharacteristic[args]},
        res /; res =!= $Failed
    ]


OpenVDBEulerCharacteristic[args___] := mOpenVDBEulerCharacteristic[args]


(* ::Subsection::Closed:: *)
(*iOpenVDBEulerCharacteristic*)


iOpenVDBEulerCharacteristic[vdb_?OpenVDBScalarGridQ] :=
    Block[{char},
        char = Which[
            !levelSetQ[vdb], Indeterminate,
            emptyVDBQ[vdb], Undefined,
            True, Replace[vdb["levelSetGridEulerCharacteristic"[]], Except[_Integer] -> $Failed, {0}]
        ];

        char /; char =!= $Failed
    ]


iOpenVDBEulerCharacteristic[___] = $Failed;


(* ::Subsection::Closed:: *)
(*Argument conform & completion*)


registerForLevelSet[iOpenVDBEulerCharacteristic, 1];


SyntaxInformation[OpenVDBEulerCharacteristic] = {"ArgumentsPattern" -> {_}};


(* ::Subsection::Closed:: *)
(*Messages*)


mOpenVDBEulerCharacteristic[expr_] /; messageScalarGridQ[expr, OpenVDBEulerCharacteristic] = $Failed;


mOpenVDBEulerCharacteristic[___] = $Failed;


(* ::Section:: *)
(*OpenVDBGenus*)


(* ::Subsection::Closed:: *)
(*Main*)


OpenVDBGenus[args___] /; !CheckArgs[OpenVDBGenus[args], 1] = $Failed;


OpenVDBGenus[args___] :=
    With[{res = iOpenVDBGenus[args]},
        res /; res =!= $Failed
    ]


OpenVDBGenus[args___] := mOpenVDBGenus[args]


(* ::Subsection::Closed:: *)
(*iOpenVDBGenus*)


iOpenVDBGenus[vdb_?OpenVDBScalarGridQ] :=
    Block[{genus},
        genus = Which[
            !levelSetQ[vdb], Indeterminate,
            emptyVDBQ[vdb], Undefined,
            True, Replace[vdb["levelSetGridGenus"[]], Except[_Integer] -> $Failed, {0}]
        ];

        genus /; genus =!= $Failed
    ]


iOpenVDBGenus[___] = $Failed;


(* ::Subsection::Closed:: *)
(*Argument conform & completion*)


registerForLevelSet[iOpenVDBGenus, 1];


SyntaxInformation[OpenVDBGenus] = {"ArgumentsPattern" -> {_}};


(* ::Subsection::Closed:: *)
(*Messages*)


mOpenVDBGenus[expr_, ___] /; messageScalarGridQ[expr, OpenVDBGenus] = $Failed;


mOpenVDBGenus[___] = $Failed;


(* ::Section:: *)
(*OpenVDBVolume*)


(* ::Subsection::Closed:: *)
(*Main*)


OpenVDBVolume[args___] /; !CheckArgs[OpenVDBVolume[args], {1, 2}] = $Failed;


OpenVDBVolume[args___] :=
    With[{res = iOpenVDBVolume[args]},
        res /; res =!= $Failed
    ]


OpenVDBVolume[args___] := mOpenVDBVolume[args]


(* ::Subsection::Closed:: *)
(*iOpenVDBVolume*)


iOpenVDBVolume[vdb_?OpenVDBScalarGridQ, regime_?regimeQ] :=
    Block[{volume, scale},
        volume = scalarVolume[vdb];
        (
            volume = regimeConvert[vdb, volume, $worldregime -> regime, 3];

            volume

        ) /; volume =!= $Failed
    ]


iOpenVDBVolume[vdb_] := iOpenVDBVolume[vdb, $worldregime]


iOpenVDBVolume[___] = $Failed;


(* ::Subsection::Closed:: *)
(*Argument conform & completion*)


registerForLevelSet[iOpenVDBVolume, 1];


SyntaxInformation[OpenVDBVolume] = {"ArgumentsPattern" -> {_, _.}};


OpenVDBDefaultSpace[OpenVDBVolume] = $worldregime;


(* ::Subsection::Closed:: *)
(*Utilities*)


scalarVolume[vdb_?emptyVDBQ] := If[TrueQ[Negative[vdb["getBackgroundValue"[]]]], \[Infinity], 0.]


scalarVolume[vdb_?levelSetQ] :=
    With[{vol = vdb["levelSetGridVolume"[]]},
        vol /; NumericQ[vol]
    ]


scalarVolume[vdb_?fogVolumeQ] :=
    With[{tots = OpenVDBActiveVoxelSliceTotals[vdb]},
        (
            Total[tots] * vdb["VoxelSize"]^3

        ) /; VectorQ[tots, NumericQ]
    ]


scalarVolume[vdb_] /; !levelSetQ[vdb] && !fogVolumeQ[vdb] = Indeterminate;


scalarVolume[___] = $Failed;


(* ::Subsection::Closed:: *)
(*Messages*)


mOpenVDBVolume[expr_, ___] /; messageScalarGridQ[expr, OpenVDBVolume] = $Failed;


mOpenVDBVolume[_, regime_] /; messageRegimeSpecQ[regime, OpenVDBVolume] = $Failed;


mOpenVDBVolume[___] = $Failed;
