Files
UnrealEngine/Engine/Source/ThirdParty/OpenVDB/openvdb-12.0.0/openvdb_wolfram/OpenVDBLink/DistanceMeasure.m
2025-05-18 13:04:45 +08:00

558 lines
14 KiB
Mathematica

(* ::Package:: *)
(* ::Title:: *)
(*DistanceMeasure*)
(* ::Subtitle:: *)
(*Perform membership, nearest, and distance queries on a grid.*)
(* ::Text:: *)
(*Copyright Contributors to the OpenVDB Project*)
(*SPDX-License-Identifier: Apache-2.0*)
(* ::Section:: *)
(*Initialization & Usage*)
Package["OpenVDBLink`"]
PackageExport["OpenVDBMember"]
PackageExport["OpenVDBNearest"]
PackageExport["OpenVDBDistance"]
PackageExport["OpenVDBSignedDistance"]
PackageExport["OpenVDBFillWithBalls"]
OpenVDBMember::usage = "OpenVDBMember[expr, pt] determines if the point pt lies within the region given by a scalar grid.";
OpenVDBNearest::usage = "OpenVDBNearest[expr, pt] finds closest point to pt on the iso surface of a scalar grid.";
OpenVDBDistance::usage = "OpenVDBDistance[expr, pt] finds minimum distance from pt to the iso surface of a scalar grid.";
OpenVDBSignedDistance::usage = "OpenVDBSignedDistance[expr, pt] finds minimum distance from pt to the iso surface of a scalar grid and returns a negative value if pt lies within the region given by the grid.";
OpenVDBFillWithBalls::usage = "OpenVDBFillWithBalls[expr, n, {rmin, rmax}] fills a closed scalar grid with up to n adaptively-sized balls with radii between rmin and rmax.";
(* ::Section:: *)
(*Membership*)
(* ::Subsection::Closed:: *)
(*OpenVDBMember*)
(* ::Subsubsection::Closed:: *)
(*Main*)
Options[OpenVDBMember] = {"IsoValue" -> Automatic};
OpenVDBMember[args___] /; !CheckArgs[OpenVDBMember[args], 2] = $Failed;
OpenVDBMember[args___] :=
With[{res = iOpenVDBMember[args]},
res /; res =!= $Failed
]
OpenVDBMember[args___] := messageCPTFunction[OpenVDBMember, args]
(* ::Subsubsection::Closed:: *)
(*iOpenVDBMember*)
(* ::Text:: *)
(*Skipping call to regimeConvert to avoid duplicating points when unnecessary.*)
Options[iOpenVDBMember] = Options[OpenVDBMember];
iOpenVDBMember[vdb_?OpenVDBScalarGridQ, pts_?coordinatesQ -> $indexregime, OptionsPattern[]] :=
Block[{isovalue, mems},
isovalue = gridIsoValue[OptionValue["IsoValue"], vdb];
(
mems = vdb["gridMember"[pts, isovalue]];
If[fogVolumeQ[vdb], Subtract[1, mems], mems] /; VectorQ[mems, IntegerQ]
) /; realQ[isovalue]
]
iOpenVDBMember[vdb_?OpenVDBScalarGridQ, pts_?coordinatesQ -> $worldregime, opts___] :=
iOpenVDBMember[vdb, regimeConvert[vdb, pts, $worldregime -> $indexregime] -> $indexregime, opts]
iOpenVDBMember[vdb_, pts_?coordinateQ -> regime_, opts___] :=
With[{res = iOpenVDBMember[vdb, {pts} -> regime, opts]},
res[[1]] /; VectorQ[res, IntegerQ] && Length[res] === 1
]
iOpenVDBMember[vdb_, pts_List, opts___] := iOpenVDBMember[vdb, pts -> $worldregime, opts]
iOpenVDBMember[___] = $Failed;
(* ::Subsubsection::Closed:: *)
(*Argument conform & completion*)
registerForLevelSet[iOpenVDBMember, 1];
SyntaxInformation[OpenVDBMember] = {"ArgumentsPattern" -> {_, _, OptionsPattern[]}};
OpenVDBDefaultSpace[OpenVDBMember] = $worldregime;
(* ::Section:: *)
(*Nearest*)
(* ::Subsection::Closed:: *)
(*OpenVDBNearest*)
(* ::Subsubsection::Closed:: *)
(*Main*)
Options[OpenVDBNearest] = {"IsoValue" -> Automatic};
OpenVDBNearest[args___] /; !CheckArgs[OpenVDBNearest[args], 2] = $Failed;
OpenVDBNearest[args___] :=
With[{res = iOpenVDBNearest[args]},
res /; res =!= $Failed
]
OpenVDBNearest[args___] := messageCPTFunction[OpenVDBNearest, args]
(* ::Subsubsection::Closed:: *)
(*iOpenVDBNearest*)
(* ::Text:: *)
(*Skipping call to regimeConvert to avoid duplicating points when unnecessary.*)
Options[iOpenVDBNearest] = Options[OpenVDBNearest];
iOpenVDBNearest[vdb_?OpenVDBScalarGridQ, pts_?coordinatesQ -> $worldregime, OptionsPattern[]] :=
Block[{isovalue, nearest},
isovalue = gridIsoValue[OptionValue["IsoValue"], vdb];
(
nearest = vdb["gridNearest"[pts, isovalue]];
nearest /; MatrixQ[nearest, NumericQ]
) /; realQ[isovalue]
]
iOpenVDBNearest[vdb_?OpenVDBScalarGridQ, pts_?coordinatesQ -> $indexregime, opts___] :=
iOpenVDBNearest[vdb, regimeConvert[vdb, pts, $indexregime -> $worldregime] -> $worldregime, opts]
iOpenVDBNearest[vdb_, pts_?coordinateQ -> regime_, opts___] :=
With[{res = iOpenVDBNearest[vdb, {pts} -> regime, opts]},
res[[1]] /; MatrixQ[res] && Dimensions[res] === {1, 3}
]
iOpenVDBNearest[vdb_, pts_List, opts___] := iOpenVDBNearest[vdb, pts -> $worldregime, opts]
iOpenVDBNearest[___] = $Failed;
(* ::Subsubsection::Closed:: *)
(*Argument conform & completion*)
registerForLevelSet[iOpenVDBNearest, 1];
SyntaxInformation[OpenVDBNearest] = {"ArgumentsPattern" -> {_, _, OptionsPattern[]}};
OpenVDBDefaultSpace[OpenVDBNearest] = $worldregime;
(* ::Section:: *)
(*Distance*)
(* ::Subsection::Closed:: *)
(*OpenVDBDistance*)
(* ::Subsubsection::Closed:: *)
(*Main*)
Options[OpenVDBDistance] = {"IsoValue" -> Automatic};
OpenVDBDistance[args___] /; !CheckArgs[OpenVDBDistance[args], 2] = $Failed;
OpenVDBDistance[args___] :=
With[{res = iOpenVDBDistance[args]},
res /; res =!= $Failed
]
OpenVDBDistance[args___] := messageCPTFunction[OpenVDBDistance, args]
(* ::Subsubsection::Closed:: *)
(*iOpenVDBDistance*)
(* ::Text:: *)
(*Skipping call to regimeConvert to avoid duplicating points when unnecessary.*)
Options[iOpenVDBDistance] = Options[OpenVDBDistance];
iOpenVDBDistance[vdb_?OpenVDBScalarGridQ, pts_?coordinatesQ -> $worldregime, OptionsPattern[]] :=
Block[{isovalue, dists},
isovalue = gridIsoValue[OptionValue["IsoValue"], vdb];
(
dists = vdb["gridDistance"[pts, isovalue]];
dists /; VectorQ[dists, NumericQ]
) /; realQ[isovalue]
]
iOpenVDBDistance[vdb_?OpenVDBScalarGridQ, pts_?coordinatesQ -> $indexregime, opts___] :=
iOpenVDBDistance[vdb, regimeConvert[vdb, pts, $indexregime -> $worldregime] -> $worldregime, opts]
iOpenVDBDistance[vdb_, pts_?coordinateQ -> regime_, opts___] :=
With[{res = iOpenVDBDistance[vdb, {pts} -> regime, opts]},
res[[1]] /; VectorQ[res, NumericQ] && Length[res] === 1
]
iOpenVDBDistance[vdb_, pts_List, opts___] := iOpenVDBDistance[vdb, pts -> $worldregime, opts]
iOpenVDBDistance[___] = $Failed;
(* ::Subsubsection::Closed:: *)
(*Argument conform & completion*)
registerForLevelSet[iOpenVDBDistance, 1];
SyntaxInformation[OpenVDBDistance] = {"ArgumentsPattern" -> {_, _, OptionsPattern[]}};
OpenVDBDefaultSpace[OpenVDBDistance] = $worldregime;
(* ::Subsection::Closed:: *)
(*OpenVDBSignedDistance*)
(* ::Subsubsection::Closed:: *)
(*Main*)
Options[OpenVDBSignedDistance] = {"IsoValue" -> Automatic};
OpenVDBSignedDistance[args___] /; !CheckArgs[OpenVDBSignedDistance[args], 2] = $Failed;
OpenVDBSignedDistance[args___] :=
With[{res = iOpenVDBSignedDistance[args]},
res /; res =!= $Failed
]
OpenVDBSignedDistance[args___] := messageCPTFunction[OpenVDBSignedDistance, args]
(* ::Subsubsection::Closed:: *)
(*iOpenVDBSignedDistance*)
(* ::Text:: *)
(*Skipping call to regimeConvert to avoid duplicating points when unnecessary.*)
Options[iOpenVDBSignedDistance] = Options[OpenVDBSignedDistance];
iOpenVDBSignedDistance[vdb_?OpenVDBScalarGridQ, pts_?coordinatesQ -> $worldregime, OptionsPattern[]] :=
Block[{isovalue, dists},
isovalue = gridIsoValue[OptionValue["IsoValue"], vdb];
(
dists = vdb["gridSignedDistance"[pts, isovalue]];
If[fogVolumeQ[vdb], Minus[dists], dists] /; VectorQ[dists, NumericQ]
) /; realQ[isovalue]
]
iOpenVDBSignedDistance[vdb_?OpenVDBScalarGridQ, pts_?coordinatesQ -> $indexregime, opts___] :=
iOpenVDBSignedDistance[vdb, regimeConvert[vdb, pts, $indexregime -> $worldregime] -> $worldregime, opts]
iOpenVDBSignedDistance[vdb_, pts_?coordinateQ -> regime_, opts___] :=
With[{res = iOpenVDBSignedDistance[vdb, {pts} -> regime, opts]},
res[[1]] /; VectorQ[res, NumericQ] && Length[res] === 1
]
iOpenVDBSignedDistance[vdb_, pts_List, opts___] := iOpenVDBSignedDistance[vdb, pts -> $worldregime, opts]
iOpenVDBSignedDistance[___] = $Failed;
(* ::Subsubsection::Closed:: *)
(*Argument conform & completion*)
registerForLevelSet[iOpenVDBSignedDistance, 1];
SyntaxInformation[OpenVDBSignedDistance] = {"ArgumentsPattern" -> {_, _, OptionsPattern[]}};
OpenVDBDefaultSpace[OpenVDBSignedDistance] = $worldregime;
(* ::Section:: *)
(*Balls*)
(* ::Subsection::Closed:: *)
(*OpenVDBFillWithBalls*)
(* ::Subsubsection::Closed:: *)
(*Main*)
Options[OpenVDBFillWithBalls] = {"IsoValue" -> Automatic, "Overlapping" -> False, "ReturnType" -> Automatic, "SeedCount" -> Automatic};
OpenVDBFillWithBalls[args___] /; !CheckArgs[OpenVDBFillWithBalls[args], {2, 3}] = $Failed;
OpenVDBFillWithBalls[args___] :=
With[{res = iOpenVDBFillWithBalls[args]},
res /; res =!= $Failed
]
OpenVDBFillWithBalls[args___] := mOpenVDBFillWithBalls[args]
(* ::Subsubsection::Closed:: *)
(*iOpenVDBFillWithBalls*)
Options[iOpenVDBFillWithBalls] = Options[OpenVDBFillWithBalls];
iOpenVDBFillWithBalls[vdb_?OpenVDBScalarGridQ, n_Integer?Positive, rspec_, OptionsPattern[]] :=
Block[{parsedrspec, isovalue, overlappingQ, rettype, seedcnt, rmin, rmax, res},
parsedrspec = parseRadiiSpec[vdb, rspec];
isovalue = gridIsoValue[OptionValue["IsoValue"], vdb];
overlappingQ = TrueQ[OptionValue["Overlapping"]];
rettype = parseBallReturnType[OptionValue["ReturnType"]];
seedcnt = parseBallSeedCount[OptionValue["SeedCount"]];
(
{rmin, rmax} = parsedrspec;
res = vdb["fillWithBalls"[1, n, overlappingQ, rmin, rmax, isovalue, seedcnt]];
returnBalls[res, rettype] /; MatrixQ[res]
) /; parsedrspec =!= $Failed && realQ[isovalue] && rettype =!= $Failed && seedcnt > 0
]
iOpenVDBFillWithBalls[vdb_?OpenVDBScalarGridQ, n_, opts:OptionsPattern[]] :=
iOpenVDBFillWithBalls[vdb, n, {0.5vdb["VoxelSize"], \[Infinity]} -> $worldregime, opts]
iOpenVDBFillWithBalls[___] = $Failed;
(* ::Subsubsection::Closed:: *)
(*Argument conform & completion*)
registerForLevelSet[iOpenVDBFillWithBalls, 1];
SyntaxInformation[OpenVDBFillWithBalls] = {"ArgumentsPattern" -> {_, _, _., OptionsPattern[]}};
OpenVDBDefaultSpace[OpenVDBFillWithBalls] = $worldregime;
(* ::Subsubsection::Closed:: *)
(*Utilities*)
parseRadiiSpec[vdb_, rspec:Except[_Rule]] := parseRadiiSpec[vdb, rspec -> $worldregime]
parseRadiiSpec[vdb_, (r_?NumericQ) -> regime_] := parseRadiiSpec[vdb, {0.5vdb["VoxelSize"], r} -> regime]
parseRadiiSpec[vdb_, {rmin_, rmax_} -> regime_?regimeQ] /; rmin <= rmax :=
Block[{bds, \[Delta], rcap},
bds = vdb["IndexDimensions"];
\[Delta] = vdb["VoxelSize"];
(
rcap = Round[0.5*Last[bds] + 3];
Clip[regimeConvert[vdb, {rmin, rmax}, regime -> $indexregime], {0.0, rcap}]
) /; ListQ[bds] && Positive[\[Delta]]
]
parseRadiiSpec[___] = $Failed;
(* default on the openvdb value side *)
parseBallSeedCount[Automatic] = 10000;
parseBallSeedCount[n_Integer?Positive] := n
parseBallSeedCount[___] = $Failed;
parseBallReturnType[ret:("Regions" | "Balls")] = "Regions";
parseBallReturnType[ret:("PackedArray" | "Packed")] = "PackedArray";
parseBallReturnType[Automatic] = parseBallReturnType["Regions"];
parseBallReturnType[___] = $Failed
returnBalls[balls_, ret_] :=
Block[{balls2},
balls2 = If[balls[[-1, 4]] == 0.0,
DeleteDuplicates[balls],
balls
];
If[ret === "PackedArray",
balls2,
If[#4 == 0.0,
Point[{#1, #2, #3}],
Ball[{#1, #2, #3}, #4]
]& @@@ balls2
]
]
(* ::Subsubsection::Closed:: *)
(*Messages*)
Options[mOpenVDBFillWithBalls] = Options[OpenVDBFillWithBalls];
mOpenVDBFillWithBalls[expr_, ___] /; messageScalarGridQ[expr, OpenVDBFillWithBalls] = $Failed;
mOpenVDBFillWithBalls[vdb_, expr_, rest___] /; !IntegerQ[expr] || !TrueQ[expr > 0] :=
(
Message[OpenVDBFillWithBalls::intpm, HoldForm[OpenVDBFillWithBalls[vdb, expr, rest]], 2];
$Failed
)
mOpenVDBFillWithBalls[vdb_, n_, rspec_List -> regime_, args___] := messageRegimeSpecQ[regime, OpenVDBFillWithBalls] || mOpenVDBFillWithBalls[vdb, n, rspec, args]
mOpenVDBFillWithBalls[vdb_, n_, rspec_, rest___] /; !MatchQ[rspec, {a_, b_} /; 0 <= a <= b] :=
(
Message[OpenVDBFillWithBalls::rspec, 3, HoldForm[OpenVDBFillWithBalls[vdb, n, rspec, rest]]];
$Failed
)
mOpenVDBFillWithBalls[args__, Longest[OptionsPattern[]]] :=
(
If[messageIsoValueQ[OptionValue["IsoValue"], OpenVDBFillWithBalls],
Return[$Failed]
];
If[parseBallReturnType[OptionValue["ReturnType"]] === $Failed,
Message[OpenVDBFillWithBalls::rettype];
Return[$Failed]
];
If[parseBallSeedCount[OptionValue["SeedCount"]] === $Failed,
Message[OpenVDBFillWithBalls::seedcnt];
Return[$Failed]
];
$Failed
)
mOpenVDBFillWithBalls[___] = $Failed;
OpenVDBFillWithBalls::rspec = "`1` at position `2` should be a list of two increasing non\[Hyphen]negative radii.";
OpenVDBFillWithBalls::rettype = "The setting for \"ReturnType\" should be one of \"Regions\", \"PackedArray\", or Automatic.";
OpenVDBFillWithBalls::seedcnt = "The setting for \"SeedCount\" should either be a positive integer or Automatic.";
(* ::Section:: *)
(*Utilities*)
(* ::Subsection::Closed:: *)
(*messageCPTFunction*)
Options[messageCPTFunction] = Options[OpenVDBMember];
messageCPTFunction[head_, expr_, ___] /; messageScalarGridQ[expr, head] = $Failed;
messageCPTFunction[head_, _, expr_, ___] /; messageCoordinateSpecQ[expr, head] = $Failed;
messageCPTFunction[head_, args__, Longest[OptionsPattern[]]] :=
(
If[messageIsoValueQ[OptionValue["IsoValue"], head],
Return[$Failed]
];
$Failed
)
messageCPTFunction[___] = $Failed;