(* ::Package:: *) (* ::Title:: *) (*OpenVDBLink*) (* ::Subtitle:: *) (*Greg Hurst, United Therapeutics*) (* ::Subsubtitle:: *) (*ghurst588@gmail.com*) (* ::Subsubtitle:: *) (*2021 - 2022*) (* ::Text:: *) (*Copyright Contributors to the OpenVDB Project*) (*SPDX-License-Identifier: Apache-2.0*) (* ::Section:: *) (*Initialization & Usage*) Package["OpenVDBLink`"] PackageImport["OpenVDBLink`LTemplate`"] PackageExport["$OpenVDBLibrary"] PackageExport["$OpenVDBInstallationDirectory"] PackageExport["OpenVDBDocumentation"] $OpenVDBLibrary::usage = "$OpenVDBLibrary is the full path to the OpenVDB Library loaded by OpenVDBLink."; $OpenVDBInstallationDirectory::usage = "$OpenVDBInstallationDirectory gives the top-level directory in which your OpenVDB installation resides."; OpenVDBDocumentation::usage = "OpenVDBDocumentation[] opens the OpenVDBLink documentation.\nOpenVDBDocumentation[\"Web\"] opens the OpenVDBLink in a web browser."; OpenVDBLink`Developer`Recompile::usage = "OpenVDBLink`Developer`Recompile[] recompiles the OpenVDB library and reloads the functions."; (* ::Section:: *) (*Path Variables*) (* ::Subsection::Closed:: *) (*Paths*) $packageDirectory = DirectoryName[$InputFileName]; $systemID = $SystemID; $libraryDirectory = FileNameJoin[{$packageDirectory, "LibraryResources", $systemID}]; $sourceDirectory = FileNameJoin[{$packageDirectory, "Source", "ExplicitGrids"}]; $unitTestDirectory = FileNameJoin[{$packageDirectory, "UnitTests"}]; $buildSettingsFile = FileNameJoin[{$packageDirectory, "BuildSettings.m"}]; (* ::Subsection::Closed:: *) (*Load build settings*) Get[$buildSettingsFile]; $LibraryPath = DeleteDuplicates[Prepend[$LibraryPath, $libraryDirectory]]; (* ::Subsection::Closed:: *) (*$OpenVDBLibrary & $OpenVDBInstallationDirectory*) $OpenVDBLibrary = FileNameJoin[{$libraryDirectory, $libraryName}]; $OpenVDBInstallationDirectory = $packageDirectory; (* ::Section:: *) (*OpenVDB library function template*) (* ::Subsection::Closed:: *) (*Class data declarations*) PackageScope["$scalarType"] PackageScope["$integerType"] PackageScope["$vectorType"] PackageScope["$booleanType"] PackageScope["$maskType"] PackageScope["$classTypeList"] PackageScope["$GridClassData"] $scalarType = "Scalar"; $integerType = "Integer"; $vectorType = "Vector"; $booleanType = "Boolean"; $maskType = "Mask"; $classTypeList = {$scalarType, $vectorType, $integerType, $booleanType, $maskType}; $GridClassData = <| $scalarType -> <| "Double" -> <|"ClassName" -> "OpenVDBDoubleGrid", "TreeName" -> "Tree_double_5_4_3", "WLBaseType" -> Real, "PixelClass" -> True|>, "Float" -> <|"ClassName" -> "OpenVDBFloatGrid", "TreeName" -> "Tree_float_5_4_3", "WLBaseType" -> Real, "PixelClass" -> True, "Alias" -> "Scalar"|> |>, $integerType -> <| "Byte" -> <|"ClassName" -> "OpenVDBByteGrid", "TreeName" -> "Tree_uint8_5_4_3", "WLBaseType" -> Integer, "PixelClass" -> True|>, "Int32" -> <|"ClassName" -> "OpenVDBInt32Grid", "TreeName" -> "Tree_int32_5_4_3", "WLBaseType" -> Integer|>, "Int64" -> <|"ClassName" -> "OpenVDBInt64Grid", "TreeName" -> "Tree_int64_5_4_3", "WLBaseType" -> Integer|>, "UInt32" -> <|"ClassName" -> "OpenVDBUInt32Grid", "TreeName" -> "Tree_uint32_5_4_3", "WLBaseType" -> Integer|> |>, $vectorType -> <| "Vec2D" -> <|"ClassName" -> "OpenVDBVec2DGrid", "TreeName" -> "Tree_vec2d_5_4_3", "WLBaseType" -> Real|>, "Vec2I" -> <|"ClassName" -> "OpenVDBVec2IGrid", "TreeName" -> "Tree_vec2i_5_4_3", "WLBaseType" -> Integer|>, "Vec2S" -> <|"ClassName" -> "OpenVDBVec2SGrid", "TreeName" -> "Tree_vec2s_5_4_3", "WLBaseType" -> Real|>, "Vec3D" -> <|"ClassName" -> "OpenVDBVec3DGrid", "TreeName" -> "Tree_vec3d_5_4_3", "WLBaseType" -> Real|>, "Vec3I" -> <|"ClassName" -> "OpenVDBVec3IGrid", "TreeName" -> "Tree_vec3i_5_4_3", "WLBaseType" -> Integer|>, "Vec3S" -> <|"ClassName" -> "OpenVDBVec3SGrid", "TreeName" -> "Tree_vec3s_5_4_3", "WLBaseType" -> Real, "Alias" -> "Vector"|> |>, $booleanType -> <| $booleanType -> <|"ClassName" -> "OpenVDBBoolGrid", "TreeName" -> "Tree_bool_5_4_3", "WLBaseType" -> Integer, "PixelClass" -> True|> |>, $maskType -> <| $maskType -> <|"ClassName" -> "OpenVDBMaskGrid", "TreeName" -> "Tree_mask_5_4_3", "PixelClass" -> True|> |> |>; $scalarClass = "OpenVDBFloatGrid"; $colorVectorClass = "OpenVDBVec3SGrid"; Scan[(pixelClassQ[#["ClassName"]] = TrueQ[#["PixelClass"]])&, Values[Join @@ $GridClassData]]; KeyValueMap[(pixelTypeQ[#1] = TrueQ[#2["PixelClass"]])&, Join @@ $GridClassData]; (* ::Subsection::Closed:: *) (*Types*) (* ::Subsubsection::Closed:: *) (*Types*) PackageScope["$gridTypeList"] PackageScope["$internalTypeList"] PackageScope["fromInternalType"] PackageScope["typeClass"] PackageScope["aliasTypeQ"] PackageScope["resolveAliasType"] Block[{alltypes, typetreegroups}, alltypes = Join @@ Values[$GridClassData]; typetreegroups = Join[ {#["Alias"], #["TreeName"], #["ClassName"]}& /@ Values[alltypes], KeyValueMap[{#1, #2["TreeName"], #2["ClassName"]}&, alltypes] ]; typetreegroups = Transpose[DeleteMissing[typetreegroups, 1, 2]]; $gridTypeList = typetreegroups[[1]]; $internalTypeList = typetreegroups[[2]]; Clear[typeClass]; Block[{$Context = "OpenVDBLink`LTemplate`Classes`"}, MapThread[(typeClass[#1] = Symbol[#3])&, typetreegroups]; ]; KeyValueMap[Function[{k, v}, If[!MissingQ[v["Alias"]], aliasTypeQ[v["Alias"]] = True; resolveAliasType[v["Alias"]] = k; ] ], alltypes]; aliasTypeQ[_] = False; ] toInternalType = AssociationThread[$gridTypeList, $internalTypeList]; fromInternalType = AssociationThread[$internalTypeList, $gridTypeList]; (* ::Subsubsection::Closed:: *) (*typeGridName*) PackageScope["typeGridName"] Clear[typeGridName]; KeyValueMap[Function[{k, v}, typeGridName[k] = v["ClassName"]], Join @@ Values[$GridClassData]]; (* ::Subsubsection::Closed:: *) (*gridType*) PackageScope["gridType"] Clear[gridType]; KeyValueMap[Function[{k, v}, gridType[v["ClassName"]] = k], Join @@ Values[$GridClassData]]; (* ::Subsection::Closed:: *) (*$OpenVDBTemplate*) (* ::Subsubsection::Closed:: *) (*BaseGrid template*) LVDBBaseGridClass[class_String, members_List] := LClass[class, Join[vdbBaseGridMembers[class], vdbBaseGridPixelMembers[class], members]] vdbBaseGridMembers[class_] := { (* ------------ creation, deletion ------------ *) LFun["createEmptyGrid", {}, "Void"], LFun["deleteGrid", {}, "Void"], LFun["copyGrid", {LExpressionID[class]}, "Void"], (* ------------ IO ------------ *) LFun["importVDBType", {"UTF8String", "UTF8String"}, "UTF8String"], LFun["importVDB", {"UTF8String", "UTF8String"}, "Boolean"], LFun["exportVDB", {"UTF8String"}, "Void"], (* ------------ setters ------------ *) LFun["setActiveStates", {{Integer, 2, "Constant"}, {Integer, 1, "Constant"}}, "Void"], LFun["setGridClass", {Integer}, "Void"], LFun["setCreator", {"UTF8String"}, "Void"], LFun["setName", {"UTF8String"}, "Void"], LFun["setVoxelSize", {Real}, "Void"], (* ------------ getters ------------ *) LFun["getActiveStates", {{Integer, 2, "Constant"}}, {Integer, 1}], LFun["getActiveLeafVoxelCount", {}, Integer], LFun["getActiveTileCount", {}, Integer], LFun["getActiveVoxelCount", {}, Integer], LFun["getGridClass", {}, Integer], LFun["getCreator", {}, "UTF8String"], LFun["getGridBoundingBox", {}, {Integer, 2}], LFun["getGridDimensions", {}, {Integer, 1}], LFun["getGridType", {}, "UTF8String"], LFun["getHasUniformVoxels", {}, "Boolean"], LFun["getIsEmpty", {}, "Boolean"], LFun["getMemoryUsage", {}, Integer], LFun["getName", {}, "UTF8String"], LFun["getVoxelSize", {}, Real], (* ------------ CSG ------------ *) LFun["gridMax", {LExpressionID[class]}, "Void"], LFun["gridMin", {LExpressionID[class]}, "Void"], (* ------------ Metadata ------------ *) LFun["getBooleanMetadata", {"UTF8String"}, "Boolean"], LFun["getIntegerMetadata", {"UTF8String"}, Integer], LFun["getRealMetadata", {"UTF8String"}, Real], LFun["getStringMetadata", {"UTF8String"}, "UTF8String"], LFun["setBooleanMetadata", {"UTF8String", "Boolean"}, "Void"], LFun["setStringMetadata", {"UTF8String", "UTF8String"}, "Void"], LFun["setDescription", {"UTF8String"}, "Void"], (* ------------ grid transformation ------------ *) LFun["transformGrid", {LExpressionID[class], {Real, 2, "Constant"}, Integer}, "Void"], (* ------------ aggregate data ------------ *) LFun["sliceVoxelCounts", {Integer, Integer}, {Integer, 1}], LFun["activeTiles", {{Integer, 2, "Constant"}, "Boolean"}, {Integer, 3}], LFun["activeVoxelPositions", {{Integer, 2, "Constant"}}, {Integer, 2}] }; vdbBaseGridPixelMembers[class_?pixelClassQ] := { (* ------------ Image ------------ *) LFun["depthMap", {{Integer, 2, "Constant"}, Real, Real, Real}, LType["Image", "Real32"]], LFun["gridSliceImage", {Integer, {Integer, 2, "Constant"}, "Boolean", "Boolean"}, LType["Image"]], LFun["gridImage3D", {{Integer, 2, "Constant"}}, LType["Image3D"]] } vdbBaseGridPixelMembers[___] = {}; (* ::Subsubsection::Closed:: *) (*BaseNumericGrid template*) LVDBBaseNumericGrid[class_String, type_, rank_, members_List] := LClass[ class, Join[ vdbBaseGridMembers[class], vdbBaseGridPixelMembers[class], vdbBaseNumericGridMembers[class, type, rank], members ] ] vdbBaseNumericGridMembers[class_, type_, rank_] := With[{ $scalarinput = scalarInput[type, rank], $scalaroutput = scalarOutput[type, rank], $vectorinput = vectorInput[type, rank], $vectoroutput = vectorOutput[type, rank], $matrixinput = matrixInput[type, rank], $matrixoutput = matrixOutput[type, rank], $cubeinput = cubeInput[type, rank], $cubeoutput = cubeOutput[type, rank] }, { (* ------------ setters ------------ *) LFun["setBackgroundValue", {$scalarinput}, "Void"], LFun["setValues", {{Integer, 2, "Constant"}, $vectorinput}, "Void"], (* ------------ getters ------------ *) LFun["getBackgroundValue", {}, $scalaroutput], LFun["getMinMaxValues", {}, $vectoroutput], LFun["getValues", {{Integer, 2, "Constant"}}, $vectoroutput], (* ------------ aggregate data ------------ *) LFun["sliceVoxelValueTotals", {Integer, Integer}, $vectoroutput], LFun["activeVoxelValues", {{Integer, 2, "Constant"}}, $vectoroutput], LFun["gridSlice", {Integer, {Integer, 2, "Constant"}, "Boolean", "Boolean"}, $matrixoutput], LFun["gridData", {{Integer, 2, "Constant"}}, $cubeoutput] } ]; scalarInput[type_, 0] := type scalarInput[type_, rank_] := {type, rank, "Constant"} vectorInput[type_, rank_] := {type, rank+1, "Constant"} matrixInput[type_, rank_] := {type, rank+2, "Constant"} cubeInput[type_, rank_] := {type, rank+3, "Constant"} scalarOutput[type_, 0] := type scalarOutput[type_, rank_] := {type, rank} vectorOutput[type_, rank_] := {type, rank+1} matrixOutput[type_, rank_] := {type, rank+2} cubeOutput[type_, rank_] := {type, rank+3} (* ::Subsubsection::Closed:: *) (*ScalarGrid template*) LVDBScalarGridClass[class_, type_] := LVDBBaseNumericGrid[class, type, 0, { (* ------------ getters ------------ *) LFun["getHalfwidth", {}, Real], (* ------------ CSG ------------ *) LFun["gridUnion", {LExpressionID[class]}, "Void"], LFun["gridIntersection", {LExpressionID[class]}, "Void"], LFun["gridDifference", {LExpressionID[class]}, "Void"], LFun["gridUnionCopy", {{Integer, 1, "Constant"}}, "Void"], LFun["gridIntersectionCopy", {{Integer, 1, "Constant"}}, "Void"], LFun["gridDifferenceCopy", {LExpressionID[class], LExpressionID[class]}, "Void"], LFun["clipGrid", {LExpressionID[class], {Real, 2, "Constant"}}, "Void"], (* ------------ level set creation ------------ *) LFun["ballLevelSet", {{Real, 1, "Constant"}, Real, Real, Real, "Boolean"}, "Void"], LFun["cuboidLevelSet", {{Real, 2, "Constant"}, Real, Real, "Boolean"}, "Void"], LFun["meshLevelSet", {{Real, 2, "Constant"}, {Integer, 2, "Constant"}, Real, Real, "Boolean"}, "Void"], LFun["offsetSurfaceLevelSet", {{Real, 2, "Constant"}, {Integer, 2, "Constant"}, Real, Real, Real, "Boolean"}, "Void"], (* ------------ level set measure ------------ *) LFun["levelSetGridArea", {}, Real], LFun["levelSetGridEulerCharacteristic", {}, Integer], LFun["levelSetGridGenus", {}, Integer], LFun["levelSetGridVolume", {}, Real], (* ------------ distance measure ------------ *) LFun["gridMember", {{Integer, 2, "Constant"}, Real}, {Integer, 1}], LFun["gridNearest", {{Real, 2, "Constant"}, Real}, {Real, 2}], LFun["gridDistance", {{Real, 2, "Constant"}, Real}, {Real, 1}], LFun["gridSignedDistance", {{Real, 2, "Constant"}, Real}, {Real, 1}], LFun["fillWithBalls", {Integer, Integer, "Boolean", Real, Real, Real, Integer}, {Real, 2}], (* ------------ filters ------------ *) LFun["filterGrid", {Integer, Integer, Integer}, "Void"], (* ------------ mesh creation ------------ *) LFun["meshCellCount", {Real, Real, "Boolean"}, {Integer, 1}], LFun["meshData", {Real, Real, "Boolean"}, {Real, 1}], (* ------------ fog volume ------------ *) LFun["levelSetToFogVolume", {Real}, "Void"], (* ------------ grid transformation ------------ *) LFun["scalarMultiply", {Real}, "Void"], LFun["gammaAdjustment", {Real}, "Void"], (* ------------ morphology ------------ *) LFun["resizeBandwidth", {Real}, "Void"], LFun["offsetLevelSet", {Real}, "Void"], (* ------------ render ------------ *) LFun["renderGrid", {Real, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, Integer, Integer, Integer, {Integer, 1, "Constant"}, Real, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, "Boolean"}, LType["Image", "Byte"]], LFun["renderGridPBR", {Real, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, Integer, Integer, {Integer, 1, "Constant"}, Real, "Boolean", {Real, 1}, {Real, 1}, {Real, 1}, Real, Real, Real, Real, {Real, 1}, Real, Real, Real, Real, Real, Real }, LType["Image", "Byte"]], (*LFun["renderGridVectorColor", {Real, LExpressionID[$colorVectorClass], LExpressionID[$colorVectorClass], LExpressionID[$colorVectorClass], {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, Integer, Integer, Integer, {Integer, 1, "Constant"}, Real, {Real, 1, "Constant"}, {Real, 1, "Constant"}, {Real, 1, "Constant"}, "Boolean"}, LType["Image", "Byte"]],*) (* ------------ aggregate data ------------ *) LFun["activeVoxels", {{Integer, 2, "Constant"}}, LType[SparseArray, Real, 3]] } ] (* ::Subsubsection::Closed:: *) (*IntegerGrid template*) LVDBIntegerGridClass[class_, type_] := LVDBBaseNumericGrid[class, type, 0, { (* ------------ aggregate data ------------ *) LFun["activeVoxels", {{Integer, 2, "Constant"}}, LType[SparseArray, Integer, 3]] } ] (* ::Subsubsection::Closed:: *) (*VectorGrid template*) LVDBVectorGridClass[class_, type_] := LVDBBaseNumericGrid[class, type, 1, { } ] (* ::Subsubsection::Closed:: *) (*BoolGrid template*) $booleanBlackList = {"sliceVoxelValueTotals"}; deleteNonBooleanFuncs[class_] := class /. {LFun[Alternatives @@ $booleanBlackList, ___] -> Nothing} LVDBBoolGridClass = With[{ class = $GridClassData[$booleanType, $booleanType, "ClassName"], type = $GridClassData[$booleanType, $booleanType, "WLBaseType"] }, deleteNonBooleanFuncs @ LVDBBaseNumericGrid[class, type, 0, { (* ------------ aggregate data ------------ *) LFun["activeVoxels", {{Integer, 2, "Constant"}}, LType[SparseArray, Integer, 3]] } ] ]; (* ::Subsubsection::Closed:: *) (*MaskGrid template*) LVDBMaskGridClass = LVDBBaseGridClass[$GridClassData[$maskType, $maskType, "ClassName"], { } ]; (* ::Subsubsection::Closed:: *) (*Main template*) $OpenVDBTemplate = LTemplate["OpenVDBLink", Join[ LVDBScalarGridClass [#ClassName, #WLBaseType]& /@ Values[$GridClassData[$scalarType]], LVDBIntegerGridClass[#ClassName, #WLBaseType]& /@ Values[$GridClassData[$integerType]], LVDBVectorGridClass [#ClassName, #WLBaseType]& /@ Values[$GridClassData[$vectorType]], { LVDBBoolGridClass, LVDBMaskGridClass } ] ]; (* ::Section:: *) (*Loader & Compiler*) (* ::Subsection::Closed:: *) (*Recompile*) OpenVDBLink`Developer`Recompile::build = "No build settings found. Please check BuildSettings.m."; OpenVDBLink`Developer`Recompile[printQ_:False] := ( If[$buildSettings === None, Message[OpenVDBLink`Developer`Recompile::build]; Return[$Failed] ]; If[!DirectoryQ[$libraryDirectory], CreateDirectory[$libraryDirectory] ]; SetDirectory[$sourceDirectory]; CompileTemplate[ $OpenVDBTemplate, {}, Sequence @@ If[TrueQ[printQ], {"ShellCommandFunction" -> Print, "ShellOutputFunction" -> Print}, {} ], "CleanIntermediate" -> True, "TargetDirectory" -> $libraryDirectory, Sequence @@ $buildSettings ]; ResetDirectory[]; LoadOpenVDBLink[] ) (* ::Subsection::Closed:: *) (*LoadOpenVDBLink*) LoadOpenVDBLink[] := Module[{deps}, deps = FileNameJoin[{$libraryDirectory, "dependencies.m"}]; Check[ If[FileExistsQ[deps], Get[deps]], Return[$Failed] ]; If[Quiet @ LoadTemplate[$OpenVDBTemplate] === $Failed, Return[$Failed] ]; ] (* ::Subsection::Closed:: *) (*Call to LoadOpenVDBLink*) If[!TrueQ[$templateLoaded], If[LoadOpenVDBLink[] === $Failed, OpenVDBLink`Developer`Recompile[] ]; $templateLoaded = True; ] (* ::Section:: *) (*Unit Testing*) (* ::Subsection::Closed:: *) (*TestOpenVDBLink*) (* ::Subsubsection::Closed:: *) (*Main*) OpenVDBLink`Developer`TestOpenVDBLink[iareas_:All] := Block[{wlts, areas, filepattern, torun, results, final, passcnt, failcnt, percentage, totaltime, stats}, wlts = FileNames[$wltFilePattern]; areas = gridAreas[iareas]; ( filepattern = Alternatives @@ areas; torun = Select[wlts, StringMatchQ[FileBaseName[#], filepattern]&]; results = Map[ (PrintTemporary[FileBaseName[#]]; TestReport[#, SameTest -> sameExpression])&, torun ]; ( final = AssociationThread[FileBaseName /@ torun, results]; passcnt = Total[#["TestsSucceededCount"]& /@ results]; failcnt = Total[#["TestsFailedCount"]& /@ results]; percentage = N[Quiet[Divide[passcnt, passcnt + failcnt]]]; totaltime = Total[#["TimeElapsed"]& /@ results]; stats = Association[{ "TestsSucceededCount" -> passcnt, "TestsFailedCount" -> failcnt, "SuccessRate" -> percentage, "TimeElapsed" -> totaltime }]; Association[{"TestResults" -> final, "GlobalStatistics" -> stats}] ) /; MatchQ[results, {__TestReportObject}] ) /; VectorQ[areas, StringQ] ] OpenVDBLink`Developer`TestOpenVDBLink[___] = $Failed; (* ::Subsubsection::Closed:: *) (*Utilities*) $wltFilePattern = FileNameJoin[{$unitTestDirectory, "wlt", "*.wlt"}]; $testAreaList = FileBaseName /@ FileNames[$wltFilePattern]; gridAreas[types_] := DeleteDuplicates[Flatten[iGridAreas /@ Flatten[{types}]]] iGridAreas[All] := $testAreaList iGridAreas[area_] /; MemberQ[$testAreaList, area] := area iGridAreas[___] = $Failed; (* ::Text:: *) (*SameQ with a fuzzy tolerance on inexact numbers. The idea is that machine numbers can vary across machines.*) (*Assumes no evaluation leaks can happen and ignores Orderless pattern matching.*) sameExpression[expr1_, expr2_] := Block[{Internal`$EqualTolerance = 11, Internal`$SameQTolerance = 11}, expr1 === expr2 ] (* ::Subsubsection::Closed:: *) (*Argument conform & completion*) SyntaxInformation[OpenVDBLink`Developer`TestOpenVDBLink] = {"ArgumentsPattern" -> {_.}}; addCodeCompletion["OpenVDBLink`Developer`TestOpenVDBLink"][iGridAreas[All]]; (* ::Subsection::Closed:: *) (*WLTToNotebook*) (* ::Text:: *) (*Specialized version of https://resources.wolframcloud.com/FunctionRepository/resources/WLTToNotebook/*) (* ::Subsubsection::Closed:: *) (*Main*) OpenVDBLink`Developer`WLTToNotebook[unitTestName_] := Enclose @ Module[{ file, heldContents, cellids, cells }, file = FileNameJoin[{ $OpenVDBInstallationDirectory, "UnitTests", "wlt", FileBaseName[unitTestName] <> ".wlt" }]; ConfirmBy[file, FileExistsQ, "File does not exist"]; ConfirmBy[ToLowerCase @ FileExtension[file], MatchQ["wlt" | "mt"], "File extension is not .wlt or .mt"]; Block[{$Context, $ContextPath, noTitleYetQ = True}, Needs["MUnit`"]; heldContents = Confirm[Import[file, {"WL", "HeldExpressions"}], "Import error"]; cellids = CreateDataStructure["HashSet"]; heldContents = testToCellGroup[#, cellids]& /@ heldContents; ]; cells = Cases[Flatten @ heldContents, _Cell]; cells = createCellGroup[cells, "Section"]; cells = Replace[cells, CellGroupData[l:{_[_, "Section"], __}, c___] :> CellGroupData[createCellGroup[l, "Subsection"], c], {1}]; NotebookPut @ Notebook[ cells, ShowGroupOpener -> True, TaggingRules -> Association["$testsRun" -> False], StyleDefinitions -> FrontEnd`FileName[ {"MUnit"}, "MUnit.nb", CharacterEncoding -> "UTF-8" ] ] ]; (* ::Subsubsection::Closed:: *) (*Utilities*) generateUniqueID[max_, hashTable_] := Module[{i = 0}, TimeConstrained[ While[True, i = RandomInteger[max]; If[ TrueQ @ hashTable["Insert", i], Break[] ] ], 2 ]; i ]; SetAttributes[testToCellGroup, HoldAllComplete]; (* Handle verification tests terminated by a ; *) testToCellGroup[ HoldComplete[CompoundExpression[expressions__]], rest___ ] := Map[ testToCellGroup[#, rest]&, Thread @ HoldComplete[{expressions}] ]; (* Handle 1-arg tests *) testToCellGroup[ HoldComplete[test : VerificationTest[fst_, {}, args___]], cellids_ ] /; Quiet @ CheckArgs[test, 1] := testToCellGroup[VerificationTest[fst, {}, {}], cellids]; (* Handle 1-arg tests *) testToCellGroup[ HoldComplete[test : VerificationTest[fst_, args___]], cellids_ ] /; Quiet @ CheckArgs[test, 1] := testToCellGroup[VerificationTest[fst, True, {}, args], cellids]; (* Handle 2-arg tests *) testToCellGroup[ HoldComplete[test : VerificationTest[fst_, snd_, args___]], cellids_ ] /; Quiet @ CheckArgs[test, 2] := testToCellGroup[VerificationTest[fst, snd, {}, args], cellids]; (* Handle 3-arg tests *) testToCellGroup[ HoldComplete[test_VerificationTest], cellids_ ] /; Quiet @ CheckArgs[test, 3] := testToCellGroup[test, cellids] (* Convert test to Cells *) testToCellGroup[ test : VerificationTest[in_, out_, msgs_, opts___], cellids_ ] := With[{ imax = 10^9 }, Cell @ CellGroupData[ { Cell[ BoxData @ MakeBoxes[in, StandardForm], "VerificationTest", CellID -> generateUniqueID[imax, cellids] ], Cell[ BoxData @ MakeBoxes[out, StandardForm], "ExpectedOutput", CellID -> generateUniqueID[imax, cellids] ], Cell[ BoxData @ MakeBoxes[msgs, StandardForm], "ExpectedMessage", CellID -> generateUniqueID[imax, cellids] ], Cell[ BoxData @ MakeBoxes[{opts}, StandardForm], "TestOptions", CellID -> generateUniqueID[imax, cellids] ], Cell[ BoxData @ ToBoxes @ MUnit`bottomCell[], "BottomCell", CellID -> generateUniqueID[imax, cellids] ] }, Open ] ]; testToCellGroup[HoldComplete[MUnit`BeginTestSection[section_String]], _] := Cell[section, sectionType[section]]; testToCellGroup[other_, _] := Nothing; sectionType[title_] /; noTitleYetQ := (noTitleYetQ = False; "Title"); sectionType[symbol_String] /; StringStartsQ[symbol, "Initialization" | (("$"...) ~~ "OpenVDB")] = "Subsection"; sectionType[_] = "Section"; createCellGroup[cells_, section_] := Block[{splitcells}, splitcells = SplitBy[cells, #[[-1]] === section&]; ( Prepend[ CellGroupData[Join[##], Closed]& @@@ Partition[Rest[splitcells], 2], splitcells[[1, 1]] ] ) /; OddQ[Length[splitcells]] && MatchQ[splitcells, {{_}, _, _, ___}] ]; createCellGroup[args___] := $Failed (* ::Subsubsection::Closed:: *) (*Argument conform & completion*) SyntaxInformation[OpenVDBLink`Developer`WLTToNotebook] = {"ArgumentsPattern" -> {_}}; addCodeCompletion["OpenVDBLink`Developer`WLTToNotebook"][iGridAreas[All]]; (* ::Section:: *) (*Utilities*) (* ::Subsection::Closed:: *) (*addCodeCompletion*) (* ::Text:: *) (*https://resources.wolframcloud.com/FunctionRepository/resources/AddCodeCompletion*) PackageScope["addCodeCompletion"] addCodeCompletion[sym_Symbol][args___] := addCodeCompletion[SymbolName[sym]][args] addCodeCompletion[function_String][args___] := With[{processed = {args} /. { None -> 0, "AbsoluteFileName" -> 2, "RelativeFileName" -> 3, "Color" -> 4, "PackageName" -> 7, "DirectoryName" -> 8, "InterpreterType" -> 9} }, FE`Evaluate[FEPrivate`AddSpecialArgCompletion[function -> processed]]; ] (* ::Subsection::Closed:: *) (*registerForLevelSet*) (* ::Subsubsection::Closed:: *) (*Main*) PackageScope["registerForLevelSet"] registerForLevelSet[func_Symbol, pos_:{{}}] := Block[{}, func[args__] := With[{res = tryToLevelSet[pos, func, args]}, res /; res =!= $Failed ] ] (* ::Subsubsection::Closed:: *) (*level set dispatch*) tryToLevelSet[pos_, func_, args__] /; validTryToLevelSetQ[] := Block[{levelsets, reps, res, $inToLevelSet = True}, levelsets = If[pos === {{}}, tryOpenVDBLevelSet /@ {args}, Quiet @ Extract[{args}, pos, tryOpenVDBLevelSet] ]; ( reps = Which[ pos === {{}}, pos -> levelsets, ListQ[levelsets], Thread[pos -> levelsets], True, pos -> levelsets ]; res = func @@ ReplacePart[{args}, reps]; res /; res =!= $Failed ) /; validScalarGridCollectionQ[levelsets] ] tryToLevelSet[___] := $Failed (* ::Subsubsection::Closed:: *) (*tryOpenVDBLevelSet*) tryOpenVDBLevelSet[vdb_?OpenVDBScalarGridQ] := vdb tryOpenVDBLevelSet[_?OpenVDBGridQ] = $Failed; tryOpenVDBLevelSet[{file_String?FileExistsQ, name_}] := Block[{vdb}, vdb = OpenVDBImport[file, name]; If[OpenVDBScalarGridQ[vdb], vdb, $Failed ] ] tryOpenVDBLevelSet[file_String] := tryOpenVDBLevelSet[{file, Automatic}] tryOpenVDBLevelSet[{File[file_], name_}] := tryOpenVDBLevelSet[{file, name}] tryOpenVDBLevelSet[File[file_]] := tryOpenVDBLevelSet[{file, Automatic}] tryOpenVDBLevelSet[opt_?OptionQ] := opt tryOpenVDBLevelSet[expr_] := With[{res = Quiet @ OpenVDBLevelSet[expr]}, res /; OpenVDBScalarGridQ[res] ] tryOpenVDBLevelSet[___] = $Failed; (* ::Subsubsection::Closed:: *) (*Utilities*) $inToLevelSet = False; validTryToLevelSetQ[] := TrueQ[!$inToLevelSet] && TrueQ[Positive[$OpenVDBSpacing]] && TrueQ[Positive[$OpenVDBHalfWidth]] validScalarGridCollectionQ[levelsets_List] := Length[levelsets] > 0 && VectorQ[levelsets, validScalarGridCollectionQ] validScalarGridCollectionQ[expr_] := OpenVDBScalarGridQ[expr] || OptionQ[expr] (* ::Subsection::Closed:: *) (*Argument checking*) PackageScope["CheckArgs"] SetAttributes[{CheckArgs, catchMessages}, HoldFirst] CheckArgs[expr_, spec_] := !FailureQ[catchMessages[System`Private`Arguments[expr, spec, HoldComplete, {}]]] catchMessages[expr_] := Catch[Internal`HandlerBlock[{"Message", Throw[$Failed, "iArgumentsMessage"]&}, expr], "iArgumentsMessage"]; (* ::Subsection::Closed:: *) (*halfWidth*) PackageScope["halfWidth"] halfWidth[vdb_] := vdb["getHalfwidth"[]] (* ::Subsection::Closed:: *) (*emptyVDBQ*) PackageScope["emptyVDBQ"] emptyVDBQ[vdb_] := TrueQ[vdb["getIsEmpty"[]]] (* ::Subsection::Closed:: *) (*voxelSize*) PackageScope["voxelSize"] voxelSize[vdb_] := vdb["getVoxelSize"[]] (* ::Subsection::Closed:: *) (*Grid class*) PackageScope["levelSetQ"] PackageScope["fogVolumeQ"] PackageScope["unknownClassQ"] PackageScope["gridClassID"] PackageScope["gridClassName"] PackageScope["$gridUnknown"] PackageScope["$gridLevelSet"] PackageScope["$gridFogVolume"] $gridUnknown = None; $gridLevelSet = "LevelSet"; $gridFogVolume = "FogVolume"; $gridClassTypes = {$gridUnknown, $gridLevelSet, $gridFogVolume}; $gridClassIDs = Range[0, Length[$gridClassTypes]-1]; MapThread[(gridClassID[#1] = #2)&, {$gridClassTypes, $gridClassIDs}]; gridClassID[_] = gridClassID[$gridUnknown]; MapThread[(gridClassName[#1] = #2)&, {$gridClassIDs, $gridClassTypes}]; gridClassName[_] = gridClassName[0]; (* ::Subsection::Closed:: *) (*Resampling methods*) PackageScope["resamplingMethod"] resamplingMethod["Nearest" | 0] = 0; resamplingMethod[Automatic | "Linear" | 1] = 1; resamplingMethod["Quadratic" | 2] = 2; resamplingMethod[___] = $Failed; (* ::Subsection::Closed:: *) (*Space regime*) PackageScope["$indexregime"] PackageScope["$worldregime"] PackageScope["regimeQ"] PackageScope["indexRegimeQ"] PackageScope["worldRegimeQ"] $indexregime = "Index"; $worldregime = "World"; regimeQ[$indexregime] = True; regimeQ[$worldregime] = True; regimeQ[_] = False; indexRegimeQ[$indexregime] = True; indexRegimeQ[_] = False; worldRegimeQ[$worldregime] = True; worldRegimeQ[_] = False; PackageScope["regimeConvert"] regimeConvert[vdb_, vals_, fromregime_ -> toregime_, dim_:1] := Which[ indexRegimeQ[fromregime] && fromregime === toregime, Round[vals], fromregime === toregime, vals, indexRegimeQ[fromregime], (* convert index space values to world space values *) vals * voxelSize[vdb]^dim, True, (* convert world space values to index space values *) Round[Divide[vals, voxelSize[vdb]^dim]] ] (* ::Subsection::Closed:: *) (*realQ*) PackageScope["realQ"] realQ = Internal`RealValuedNumericQ; (* ::Subsection::Closed:: *) (*Bounds & coordinates*) PackageScope["intervalQ"] intervalQ[int_] := VectorQ[int, realQ] && Length[int] === 2 && LessEqual @@ int PackageScope["bounds2DQ"] PackageScope["bounds3DQ"] bounds2DQ[bds_] := MatrixQ[bds, realQ] && Dimensions[bds] === {2, 2} && And @@ LessEqual @@@ bds bounds3DQ[bds_] := MatrixQ[bds, realQ] && Dimensions[bds] === {3, 2} && And @@ LessEqual @@@ bds PackageScope["coordinatesQ"] PackageScope["coordinateQ"] coordinatesQ[coords_] := MatrixQ[coords, realQ] && Length[coords[[1]]] == 3 coordinatesQ[___] = False; coordinateQ[coords_] := VectorQ[coords, realQ] && Length[coords] == 3 coordinateQ[___] = False; (* ::Subsection::Closed:: *) (*gridIsoValue*) PackageScope["gridIsoValue"] gridIsoValue[x_?realQ, _] := x gridIsoValue[Automatic, _?fogVolumeQ] = 0.5; gridIsoValue[Automatic, _] = 0.0; gridIsoValue[___] = $Failed; (* ::Subsection::Closed:: *) (*Same structures*) PackageScope["sameGridTypeQ"] PackageScope["sameVoxelSizeQ"] sameGridTypeQ[vdbs__?OpenVDBGridQ] := SameQ @@ (#["GridType"]& /@ {vdbs}) sameGridTypeQ[___] = False; sameVoxelSizeQ[vdbs__?OpenVDBGridQ] := SameQ @@ (#["VoxelSize"]& /@ {vdbs}) sameVoxelSizeQ[___] = False; (* ::Subsection::Closed:: *) (*internal GridQ functions*) PackageScope["levelSetQ"] PackageScope["fogVolumeQ"] PackageScope["unknownClassQ"] PackageScope["numericGridQ"] PackageScope["carefulNumericGridQ"] PackageScope["nonMaskGridQ"] PackageScope["carefulNonMaskGridQ"] PackageScope["pixelGridQ"] PackageScope["carefulPixelGridQ"] With[{lid = gridClassID[$gridLevelSet], fid = gridClassID[$gridFogVolume]}, levelSetQ[vdb_] := vdb["getGridClass"[]] === lid; fogVolumeQ[vdb_] := vdb["getGridClass"[]] === fid; unknownClassQ[vdb_] := vdb["getGridClass"[]] =!= lid && vdb["getGridClass"[]] =!= fid; ] nonMaskGridQ[vdb_] := vdb[[2]] =!= $maskType carefulNonMaskGridQ[vdb_] := OpenVDBGridQ[vdb] && nonMaskGridQ[vdb] pixelGridQ[vdb_] := pixelTypeQ[vdb[[2]]] carefulPixelGridQ[vdb_] := OpenVDBGridQ[vdb] && pixelGridQ[vdb] (* ::Section:: *) (*Documentation*) (* ::Subsection::Closed:: *) (*OpenVDBDocumentation*) (* ::Subsubsection::Closed:: *) (*OpenVDBDocumentation*) OpenVDBDocumentation[args___] /; !CheckArgs[OpenVDBDocumentation[args], {0, 1}] = $Failed; OpenVDBDocumentation[args___] := With[{res = iOpenVDBDocumentation[args]}, res /; res =!= $Failed ] OpenVDBDocumentation[args___] := mOpenVDBDocumentation[args] (* ::Subsubsection::Closed:: *) (*iOpenVDBDocumentation*) iOpenVDBDocumentation[] := iOpenVDBDocumentation["Notebook"] iOpenVDBDocumentation["Web"] := SystemOpen[$vdbWebDocURL] iOpenVDBDocumentation["Notebook"] := notebookDocumentation[] iOpenVDBDocumentation["Update"] := ( Quiet[DeleteFile[$vdbDoc]]; notebookDocumentation[] ) iOpenVDBDocumentation[___] = $Failed; (* ::Subsubsection::Closed:: *) (*Argument conform & completion*) SyntaxInformation[OpenVDBDocumentation] = {"ArgumentsPattern" -> {_.}}; addCodeCompletion[OpenVDBDocumentation][{"Web", "Notebook", "Update"}]; (* ::Subsubsection::Closed:: *) (*notebookDocumentation*) notebookDocumentation[] /; FileExistsQ[$vdbDoc] := NotebookOpen[$vdbDoc] notebookDocumentation[] := Block[{zip}, If[!FileExistsQ[$vdbDocDir], CreateDirectory[$vdbDocDir]; ]; zip = URLDownload[$vdbDocURL, $vdbDocDir]; ( Quiet[ ExtractArchive[zip, $vdbDocDir, FileNameTake[$vdbDoc]]; DeleteFile[zip]; ]; NotebookOpen[$vdbDoc] /; FileExistsQ[$vdbDoc] ) /; FileExistsQ[zip] ] notebookDocumentation[___] = $Failed; (* ::Subsubsection::Closed:: *) (*Documentation paths and URLs*) $vdbDocDir = FileNameJoin[{$UserBaseDirectory, "ApplicationData", "OpenVDBLink"}]; $vdbDoc = FileNameJoin[{$vdbDocDir, "OpenVDBLink.nb"}]; $vdbDocURL = "https://www.openvdb.org/download/files/OpenVDBLink.nb.zip"; $vdbWebDocURL = "https://www.openvdb.org/documentation/wolfram"; (* ::Subsubsection::Closed:: *) (*Messages*) mOpenVDBDocumentation[type_] := ( If[type =!= "Web" && type =!= "Notebook", Message[OpenVDBDocumentation::type, type, 1]; ]; $Failed ) mOpenVDBDocumentation[___] = $Failed; OpenVDBDocumentation::type = "`1` at position `2` is not one of \"Web\", \"Notebook\", or \"Update\".";