diff --git a/.github/workflows/ax.yml b/.github/workflows/ax.yml index 1d80168419..49a63cefa6 100644 --- a/.github/workflows/ax.yml +++ b/.github/workflows/ax.yml @@ -101,7 +101,7 @@ jobs: matrix: compiler: ['clang++'] build: ['Release'] - llvm: ['7','8','9','10'] + llvm: ['7','8','9'] #@todo add back llvm 10 when the brew formula is fixed # Extra builds include: - compiler: 'g++' diff --git a/.github/workflows/houdini_cache_update.yml b/.github/workflows/houdini_cache_update.yml index 2fa9024ed1..5f30d44034 100644 --- a/.github/workflows/houdini_cache_update.yml +++ b/.github/workflows/houdini_cache_update.yml @@ -11,6 +11,26 @@ on: # succeeds, put it into the GitHub Actions cache jobs: + houdini18_5: + runs-on: ubuntu-16.04 + container: + image: aswf/ci-base:2020 + steps: + - uses: actions/checkout@v2 + - name: download_houdini + run: ./ci/download_houdini.sh 18.5 ON ON ${{ secrets.HOUDINI_CLIENT_ID }} ${{ secrets.HOUDINI_SECRET_KEY }} + - name: install_houdini + run: ./ci/install_houdini.sh + - name: build + run: ./ci/build_houdini.sh clang++ Release ON + - name: test + run: ./ci/test.sh + - name: write_houdini_cache + uses: actions/cache@v2 + with: + path: hou + key: vdb1-houdini18_5-${{ hashFiles('hou/hou.tar.gz') }} + houdini18_0: runs-on: ubuntu-16.04 container: diff --git a/CHANGES b/CHANGES index f30fea7ec8..6a2aef3cc6 100644 --- a/CHANGES +++ b/CHANGES @@ -1,10 +1,24 @@ OpenVDB Version History ======================= -Version 7.1.1 - In Development +Version 7.2.0 - In Development + + New features: + - Added tree::DynamicNodeManager that lazily caches the nodes at each + level of the tree to allow for efficient threading of topology-changing + top-down workflows. + - Added tools::CsgUnionOp, tools::CsgIntersectionOp and + tools::CsgDifferenceOp that use a parallel breadth-first algorithm to + accelerate CSG union, intersection and difference operations. + - Added tools::TreeToMerge which provides methods to steal or deep-copy + from a tree based on the tag dispatch class used in its construction. Improvements: - util::CpuTimer now uses C++11 chrono instead of TBB. + - Threaded the construction of LeafManager and NodeManager linear arrays. + - tools::csgUnion, tools::csgIntersection and tools::csgDifference now use + the new parallel breadth-first algorithms for much faster performance. + - Extended tree::NodeManager to allow for use with a const tree. Houdini: - Fixed a bug in the OpenVDB Points Convert SOP where the auto voxel @@ -18,6 +32,17 @@ Version 7.1.1 - In Development Bug fixes: - Fixed a bug which could cause recursive compile time instantiations of TypeList objects, manifesting in longer compile times. + - Deprecated util::PagedArray::push_back due to a race condition. Instead + use util::PagedArray::ValueBuffer::push_back which is faster and thread-safe. + + API changes: + - Deprecated tree::LeafManager::getNodes. This method is no longer used when + constructing a NodeManager from a LeafManager. + - Deprecated Tree::visitActiveBBox, Tree::visit and Tree::visit2 methods in + favor of using a tree::DynamicNodeManager. + - Removed tools::CsgVisitorBase, tools::CsgVisitorUnion, + tools::CsgVisitorIntersection and tools::CsgVisitorDifference. The CSG + tools now use the parallel breath-first algorithms. Build: - Removed the Makefiles. diff --git a/ci/download_houdini.sh b/ci/download_houdini.sh index 9fc482b5ba..a76e1fc65b 100755 --- a/ci/download_houdini.sh +++ b/ci/download_houdini.sh @@ -45,6 +45,7 @@ cp -r dsolib/libbz2* ../hou/dsolib/. cp -r dsolib/libtbb* ../hou/dsolib/. cp -r dsolib/libHalf* ../hou/dsolib/. cp -r dsolib/libjemalloc* ../hou/dsolib/. +cp -r dsolib/liblzma* ../hou/dsolib/. # needed for < H18.0 (due to sesitag) if [ "$HOUDINI_MAJOR" == "17.0" ] || [ "$HOUDINI_MAJOR" == "17.5" ]; then diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 1e7e1036e3..9c622ca0ab 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -51,7 +51,7 @@ set(DOXY_FILES doc/python.txt) set(DOXYGEN_PROJECT_NAME "OpenVDB") -set(DOXYGEN_PROJECT_NUMBER "7.1.1") +set(DOXYGEN_PROJECT_NUMBER "7.2.0") set(DOXYGEN_PROJECT_BRIEF "") set(DOXYGEN_FILE_PATTERNS "*.h") # headers only set(DOXYGEN_IMAGE_PATH "doc/img") @@ -86,7 +86,7 @@ set(DOXYGEN_ALIASES [[ijk="(ijk)"]] [[xyz="(xyz)"]] [[const="const"]] - [["vdbnamespace=openvdb::v7_1"]] + [["vdbnamespace=openvdb::v7_2"]] [["hunamespace=houdini_utils"]] [["hvdbnamespace=openvdb_houdini"]] # Use this command to create a link to an OpenVDB class, function, etc. @@ -109,7 +109,7 @@ set(DOXYGEN_ALIASES ) set(DOXYGEN_PREDEFINED - "OPENVDB_VERSION_NAME=v7_1" + "OPENVDB_VERSION_NAME=v7_2" "OPENVDB_ABI_VERSION_NUMBER=7" [[__declspec(x):= __attribute__(x):=]] "OPENVDB_USE_LOG4CPLUS=") diff --git a/doc/changes.txt b/doc/changes.txt index 04a9513048..064d7b413f 100644 --- a/doc/changes.txt +++ b/doc/changes.txt @@ -2,13 +2,34 @@ @page changes Release Notes -@htmlonly @endhtmlonly +@htmlonly @endhtmlonly @par -Version 7.1.1 - In Development +Version 7.2.0 - In Development + +@par +New features: +- Added @vdblink::tree::DynamicNodeManager DynamicNodeManager@endlink that + lazily caches the nodes at each level of the tree to allow for efficient + threading of topology-changing top-down workflows. +- Added @vdblink::tools::CsgUnionOp CsgUnionOp@endlink, + @vdblink::tools::CsgIntersectionOp CsgIntersectionOp@endlink and + @vdblink::tools::CsgDifferenceOp CsgDifferenceOp@endlink that use a parallel + breadth-first algorithm to accelerate CSG union, intersection and difference + operations. +- Added @vdblink::tools::TreeToMerge TreeToMerge@endlink which provides methods + to steal or deep-copy from a tree based on the tag dispatch class used in its + construction. @par Improvements: - util::CpuTimer now uses C++11 chrono instead of TBB. +- Threaded the construction of LeafManager and NodeManager linear arrays. +- @vdblink::tools::csgUnion() csgUnion@endlink, + @vdblink::tools::csgIntersection() csgIntersection@endlink and + @vdblink::tools::csgDifference() csgDifference@endlink now use the new + parallel breadth-first algorithms for much faster performance. +- Extended @vdblink::tree::NodeManager NodeManager@endlink to allow for use with + a const tree. @par Houdini: @@ -24,6 +45,20 @@ Houdini: Bug Fixes: - Fixed a bug which could cause recursive compile time instantiations of TypeList objects, manifesting in longer compile times. +- Deprecated @vdblink::util::PagedArray::push_back util::PagedArray::push_back@endlink + due to a race condition. Instead use + @vdblink::util::PagedArray::ValueBuffer::push_back util::PagedArray::ValueBuffer::push_back@endlink + which is faster and thread-safe. + +@par +API changes: +- Deprecated tree::LeafManager::getNodes. This method is no longer used when + constructing a NodeManager from a LeafManager. +- Deprecated Tree::visitActiveBBox, Tree::visit and Tree::visit2 methods in + favor of using a tree::DynamicNodeManager. +- Removed tools::CsgVisitorBase, tools::CsgVisitorUnion, + tools::CsgVisitorIntersection and tools::CsgVisitorDifference. The CSG tools + now use the parallel breath-first algorithms. @par Build: diff --git a/openvdb/openvdb/CMakeLists.txt b/openvdb/openvdb/CMakeLists.txt index 036c7c259c..0ead889f52 100644 --- a/openvdb/openvdb/CMakeLists.txt +++ b/openvdb/openvdb/CMakeLists.txt @@ -318,6 +318,7 @@ set(OPENVDB_LIBRARY_TOOLS_INCLUDE_FILES tools/LevelSetTracker.h tools/LevelSetUtil.h tools/Mask.h + tools/Merge.h tools/MeshToVolume.h tools/Morphology.h tools/MultiResGrid.h diff --git a/openvdb/openvdb/Types.h b/openvdb/openvdb/Types.h index 13699e094a..fd30ef82ae 100644 --- a/openvdb/openvdb/Types.h +++ b/openvdb/openvdb/Types.h @@ -1045,6 +1045,10 @@ class ShallowCopy {}; /// @brief Tag dispatch class that distinguishes topology copy constructors /// from deep copy constructors class TopologyCopy {}; +/// @brief Tag dispatch class that distinguishes constructors that deep copy +class DeepCopy {}; +/// @brief Tag dispatch class that distinguishes constructors that steal +class Steal {}; /// @brief Tag dispatch class that distinguishes constructors during file input class PartialCreate {}; diff --git a/openvdb/openvdb/tools/Composite.h b/openvdb/openvdb/tools/Composite.h index cf401ac2ef..6bc170bf76 100644 --- a/openvdb/openvdb/tools/Composite.h +++ b/openvdb/openvdb/tools/Composite.h @@ -15,6 +15,7 @@ #include #include #include // for isExactlyEqual() +#include "Merge.h" #include "ValueTransformer.h" // for transformValues() #include "Prune.h"// for prune #include "SignedFloodFill.h" // for signedFloodFill() @@ -722,6 +723,27 @@ struct CopyOp }; /// @endcond +template +inline void validateLevelSet(const TreeT& tree, const std::string& gridName = std::string("")) +{ + using ValueT = typename TreeT::ValueType; + const ValueT zero = zeroVal(); + if (!(tree.background() > zero)) { + std::stringstream ss; + ss << "expected grid "; + if (!gridName.empty()) ss << gridName << " "; + ss << "outside value > 0, got " << tree.background(); + OPENVDB_THROW(ValueError, ss.str()); + } + if (!(-tree.background() < zero)) { + std::stringstream ss; + ss << "expected grid "; + if (!gridName.empty()) ss << gridName << " "; + ss << "inside value < 0, got " << -tree.background(); + OPENVDB_THROW(ValueError, ss.str()); + } +} + } // namespace composite @@ -858,270 +880,6 @@ compReplace(GridOrTreeT& aTree, const GridOrTreeT& bTree) //////////////////////////////////////// -/// Base visitor class for CSG operations -/// (not intended to be used polymorphically, so no virtual functions) -template -class CsgVisitorBase -{ -public: - using TreeT = TreeType; - using ValueT = typename TreeT::ValueType; - using ChildIterT = typename TreeT::LeafNodeType::ChildAllIter; - - enum { STOP = 3 }; - - CsgVisitorBase(const TreeT& aTree, const TreeT& bTree): - mAOutside(aTree.background()), - mAInside(math::negative(mAOutside)), - mBOutside(bTree.background()), - mBInside(math::negative(mBOutside)) - { - const ValueT zero = zeroVal(); - if (!(mAOutside > zero)) { - OPENVDB_THROW(ValueError, - "expected grid A outside value > 0, got " << mAOutside); - } - if (!(mAInside < zero)) { - OPENVDB_THROW(ValueError, - "expected grid A inside value < 0, got " << mAInside); - } - if (!(mBOutside > zero)) { - OPENVDB_THROW(ValueError, - "expected grid B outside value > 0, got " << mBOutside); - } - if (!(mBInside < zero)) { - OPENVDB_THROW(ValueError, - "expected grid B outside value < 0, got " << mBOutside); - } - } - -protected: - ValueT mAOutside, mAInside, mBOutside, mBInside; -}; - - -//////////////////////////////////////// - - -template -struct CsgUnionVisitor: public CsgVisitorBase -{ - using TreeT = TreeType; - using ValueT = typename TreeT::ValueType; - using ChildIterT = typename TreeT::LeafNodeType::ChildAllIter; - - enum { STOP = CsgVisitorBase::STOP }; - - CsgUnionVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} - - /// Don't process nodes that are at different tree levels. - template - inline int operator()(AIterT&, BIterT&) { return 0; } - - /// Process root and internal nodes. - template - inline int operator()(IterT& aIter, IterT& bIter) - { - ValueT aValue = zeroVal(); - typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); - if (!aChild && aValue < zeroVal()) { - // A is an inside tile. Leave it alone and stop traversing this branch. - return STOP; - } - - ValueT bValue = zeroVal(); - typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); - if (!bChild && bValue < zeroVal()) { - // B is an inside tile. Make A an inside tile and stop traversing this branch. - aIter.setValue(this->mAInside); - aIter.setValueOn(bIter.isValueOn()); - delete aChild; - return STOP; - } - - if (!aChild && aValue > zeroVal()) { - // A is an outside tile. If B has a child, transfer it to A, - // otherwise leave A alone. - if (bChild) { - bIter.setValue(this->mBOutside); - bIter.setValueOff(); - bChild->resetBackground(this->mBOutside, this->mAOutside); - aIter.setChild(bChild); // transfer child - delete aChild; - } - return STOP; - } - - // If A has a child and B is an outside tile, stop traversing this branch. - // Continue traversal only if A and B both have children. - return (aChild && bChild) ? 0 : STOP; - } - - /// Process leaf node values. - inline int operator()(ChildIterT& aIter, ChildIterT& bIter) - { - ValueT aValue, bValue; - aIter.probeValue(aValue); - bIter.probeValue(bValue); - if (aValue > bValue) { // a = min(a, b) - aIter.setValue(bValue); - aIter.setValueOn(bIter.isValueOn()); - } - return 0; - } -}; - - - -//////////////////////////////////////// - - -template -struct CsgIntersectVisitor: public CsgVisitorBase -{ - using TreeT = TreeType; - using ValueT = typename TreeT::ValueType; - using ChildIterT = typename TreeT::LeafNodeType::ChildAllIter; - - enum { STOP = CsgVisitorBase::STOP }; - - CsgIntersectVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} - - /// Don't process nodes that are at different tree levels. - template - inline int operator()(AIterT&, BIterT&) { return 0; } - - /// Process root and internal nodes. - template - inline int operator()(IterT& aIter, IterT& bIter) - { - ValueT aValue = zeroVal(); - typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); - if (!aChild && !(aValue < zeroVal())) { - // A is an outside tile. Leave it alone and stop traversing this branch. - return STOP; - } - - ValueT bValue = zeroVal(); - typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); - if (!bChild && !(bValue < zeroVal())) { - // B is an outside tile. Make A an outside tile and stop traversing this branch. - aIter.setValue(this->mAOutside); - aIter.setValueOn(bIter.isValueOn()); - delete aChild; - return STOP; - } - - if (!aChild && aValue < zeroVal()) { - // A is an inside tile. If B has a child, transfer it to A, - // otherwise leave A alone. - if (bChild) { - bIter.setValue(this->mBOutside); - bIter.setValueOff(); - bChild->resetBackground(this->mBOutside, this->mAOutside); - aIter.setChild(bChild); // transfer child - delete aChild; - } - return STOP; - } - - // If A has a child and B is an outside tile, stop traversing this branch. - // Continue traversal only if A and B both have children. - return (aChild && bChild) ? 0 : STOP; - } - - /// Process leaf node values. - inline int operator()(ChildIterT& aIter, ChildIterT& bIter) - { - ValueT aValue, bValue; - aIter.probeValue(aValue); - bIter.probeValue(bValue); - if (aValue < bValue) { // a = max(a, b) - aIter.setValue(bValue); - aIter.setValueOn(bIter.isValueOn()); - } - return 0; - } -}; - - -//////////////////////////////////////// - - -template -struct CsgDiffVisitor: public CsgVisitorBase -{ - using TreeT = TreeType; - using ValueT = typename TreeT::ValueType; - using ChildIterT = typename TreeT::LeafNodeType::ChildAllIter; - - enum { STOP = CsgVisitorBase::STOP }; - - CsgDiffVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} - - /// Don't process nodes that are at different tree levels. - template - inline int operator()(AIterT&, BIterT&) { return 0; } - - /// Process root and internal nodes. - template - inline int operator()(IterT& aIter, IterT& bIter) - { - ValueT aValue = zeroVal(); - typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); - if (!aChild && !(aValue < zeroVal())) { - // A is an outside tile. Leave it alone and stop traversing this branch. - return STOP; - } - - ValueT bValue = zeroVal(); - typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); - if (!bChild && bValue < zeroVal()) { - // B is an inside tile. Make A an inside tile and stop traversing this branch. - aIter.setValue(this->mAOutside); - aIter.setValueOn(bIter.isValueOn()); - delete aChild; - return STOP; - } - - if (!aChild && aValue < zeroVal()) { - // A is an inside tile. If B has a child, transfer it to A, - // otherwise leave A alone. - if (bChild) { - bIter.setValue(this->mBOutside); - bIter.setValueOff(); - bChild->resetBackground(this->mBOutside, this->mAOutside); - aIter.setChild(bChild); // transfer child - bChild->negate(); - delete aChild; - } - return STOP; - } - - // If A has a child and B is an outside tile, stop traversing this branch. - // Continue traversal only if A and B both have children. - return (aChild && bChild) ? 0 : STOP; - } - - /// Process leaf node values. - inline int operator()(ChildIterT& aIter, ChildIterT& bIter) - { - ValueT aValue, bValue; - aIter.probeValue(aValue); - bIter.probeValue(bValue); - bValue = math::negative(bValue); - if (aValue < bValue) { // a = max(a, -b) - aIter.setValue(bValue); - aIter.setValueOn(bIter.isValueOn()); - } - return 0; - } -}; - - -//////////////////////////////////////// - - template inline void csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune) @@ -1129,8 +887,11 @@ csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune) using Adapter = TreeAdapter; using TreeT = typename Adapter::TreeType; TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); - CsgUnionVisitor visitor(aTree, bTree); - aTree.visit2(bTree, visitor); + composite::validateLevelSet(aTree, "A"); + composite::validateLevelSet(bTree, "B"); + CsgUnionOp op(bTree, Steal()); + tree::DynamicNodeManager nodeManager(aTree); + nodeManager.foreachTopDown(op); if (prune) tools::pruneLevelSet(aTree); } @@ -1141,8 +902,11 @@ csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune) using Adapter = TreeAdapter; using TreeT = typename Adapter::TreeType; TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); - CsgIntersectVisitor visitor(aTree, bTree); - aTree.visit2(bTree, visitor); + composite::validateLevelSet(aTree, "A"); + composite::validateLevelSet(bTree, "B"); + CsgIntersectionOp op(bTree, Steal()); + tree::DynamicNodeManager nodeManager(aTree); + nodeManager.foreachTopDown(op); if (prune) tools::pruneLevelSet(aTree); } @@ -1153,8 +917,11 @@ csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune) using Adapter = TreeAdapter; using TreeT = typename Adapter::TreeType; TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); - CsgDiffVisitor visitor(aTree, bTree); - aTree.visit2(bTree, visitor); + composite::validateLevelSet(aTree, "A"); + composite::validateLevelSet(bTree, "B"); + CsgDifferenceOp op(bTree, Steal()); + tree::DynamicNodeManager nodeManager(aTree); + nodeManager.foreachTopDown(op); if (prune) tools::pruneLevelSet(aTree); } diff --git a/openvdb/openvdb/tools/Merge.h b/openvdb/openvdb/tools/Merge.h new file mode 100644 index 0000000000..9fdc2ae43b --- /dev/null +++ b/openvdb/openvdb/tools/Merge.h @@ -0,0 +1,956 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 +// +/// @file Merge.h +/// +/// @brief Functions to efficiently merge grids +/// +/// @author Dan Bailey + +#ifndef OPENVDB_TOOLS_MERGE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MERGE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Convenience class that contains a pointer to a tree to be stolen or +/// deep copied depending on the tag dispatch class used and a subset of +/// methods to retrieve data from the tree. +/// +/// @details The primary purpose of this class is to be able to create an array +/// of TreeToMerge objects that each store a tree to be stolen or a tree to be +/// deep-copied in an arbitrary order. Certain operations such as floating-point +/// addition are non-associative so the order in which they are merged is +/// important for the operation to remain deterministic regardless of how the +/// data is being extracted from the tree. +/// +/// @note Stealing data requires a non-const tree pointer. There is a constructor +/// to pass in a tree shared pointer for cases where it is desirable for this class +/// to maintain shared ownership. +template +struct TreeToMerge +{ + using TreeType = std::remove_const_t; + using RootNodeType = typename TreeType::RootNodeType; + using ValueType = typename TreeType::ValueType; + using MaskTreeType = typename TreeT::template ValueConverter::Type; + + TreeToMerge() = delete; + + /// @brief Non-const pointer tree constructor for stealing data. + TreeToMerge(TreeType& tree, Steal) + : mTree(&tree), mSteal(true) { } + /// @brief Non-const shared pointer tree constructor for stealing data. + TreeToMerge(typename TreeType::Ptr treePtr, Steal) + : mTreePtr(treePtr), mTree(mTreePtr.get()), mSteal(true) { } + + /// @brief Const tree pointer constructor for deep-copying data. As the + /// tree is not mutable and thus cannot be pruned, a lightweight mask tree + /// with the same topology is created that can be pruned to use as a + /// reference. Initialization of this mask tree can optionally be disabled + /// for delayed construction. + TreeToMerge(const TreeType& tree, DeepCopy, bool initialize = true) + : mTree(&tree), mSteal(false) + { + if (mTree && initialize) this->initializeMask(); + } + + /// @brief Non-const tree pointer constructor for deep-copying data. The + /// tree is not intended to be modified so is not pruned, instead a + /// lightweight mask tree with the same topology is created that can be + /// pruned to use as a reference. Initialization of this mask tree can + /// optionally be disabled for delayed construction. + TreeToMerge(TreeType& tree, DeepCopy tag, bool initialize = true) + : TreeToMerge(static_cast(tree), tag, initialize) { } + + /// @brief Reset the non-const tree shared pointer. This is primarily + /// used to preserve the order of trees to merge in a container but have + /// the data in the tree be lazily loaded or resampled. + void reset(typename TreeType::Ptr treePtr, Steal); + + /// @brief Return a pointer to the tree to be stolen. + TreeType* treeToSteal() { return mSteal ? const_cast(mTree) : nullptr; } + /// @brief Return a pointer to the tree to be deep-copied. + const TreeType* treeToDeepCopy() { return mSteal ? nullptr : mTree; } + + /// @brief Retrieve a const pointer to the root node. + const RootNodeType* rootPtr() const; + + /// @brief Return a pointer to the node of type @c NodeT that contains + /// voxel (x, y, z). If no such node exists, return @c nullptr. + template + const NodeT* probeConstNode(const Coord& ijk) const; + + /// @brief Return a pointer to the node of type @c NodeT that contains voxel (x, y, z). + /// If the tree is non-const, steal the node and replace it with an inactive + /// background-value tile. + /// If the tree is const, deep-copy the node and modify the mask tree to prune the node. + template + std::unique_ptr stealOrDeepCopyNode(const Coord& ijk); + + /// @brief Add a tile containing voxel (x, y, z) at the level of NodeT, + /// deleting the existing branch if necessary. + template + void addTile(const Coord& ijk, const ValueType& value, bool active); + + // build a lightweight mask using a union of the const tree where leaf nodes + // are converted into active tiles + void initializeMask(); + + // returns true if mask has been initialized + bool hasMask() const; + + // returns MaskTree pointer or nullptr + MaskTreeType* mask() { return mMaskTree.ptr.get(); } + const MaskTreeType* mask() const { return mMaskTree.ptr.get(); } + +private: + struct MaskPtr; + struct MaskUnionOp; + + typename TreeType::Ptr mTreePtr; + const TreeType* mTree; + MaskPtr mMaskTree; + bool mSteal; +}; // struct TreeToMerge + + +/// @brief Wrapper around unique_ptr that deep-copies mask on copy construction +template +struct TreeToMerge::MaskPtr +{ + std::unique_ptr ptr; + + MaskPtr() = default; + ~MaskPtr() = default; + MaskPtr(MaskPtr&& other) = default; + MaskPtr& operator=(MaskPtr&& other) = default; + MaskPtr(const MaskPtr& other) + : ptr(bool(other.ptr) ? std::make_unique(*other.ptr) : nullptr) { } + MaskPtr& operator=(const MaskPtr& other) + { + ptr.reset(bool(other.ptr) ? std::make_unique(*other.ptr) : nullptr); + return *this; + } +}; + +/// @brief DynamicNodeManager operator used to generate a mask of the input +/// tree, but with dense leaf nodes replaced with active tiles for compactness +template +struct TreeToMerge::MaskUnionOp +{ + using MaskT = MaskTreeType; + using RootT = typename MaskT::RootNodeType; + using LeafT = typename MaskT::LeafNodeType; + + explicit MaskUnionOp(const TreeT& tree) : mTree(tree) { } + bool operator()(RootT& root, size_t) const; + template + bool operator()(NodeT& node, size_t) const; + bool operator()(LeafT&, size_t) const { return false; } +private: + const TreeT& mTree; +}; // struct TreeToMerge::MaskUnionOp + + +//////////////////////////////////////// + + +/// @brief DynamicNodeManager operator to merge trees using a CSG union or intersection. +/// @note This class modifies the topology of the tree so is designed to be used +/// from DynamicNodeManager::foreachTopDown(). +/// @details A union and an intersection are opposite operations to each other so +/// implemented in a combined class. Use the CsgUnionOp and CsgIntersectionOp aliases +/// for convenience. +template +struct CsgUnionOrIntersectionOp +{ + using ValueT = typename TreeT::ValueType; + using RootT = typename TreeT::RootNodeType; + using LeafT = typename TreeT::LeafNodeType; + + /// @brief Convenience constructor to CSG union or intersect a single + /// non-const tree with another. This constructor takes a Steal or DeepCopy + /// tag dispatch class. + template + CsgUnionOrIntersectionOp(TreeT& tree, TagT tag) { mTreesToMerge.emplace_back(tree, tag); } + + /// @brief Convenience constructor to CSG union or intersect a single + /// const tree with another. This constructor requires a DeepCopy tag + /// dispatch class. + CsgUnionOrIntersectionOp(const TreeT& tree, DeepCopy tag) { mTreesToMerge.emplace_back(tree, tag); } + + /// @brief Constructor to CSG union or intersect a container of multiple + /// const or non-const tree pointers. A Steal tag requires a container of + /// non-const trees, a DeepCopy tag will accept either const or non-const + /// trees. + template + CsgUnionOrIntersectionOp(TreesT& trees, TagT tag) + { + for (auto* tree : trees) { + if (tree) { + mTreesToMerge.emplace_back(*tree, tag); + } + } + } + + /// @brief Constructor to accept a vector of TreeToMerge objects, primarily + /// used when mixing const/non-const trees. + /// @note Union/intersection order is preserved. + explicit CsgUnionOrIntersectionOp(const std::vector>& trees) + : mTreesToMerge(trees) { } + + /// @brief Constructor to accept a deque of TreeToMerge objects, primarily + /// used when mixing const/non-const trees. + /// @note Union/intersection order is preserved. + explicit CsgUnionOrIntersectionOp(const std::deque>& trees) + : mTreesToMerge(trees.cbegin(), trees.cend()) { } + + /// @brief Return true if no trees being merged + bool empty() const { return mTreesToMerge.empty(); } + + /// @brief Return the number of trees being merged + size_t size() const { return mTreesToMerge.size(); } + + // Processes the root node. Required by the NodeManager + bool operator()(RootT& root, size_t idx) const; + + // Processes the internal nodes. Required by the NodeManager + template + bool operator()(NodeT& node, size_t idx) const; + + // Processes the leaf nodes. Required by the NodeManager + bool operator()(LeafT& leaf, size_t idx) const; + +private: + // on processing the root node, the background value is stored, retrieve it + // and check that the root node has already been processed + const ValueT& background() const; + + mutable std::vector> mTreesToMerge; + mutable const ValueT* mBackground = nullptr; +}; // struct CsgUnionOrIntersectionOp + + +template +using CsgUnionOp = CsgUnionOrIntersectionOp; + +template +using CsgIntersectionOp = CsgUnionOrIntersectionOp; + + +/// @brief DynamicNodeManager operator to merge two trees using a CSG difference. +/// @note This class modifies the topology of the tree so is designed to be used +/// from DynamicNodeManager::foreachTopDown(). +template +struct CsgDifferenceOp +{ + using ValueT = typename TreeT::ValueType; + using RootT = typename TreeT::RootNodeType; + using LeafT = typename TreeT::LeafNodeType; + + /// @brief Convenience constructor to CSG difference a single non-const + /// tree from another. This constructor takes a Steal or DeepCopy tag + /// dispatch class. + template + CsgDifferenceOp(TreeT& tree, TagT tag) : mTree(tree, tag) { } + /// @brief Convenience constructor to CSG difference a single const + /// tree from another. This constructor requires an explicit DeepCopy tag + /// dispatch class. + CsgDifferenceOp(const TreeT& tree, DeepCopy tag) : mTree(tree, tag) { } + + /// @brief Constructor to CSG difference the tree in a TreeToMerge object + /// from another. + explicit CsgDifferenceOp(TreeToMerge& tree) : mTree(tree) { } + + /// @brief Return the number of trees being merged (only ever 1) + size_t size() const { return 1; } + + // Processes the root node. Required by the NodeManager + bool operator()(RootT& root, size_t idx) const; + + // Processes the internal nodes. Required by the NodeManager + template + bool operator()(NodeT& node, size_t idx) const; + + // Processes the leaf nodes. Required by the NodeManager + bool operator()(LeafT& leaf, size_t idx) const; + +private: + // on processing the root node, the background values are stored, retrieve them + // and check that the root nodes have already been processed + const ValueT& background() const; + const ValueT& otherBackground() const; + + // note that this vector is copied in NodeTransformer every time a foreach call is made, + // however in typical use cases this cost will be dwarfed by the actual merge algorithm + mutable TreeToMerge mTree; + mutable const ValueT* mBackground = nullptr; + mutable const ValueT* mOtherBackground = nullptr; +}; // struct CsgDifferenceOp + + +//////////////////////////////////////// + + +template +void TreeToMerge::initializeMask() +{ + if (mSteal) return; + mMaskTree.ptr.reset(new MaskTreeType); + MaskUnionOp op(*mTree); + tree::DynamicNodeManager manager(*this->mask()); + manager.foreachTopDown(op); +} + +template +bool TreeToMerge::hasMask() const +{ + return bool(mMaskTree.ptr); +} + +template +void TreeToMerge::reset(typename TreeType::Ptr treePtr, Steal) +{ + if (!treePtr) { + OPENVDB_THROW(RuntimeError, "Cannot reset with empty Tree shared pointer."); + } + mSteal = true; + mTreePtr = treePtr; + mTree = mTreePtr.get(); +} + +template +const typename TreeToMerge::RootNodeType* +TreeToMerge::rootPtr() const +{ + return &mTree->root(); +} + +template +template +const NodeT* +TreeToMerge::probeConstNode(const Coord& ijk) const +{ + // test mutable mask first, node may have already been pruned + if (!mSteal && !this->mask()->isValueOn(ijk)) return nullptr; + return mTree->template probeConstNode(ijk); +} + +template +template +std::unique_ptr +TreeToMerge::stealOrDeepCopyNode(const Coord& ijk) +{ + if (mSteal) { + TreeType* tree = const_cast(mTree); + return std::unique_ptr( + tree->root().template stealNode(ijk, mTree->root().background(), false) + ); + } else { + auto* child = this->probeConstNode(ijk); + if (child) { + assert(this->hasMask()); + auto result = std::make_unique(*child); + // prune mask tree + this->mask()->addTile(NodeT::LEVEL, ijk, false, false); + return result; + } + } + return std::unique_ptr(); +} + +template +template +void +TreeToMerge::addTile(const Coord& ijk, const ValueType& value, bool active) +{ + // ignore leaf node tiles (values) + if (NodeT::LEVEL == 0) return; + + if (mSteal) { + TreeType* tree = const_cast(mTree); + auto* node = tree->template probeNode(ijk); + if (node) { + const Index pos = NodeT::coordToOffset(ijk); + node->addTile(pos, value, active); + } + } else { + auto* node = mTree->template probeConstNode(ijk); + // prune mask tree + if (node) { + assert(this->hasMask()); + this->mask()->addTile(NodeT::LEVEL, ijk, false, false); + } + } +} + + +//////////////////////////////////////// + + +template +bool TreeToMerge::MaskUnionOp::operator()(RootT& root, size_t /*idx*/) const +{ + using ChildT = typename RootT::ChildNodeType; + + const Index count = mTree.root().childCount(); + + std::vector> children(count); + + // allocate new root children + + tbb::parallel_for( + tbb::blocked_range(0, count), + [&](tbb::blocked_range& range) + { + for (Index i = range.begin(); i < range.end(); i++) { + children[i] = std::make_unique(Coord::max(), true, true); + } + } + ); + + // apply origins and add root children to new root node + + size_t i = 0; + for (auto iter = mTree.root().cbeginChildOn(); iter; ++iter) { + children[i]->setOrigin(iter->origin()); + root.addChild(children[i].release()); + i++; + } + + return true; +} + +template +template +bool TreeToMerge::MaskUnionOp::operator()(NodeT& node, size_t /*idx*/) const +{ + using ChildT = typename NodeT::ChildNodeType; + + const auto* otherNode = mTree.template probeConstNode(node.origin()); + if (!otherNode) return false; + + // this mask tree stores active tiles in place of leaf nodes for compactness + + if (NodeT::LEVEL == 1) { + for (auto iter = otherNode->cbeginChildOn(); iter; ++iter) { + node.addTile(iter.pos(), true, true); + } + } else { + for (auto iter = otherNode->cbeginChildOn(); iter; ++iter) { + auto* child = new ChildT(iter->origin(), true, true); + node.addChild(child); + } + } + + return true; +} + + +//////////////////////////////////////// + + +namespace merge_internal { + + +template +struct UnallocatedBuffer +{ + static void allocateAndFill(BufferT& buffer, const ValueT& background) + { + if (!buffer.isOutOfCore() && buffer.empty()) { + buffer.allocate(); + buffer.fill(background); + } + } + + static bool isPartiallyConstructed(const BufferT& buffer) + { + return !buffer.isOutOfCore() && buffer.empty(); + } +}; // struct AllocateAndFillBuffer + +template +struct UnallocatedBuffer +{ + // do nothing for bool buffers as they cannot be unallocated + static void allocateAndFill(BufferT&, const bool&) { } + static bool isPartiallyConstructed(const BufferT&) { return false; } +}; // struct AllocateAndFillBuffer + + +} // namespace merge_internal + + +//////////////////////////////////////// + + +template +bool CsgUnionOrIntersectionOp::operator()(RootT& root, size_t) const +{ + if (this->empty()) return false; + + // store the background value + if (!mBackground) mBackground = &root.background(); + + // find all tile values in this root and track inside/outside and active state + // note that level sets should never contain active tiles, but we handle them anyway + + constexpr uint8_t ACTIVE_TILE = 0x1; + constexpr uint8_t INSIDE_TILE = 0x2; + constexpr uint8_t OUTSIDE_TILE = 0x4; + + constexpr uint8_t INSIDE_STATE = Union ? INSIDE_TILE : OUTSIDE_TILE; + constexpr uint8_t OUTSIDE_STATE = Union ? OUTSIDE_TILE : INSIDE_TILE; + + const ValueT insideBackground = Union ? -this->background() : this->background(); + const ValueT outsideBackground = -insideBackground; + + auto getTileFlag = [&](auto& valueIter) -> uint8_t + { + uint8_t flag(0); + const ValueT& value = valueIter.getValue(); + if (value < zeroVal()) flag |= INSIDE_TILE; + else if (value > zeroVal()) flag |= OUTSIDE_TILE; + if (valueIter.isValueOn()) flag |= ACTIVE_TILE; + return flag; + }; + + std::unordered_map tiles; + + if (root.getTableSize() > 0) { + for (auto valueIter = root.cbeginValueAll(); valueIter; ++valueIter) { + const Coord& key = valueIter.getCoord(); + tiles.insert({key, getTileFlag(valueIter)}); + } + } + + // find all tiles values in other roots and replace outside tiles with inside tiles + + for (TreeToMerge& mergeTree : mTreesToMerge) { + const auto* mergeRoot = mergeTree.rootPtr(); + if (!mergeRoot) continue; + for (auto valueIter = mergeRoot->cbeginValueAll(); valueIter; ++valueIter) { + const Coord& key = valueIter.getCoord(); + auto it = tiles.find(key); + if (it == tiles.end()) { + // if no tile with this key, insert it + tiles.insert({key, getTileFlag(valueIter)}); + } else { + // replace an outside tile with an inside tile + const uint8_t flag = it->second; + if (flag & OUTSIDE_STATE) { + const uint8_t newFlag = getTileFlag(valueIter); + if (newFlag & INSIDE_STATE) { + it->second = newFlag; + } + } + } + } + } + + // insert all inside tiles + + for (auto it : tiles) { + const uint8_t flag = it.second; + if (flag & INSIDE_STATE) { + const Coord& key = it.first; + const bool state = flag & ACTIVE_TILE; + root.addTile(key, insideBackground, state); + } + } + + std::unordered_set children; + + if (root.getTableSize() > 0) { + for (auto childIter = root.cbeginChildOn(); childIter; ++childIter) { + const Coord& key = childIter.getCoord(); + children.insert(key); + } + } + + bool continueRecurse = false; + + // find all children in other roots and insert them if a child or tile with this key + // does not already exist or if the child will replace an outside tile + + for (TreeToMerge& mergeTree : mTreesToMerge) { + const auto* mergeRoot = mergeTree.rootPtr(); + if (!mergeRoot) continue; + for (auto childIter = mergeRoot->cbeginChildOn(); childIter; ++childIter) { + const Coord& key = childIter.getCoord(); + + // if child already exists, merge recursion will need to continue to resolve conflict + if (children.count(key)) { + continueRecurse = true; + continue; + } + + // if an inside tile exists, do nothing + auto it = tiles.find(key); + if (it != tiles.end() && it->second == INSIDE_STATE) continue; + + auto childPtr = mergeTree.template stealOrDeepCopyNode(key); + if (childPtr) root.addChild(childPtr.release()); + + children.insert(key); + } + } + + // insert all outside tiles that don't replace an inside tile or a child node + + for (auto it : tiles) { + const uint8_t flag = it.second; + if (flag & OUTSIDE_STATE) { + const Coord& key = it.first; + if (!children.count(key)) { + const bool state = flag & ACTIVE_TILE; + root.addTile(key, outsideBackground, state); + } + } + } + + return continueRecurse; +} + +template +template +bool CsgUnionOrIntersectionOp::operator()(NodeT& node, size_t) const +{ + using NonConstNodeT = typename std::remove_const::type; + + if (this->empty()) return false; + + const ValueT insideBackground = Union ? -this->background() : this->background(); + const ValueT outsideBackground = -insideBackground; + + using NodeMaskT = typename NodeT::NodeMaskType; + + // store temporary masks to track inside and outside tile states + NodeMaskT validTile; + NodeMaskT invalidTile; + + auto isValid = [](const ValueT& value) + { + return Union ? value < zeroVal() : value > zeroVal(); + }; + + auto isInvalid = [](const ValueT& value) + { + return Union ? value > zeroVal() : value < zeroVal(); + }; + + for (auto iter = node.cbeginValueAll(); iter; ++iter) { + if (isValid(iter.getValue())) { + validTile.setOn(iter.pos()); + } else if (isInvalid(iter.getValue())) { + invalidTile.setOn(iter.pos()); + } + } + + bool continueRecurse = false; + + for (TreeToMerge& mergeTree : mTreesToMerge) { + + auto* mergeNode = mergeTree.template probeConstNode(node.origin()); + + if (!mergeNode) continue; + + // iterate over all tiles + + for (auto iter = mergeNode->cbeginValueAll(); iter; ++iter) { + Index pos = iter.pos(); + // source node contains an inside tile, so ignore + if (validTile.isOn(pos)) continue; + // this node contains an inside tile, so turn into an inside tile + if (isValid(iter.getValue())) { + node.addTile(pos, insideBackground, iter.isValueOn()); + validTile.setOn(pos); + } + } + + // iterate over all child nodes + + for (auto iter = mergeNode->cbeginChildOn(); iter; ++iter) { + Index pos = iter.pos(); + const Coord& ijk = iter.getCoord(); + // source node contains an inside tile, so ensure other node has no child + if (validTile.isOn(pos)) { + mergeTree.template addTile(ijk, outsideBackground, false); + } else if (invalidTile.isOn(pos)) { + auto childPtr = mergeTree.template stealOrDeepCopyNode(ijk); + if (childPtr) node.addChild(childPtr.release()); + invalidTile.setOff(pos); + } else { + // if both source and target are child nodes, merge recursion needs to continue + // along this branch to resolve the conflict + continueRecurse = true; + } + } + } + + return continueRecurse; +} + +template +bool CsgUnionOrIntersectionOp::operator()(LeafT& leaf, size_t) const +{ + using LeafT = typename TreeT::LeafNodeType; + using ValueT = typename LeafT::ValueType; + using BufferT = typename LeafT::Buffer; + + if (this->empty()) return false; + + const ValueT background = Union ? this->background() : -this->background(); + + // if buffer is not out-of-core and empty, leaf node must have only been + // partially constructed, so allocate and fill with background value + + merge_internal::UnallocatedBuffer::allocateAndFill( + leaf.buffer(), background); + + for (TreeToMerge& mergeTree : mTreesToMerge) { + const LeafT* mergeLeaf = mergeTree.template probeConstNode(leaf.origin()); + if (!mergeLeaf) continue; + // if buffer is not out-of-core yet empty, leaf node must have only been + // partially constructed, so skip merge + if (merge_internal::UnallocatedBuffer::isPartiallyConstructed( + mergeLeaf->buffer())) { + continue; + } + + for (Index i = 0 ; i < LeafT::SIZE; i++) { + const ValueT& newValue = mergeLeaf->getValue(i); + const bool doMerge = Union ? newValue < leaf.getValue(i) : newValue > leaf.getValue(i); + if (doMerge) { + leaf.setValueOnly(i, newValue); + leaf.setActiveState(i, mergeLeaf->isValueOn(i)); + } + } + } + + return false; +} + +template +const typename CsgUnionOrIntersectionOp::ValueT& +CsgUnionOrIntersectionOp::background() const +{ + // this operator is only intended to be used with foreachTopDown() + assert(mBackground); + return *mBackground; +} + + +//////////////////////////////////////// + + +template +bool CsgDifferenceOp::operator()(RootT& root, size_t) const +{ + // store the background values + if (!mBackground) mBackground = &root.background(); + if (!mOtherBackground) mOtherBackground = &mTree.rootPtr()->background(); + + // find all tile values in this root and track inside/outside and active state + // note that level sets should never contain active tiles, but we handle them anyway + + constexpr uint8_t ACTIVE_TILE = 0x1; + constexpr uint8_t INSIDE_TILE = 0x2; + constexpr uint8_t CHILD = 0x4; + + auto getTileFlag = [&](auto& valueIter) -> uint8_t + { + uint8_t flag(0); + const ValueT& value = valueIter.getValue(); + if (value < zeroVal()) flag |= INSIDE_TILE; + if (valueIter.isValueOn()) flag |= ACTIVE_TILE; + return flag; + }; + + std::unordered_map flags; + + if (root.getTableSize() > 0) { + for (auto valueIter = root.cbeginValueAll(); valueIter; ++valueIter) { + const Coord& key = valueIter.getCoord(); + const uint8_t flag = getTileFlag(valueIter); + if (flag & INSIDE_TILE) { + flags.insert({key, getTileFlag(valueIter)}); + } + } + + for (auto childIter = root.cbeginChildOn(); childIter; ++childIter) { + const Coord& key = childIter.getCoord(); + flags.insert({key, CHILD}); + } + } + + bool continueRecurse = false; + + const auto* mergeRoot = mTree.rootPtr(); + + if (mergeRoot) { + for (auto valueIter = mergeRoot->cbeginValueAll(); valueIter; ++valueIter) { + const Coord& key = valueIter.getCoord(); + const uint8_t flag = getTileFlag(valueIter); + if (flag & INSIDE_TILE) { + auto it = flags.find(key); + if (it != flags.end()) { + const bool state = flag & ACTIVE_TILE; + root.addTile(key, this->background(), state); + } + } + } + + for (auto childIter = mergeRoot->cbeginChildOn(); childIter; ++childIter) { + const Coord& key = childIter.getCoord(); + auto it = flags.find(key); + if (it != flags.end()) { + const uint8_t otherFlag = it->second; + if (otherFlag & CHILD) { + // if child already exists, merge recursion will need to continue to resolve conflict + continueRecurse = true; + } else if (otherFlag & INSIDE_TILE) { + auto childPtr = mTree.template stealOrDeepCopyNode(key); + if (childPtr) { + childPtr->resetBackground(this->otherBackground(), this->background()); + childPtr->negate(); + root.addChild(childPtr.release()); + } + } + } + } + } + + return continueRecurse; +} + +template +template +bool CsgDifferenceOp::operator()(NodeT& node, size_t) const +{ + using NonConstNodeT = typename std::remove_const::type; + + using NodeMaskT = typename NodeT::NodeMaskType; + + // store temporary mask to track inside tile state + + NodeMaskT insideTile; + for (auto iter = node.cbeginValueAll(); iter; ++iter) { + if (iter.getValue() < zeroVal()) { + insideTile.setOn(iter.pos()); + } + } + + bool continueRecurse = false; + + auto* mergeNode = mTree.template probeConstNode(node.origin()); + + if (!mergeNode) return continueRecurse; + + // iterate over all tiles + + for (auto iter = mergeNode->cbeginValueAll(); iter; ++iter) { + Index pos = iter.pos(); + if (iter.getValue() < zeroVal()) { + if (insideTile.isOn(pos) || node.isChildMaskOn(pos)) { + node.addTile(pos, this->background(), iter.isValueOn()); + } + } + } + + // iterate over all children + + for (auto iter = mergeNode->cbeginChildOn(); iter; ++iter) { + Index pos = iter.pos(); + const Coord& ijk = iter.getCoord(); + if (insideTile.isOn(pos)) { + auto childPtr = mTree.template stealOrDeepCopyNode(ijk); + if (childPtr) { + childPtr->resetBackground(this->otherBackground(), this->background()); + childPtr->negate(); + node.addChild(childPtr.release()); + } + } else if (node.isChildMaskOn(pos)) { + // if both source and target are child nodes, merge recursion needs to continue + // along this branch to resolve the conflict + continueRecurse = true; + } + } + + return continueRecurse; +} + +template +bool CsgDifferenceOp::operator()(LeafT& leaf, size_t) const +{ + using LeafT = typename TreeT::LeafNodeType; + using ValueT = typename LeafT::ValueType; + using BufferT = typename LeafT::Buffer; + + // if buffer is not out-of-core and empty, leaf node must have only been + // partially constructed, so allocate and fill with background value + + merge_internal::UnallocatedBuffer::allocateAndFill( + leaf.buffer(), this->background()); + + const LeafT* mergeLeaf = mTree.template probeConstNode(leaf.origin()); + if (!mergeLeaf) return false; + + // if buffer is not out-of-core yet empty, leaf node must have only been + // partially constructed, so skip merge + + if (merge_internal::UnallocatedBuffer::isPartiallyConstructed( + mergeLeaf->buffer())) { + return false; + } + + for (Index i = 0 ; i < LeafT::SIZE; i++) { + const ValueT& aValue = leaf.getValue(i); + ValueT bValue = math::negative(mergeLeaf->getValue(i)); + if (aValue < bValue) { // a = max(a, -b) + leaf.setValueOnly(i, bValue); + leaf.setActiveState(i, mergeLeaf->isValueOn(i)); + } + } + + return false; +} + +template +const typename CsgDifferenceOp::ValueT& +CsgDifferenceOp::background() const +{ + // this operator is only intended to be used with foreachTopDown() + assert(mBackground); + return *mBackground; +} + +template +const typename CsgDifferenceOp::ValueT& +CsgDifferenceOp::otherBackground() const +{ + // this operator is only intended to be used with foreachTopDown() + assert(mOtherBackground); + return *mOtherBackground; +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MERGE_HAS_BEEN_INCLUDED diff --git a/openvdb/openvdb/tree/LeafManager.h b/openvdb/openvdb/tree/LeafManager.h index 06b913659b..696cfda929 100644 --- a/openvdb/openvdb/tree/LeafManager.h +++ b/openvdb/openvdb/tree/LeafManager.h @@ -16,9 +16,11 @@ #define OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED #include +#include // for NodeChain #include #include #include +#include #include #include @@ -192,10 +194,6 @@ class LeafManager , mLeafCount(0) , mAuxBufferCount(0) , mAuxBuffersPerLeaf(auxBuffersPerLeaf) - , mLeafs(nullptr) - , mAuxBuffers(nullptr) - , mTask(nullptr) - , mIsMaster(true) { this->rebuild(serial); } @@ -209,10 +207,8 @@ class LeafManager , mLeafCount(end-begin) , mAuxBufferCount(0) , mAuxBuffersPerLeaf(auxBuffersPerLeaf) - , mLeafs(new LeafType*[mLeafCount]) - , mAuxBuffers(nullptr) - , mTask(nullptr) - , mIsMaster(true) + , mLeafPtrs(new LeafType*[mLeafCount]) + , mLeafs(mLeafPtrs.get()) { size_t n = mLeafCount; LeafType **target = mLeafs, **source = begin; @@ -231,17 +227,10 @@ class LeafManager , mLeafs(other.mLeafs) , mAuxBuffers(other.mAuxBuffers) , mTask(other.mTask) - , mIsMaster(false) { } - virtual ~LeafManager() - { - if (mIsMaster) { - delete [] mLeafs; - delete [] mAuxBuffers; - } - } + virtual ~LeafManager() { } /// @brief (Re)initialize by resizing (if necessary) and repopulating the leaf array /// and by deleting existing auxiliary buffers and allocating new ones. @@ -250,7 +239,7 @@ class LeafManager /// of corresponding leaf node buffers. void rebuild(bool serial=false) { - this->initLeafArray(); + this->initLeafArray(serial); this->initAuxBuffers(serial); } //@{ @@ -285,10 +274,10 @@ class LeafManager void removeAuxBuffers() { this->rebuildAuxBuffers(0); } /// @brief Remove the auxiliary buffers and rebuild the leaf array. - void rebuildLeafArray() + void rebuildLeafArray(bool serial = false) { this->removeAuxBuffers(); - this->initLeafArray(); + this->initLeafArray(serial); } /// @brief Return the total number of allocated auxiliary buffers. @@ -548,13 +537,8 @@ class LeafManager transform.run(this->leafRange(grainSize), threaded); } - - /// @brief Insert pointers to nodes of the specified type into the array. - /// @details The type of node pointer is defined by the type - /// ArrayT::value_type. If the node type is a LeafNode the nodes - /// are inserted from this LeafManager, else of the corresponding tree. template - void getNodes(ArrayT& array) + [[deprecated("Use Tree::getNodes()")]] void getNodes(ArrayT& array) { using T = typename ArrayT::value_type; static_assert(std::is_pointer::value, "argument to getNodes() must be a pointer array"); @@ -571,12 +555,8 @@ class LeafManager OPENVDB_NO_UNREACHABLE_CODE_WARNING_END } - /// @brief Insert node pointers of the specified type into the array. - /// @details The type of node pointer is defined by the type - /// ArrayT::value_type. If the node type is a LeafNode the nodes - /// are inserted from this LeafManager, else of the corresponding tree. template - void getNodes(ArrayT& array) const + [[deprecated("Use Tree::getNodes()")]] void getNodes(ArrayT& array) const { using T = typename ArrayT::value_type; static_assert(std::is_pointer::value, "argument to getNodes() must be a pointer array"); @@ -635,34 +615,102 @@ class LeafManager private: - // This a simple wrapper for a c-style array so it mimics the api - // of a std container, e.g. std::vector or std::deque, and can be - // passed to Tree::getNodes(). - struct MyArray { - using value_type = LeafType*;//required by Tree::getNodes - value_type* ptr; - MyArray(value_type* array) : ptr(array) {} - void push_back(value_type leaf) { *ptr++ = leaf; }//required by Tree::getNodes - }; - - void initLeafArray() + void initLeafArray(bool serial = false) { - const size_t leafCount = mTree->leafCount(); + // Build an array of all nodes that have leaf nodes as their immediate children + + using NodeChainT = typename NodeChain::Type; + using NonConstLeafParentT = typename NodeChainT::template Get; + using LeafParentT = typename CopyConstness::Type; + + std::deque leafParents; + mTree->getNodes(leafParents); + + // Compute the leaf counts for each node + + std::vector leafCounts; + if (serial) { + leafCounts.reserve(leafParents.size()); + for (LeafParentT* leafParent : leafParents) { + leafCounts.push_back(leafParent->childCount()); + } + } else { + leafCounts.resize(leafParents.size()); + tbb::parallel_for( + // with typical node sizes and SSE enabled, there are only a handful + // of instructions executed per-operation with a default grainsize + // of 1, so increase to 64 to reduce parallel scheduling overhead + tbb::blocked_range(0, leafParents.size(), /*grainsize=*/64), + [&](tbb::blocked_range& range) + { + for (size_t i = range.begin(); i < range.end(); i++) { + leafCounts[i] = leafParents[i]->childCount(); + } + } + ); + } + + // Turn leaf counts into a cumulative histogram and obtain total leaf count + + for (size_t i = 1; i < leafCounts.size(); i++) { + leafCounts[i] += leafCounts[i-1]; + } + + const size_t leafCount = leafCounts.empty() ? 0 : leafCounts.back(); + + // Allocate (or deallocate) the leaf pointer array + if (leafCount != mLeafCount) { - delete [] mLeafs; - mLeafs = (leafCount == 0) ? nullptr : new LeafType*[leafCount]; + if (leafCount > 0) { + mLeafPtrs.reset(new LeafType*[leafCount]); + mLeafs = mLeafPtrs.get(); + } else { + mLeafPtrs.reset(); + mLeafs = nullptr; + } mLeafCount = leafCount; } - MyArray a(mLeafs); - mTree->getNodes(a); + + if (mLeafCount == 0) return; + + // Populate the leaf node pointers + + if (serial) { + LeafType** leafPtr = mLeafs; + for (LeafParentT* leafParent : leafParents) { + for (auto iter = leafParent->beginChildOn(); iter; ++iter) { + *leafPtr++ = &iter.getValue(); + } + } + } else { + tbb::parallel_for( + tbb::blocked_range(0, leafParents.size()), + [&](tbb::blocked_range& range) + { + size_t i = range.begin(); + LeafType** leafPtr = mLeafs; + if (i > 0) leafPtr += leafCounts[i-1]; + for ( ; i < range.end(); i++) { + for (auto iter = leafParents[i]->beginChildOn(); iter; ++iter) { + *leafPtr++ = &iter.getValue(); + } + } + } + ); + } } void initAuxBuffers(bool serial) { const size_t auxBufferCount = mLeafCount * mAuxBuffersPerLeaf; if (auxBufferCount != mAuxBufferCount) { - delete [] mAuxBuffers; - mAuxBuffers = (auxBufferCount == 0) ? nullptr : new NonConstBufferType[auxBufferCount]; + if (auxBufferCount > 0) { + mAuxBufferPtrs.reset(new NonConstBufferType[auxBufferCount]); + mAuxBuffers = mAuxBufferPtrs.get(); + } else { + mAuxBufferPtrs.reset(); + mAuxBuffers = nullptr; + } mAuxBufferCount = auxBufferCount; } this->syncAllBuffers(serial); @@ -745,14 +793,14 @@ class LeafManager template struct LeafReducer { - LeafReducer(LeafOp &leafOp) : mLeafOp(&leafOp), mOwnsOp(false) + LeafReducer(LeafOp &leafOp) : mLeafOp(&leafOp) { } LeafReducer(const LeafReducer &other, tbb::split) - : mLeafOp(new LeafOp(*(other.mLeafOp), tbb::split())), mOwnsOp(true) + : mLeafOpPtr(std::make_unique(*(other.mLeafOp), tbb::split())) + , mLeafOp(mLeafOpPtr.get()) { } - ~LeafReducer() { if (mOwnsOp) delete mLeafOp; } void run(const LeafRange& range, bool threaded) { threaded ? tbb::parallel_reduce(range, *this) : (*this)(range); @@ -763,8 +811,8 @@ class LeafManager for (typename LeafRange::Iterator it = range.begin(); it; ++it) op(*it, it.pos()); } void join(const LeafReducer& other) { mLeafOp->join(*(other.mLeafOp)); } - LeafOp *mLeafOp; - const bool mOwnsOp; + std::unique_ptr mLeafOpPtr; + LeafOp *mLeafOp = nullptr; };// LeafReducer // Helper class to compute a prefix sum of offsets to active voxels @@ -790,12 +838,13 @@ class LeafManager using FuncType = typename std::function; - TreeType* mTree; - size_t mLeafCount, mAuxBufferCount, mAuxBuffersPerLeaf; - LeafType** mLeafs;//array of LeafNode pointers - NonConstBufferType* mAuxBuffers;//array of auxiliary buffers - FuncType mTask; - const bool mIsMaster; + TreeType* mTree; + size_t mLeafCount, mAuxBufferCount, mAuxBuffersPerLeaf; + std::unique_ptr mLeafPtrs; + LeafType** mLeafs = nullptr;//array of LeafNode pointers + std::unique_ptr mAuxBufferPtrs; + NonConstBufferType* mAuxBuffers = nullptr;//array of auxiliary buffers + FuncType mTask = nullptr; };//end of LeafManager class diff --git a/openvdb/openvdb/tree/NodeManager.h b/openvdb/openvdb/tree/NodeManager.h index 8cca7f2575..21fc5661b6 100644 --- a/openvdb/openvdb/tree/NodeManager.h +++ b/openvdb/openvdb/tree/NodeManager.h @@ -3,13 +3,12 @@ /// @file tree/NodeManager.h /// -/// @author Ken Museth +/// @authors Ken Museth, Dan Bailey /// /// @brief NodeManager produces linear arrays of all tree nodes /// allowing for efficient threading and bottom-up processing. /// /// @note A NodeManager can be constructed from a Tree or LeafManager. -/// The latter is slightly more efficient since the cached leaf nodes will be reused. #ifndef OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED #define OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED @@ -31,9 +30,23 @@ template +class DynamicNodeManager; + + //////////////////////////////////////// +// This is a dummy node filtering class used by the NodeManager class to match +// the internal filtering interface used by the DynamicNodeManager. +struct NodeFilter +{ + static bool valid(size_t) { return true; } +}; // struct NodeFilter + + /// @brief This class caches tree nodes of a specific type in a linear array. /// /// @note It is for internal use and should rarely be used directly. @@ -41,22 +54,135 @@ template class NodeList { public: - using value_type = NodeT*; - using ListT = std::deque; + NodeList() = default; + + NodeT& operator()(size_t n) const { assert(n + bool initRootChildren(RootT& root) + { + // Allocate (or deallocate) the node pointer array - NodeT*& operator[](size_t n) { assert(n 0) { + mNodePtrs.reset(new NodeT*[nodeCount]); + mNodes = mNodePtrs.get(); + } else { + mNodePtrs.reset(); + mNodes = nullptr; + } + mNodeCount = nodeCount; + } - void clear() { mList.clear(); } + if (mNodeCount == 0) return false; + + // Populate the node pointers - void resize(size_t n) { mList.resize(n); } + NodeT** nodePtr = mNodes; + for (auto iter = root.beginChildOn(); iter; ++iter) { + *nodePtr++ = &iter.getValue(); + } + + return true; + } + + // initialize this node list from another node list containing the parent nodes + template + bool initNodeChildren(ParentsT& parents, const NodeFilterT& nodeFilter = NodeFilterT(), bool serial = false) + { + // Compute the node counts for each node + + std::vector nodeCounts; + if (serial) { + nodeCounts.reserve(parents.nodeCount()); + for (size_t i = 0; i < parents.nodeCount(); i++) { + if (!nodeFilter.valid(i)) nodeCounts.push_back(0); + else nodeCounts.push_back(parents(i).childCount()); + } + } else { + nodeCounts.resize(parents.nodeCount()); + tbb::parallel_for( + // with typical node sizes and SSE enabled, there are only a handful + // of instructions executed per-operation with a default grainsize + // of 1, so increase to 64 to reduce parallel scheduling overhead + tbb::blocked_range(0, parents.nodeCount(), /*grainsize=*/64), + [&](tbb::blocked_range& range) + { + for (Index64 i = range.begin(); i < range.end(); i++) { + if (!nodeFilter.valid(i)) nodeCounts[i] = 0; + else nodeCounts[i] = parents(i).childCount(); + } + } + ); + } + + // Turn node counts into a cumulative histogram and obtain total node count + + for (size_t i = 1; i < nodeCounts.size(); i++) { + nodeCounts[i] += nodeCounts[i-1]; + } + + const size_t nodeCount = nodeCounts.empty() ? 0 : nodeCounts.back(); + + // Allocate (or deallocate) the node pointer array + + if (nodeCount != mNodeCount) { + if (nodeCount > 0) { + mNodePtrs.reset(new NodeT*[nodeCount]); + mNodes = mNodePtrs.get(); + } else { + mNodePtrs.reset(); + mNodes = nullptr; + } + mNodeCount = nodeCount; + } + + if (mNodeCount == 0) return false; + + // Populate the node pointers + + if (serial) { + NodeT** nodePtr = mNodes; + for (size_t i = 0; i < parents.nodeCount(); i++) { + if (!nodeFilter.valid(i)) continue; + for (auto iter = parents(i).beginChildOn(); iter; ++iter) { + *nodePtr++ = &iter.getValue(); + } + } + } else { + tbb::parallel_for( + tbb::blocked_range(0, parents.nodeCount()), + [&](tbb::blocked_range& range) + { + Index64 i = range.begin(); + NodeT** nodePtr = mNodes; + if (i > 0) nodePtr += nodeCounts[i-1]; + for ( ; i < range.end(); i++) { + if (!nodeFilter.valid(i)) continue; + for (auto iter = parents(i).beginChildOn(); iter; ++iter) { + *nodePtr++ = &iter.getValue(); + } + } + } + ); + } + + return true; + } class NodeRange { @@ -152,10 +278,42 @@ class NodeList transform.run(this->nodeRange(grainSize), threaded); } + // identical to foreach except the operator() method has a node index + template + void foreachWithIndex(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + NodeTransformer transform(op); + transform.run(this->nodeRange(grainSize), threaded); + } + + // identical to reduce except the operator() method has a node index + template + void reduceWithIndex(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + NodeReducer transform(op); + transform.run(this->nodeRange(grainSize), threaded); + } + private: + // default execution in the NodeManager ignores the node index + // given by the iterator position + struct OpWithoutIndex + { + template + static void eval(T& node, typename NodeRange::Iterator& iter) { node(*iter); } + }; + + // execution in the DynamicNodeManager matches that of the LeafManager in + // passing through the node index given by the iterator position + struct OpWithIndex + { + template + static void eval(T& node, typename NodeRange::Iterator& iter) { node(*iter, iter.pos()); } + }; + // Private struct of NodeList that performs parallel_for - template + template struct NodeTransformer { NodeTransformer(const NodeOp& nodeOp) : mNodeOp(nodeOp) @@ -167,43 +325,48 @@ class NodeList } void operator()(const NodeRange& range) const { - for (typename NodeRange::Iterator it = range.begin(); it; ++it) mNodeOp(*it); + for (typename NodeRange::Iterator it = range.begin(); it; ++it) { + OpT::template eval(mNodeOp, it); + } } - const NodeOp mNodeOp; + const NodeOp& mNodeOp; };// NodeList::NodeTransformer // Private struct of NodeList that performs parallel_reduce - template + template struct NodeReducer { - NodeReducer(NodeOp& nodeOp) : mNodeOp(&nodeOp), mOwnsOp(false) + NodeReducer(NodeOp& nodeOp) : mNodeOp(&nodeOp) { } - NodeReducer(const NodeReducer& other, tbb::split) : - mNodeOp(new NodeOp(*(other.mNodeOp), tbb::split())), mOwnsOp(true) + NodeReducer(const NodeReducer& other, tbb::split) + : mNodeOpPtr(std::make_unique(*(other.mNodeOp), tbb::split())) + , mNodeOp(mNodeOpPtr.get()) { } - ~NodeReducer() { if (mOwnsOp) delete mNodeOp; } void run(const NodeRange& range, bool threaded = true) { threaded ? tbb::parallel_reduce(range, *this) : (*this)(range); } void operator()(const NodeRange& range) { - NodeOp &op = *mNodeOp; - for (typename NodeRange::Iterator it = range.begin(); it; ++it) op(*it); + for (typename NodeRange::Iterator it = range.begin(); it; ++it) { + OpT::template eval(*mNodeOp, it); + } } void join(const NodeReducer& other) { mNodeOp->join(*(other.mNodeOp)); } - NodeOp *mNodeOp; - const bool mOwnsOp; + std::unique_ptr mNodeOpPtr; + NodeOp *mNodeOp = nullptr; };// NodeList::NodeReducer protected: - ListT mList; + size_t mNodeCount = 0; + std::unique_ptr mNodePtrs; + NodeT** mNodes = nullptr; };// NodeList @@ -218,25 +381,25 @@ template class NodeManagerLink { public: - NodeManagerLink() {} + using NonConstChildNodeType = typename NodeT::ChildNodeType; + using ChildNodeType = typename CopyConstness::Type; - virtual ~NodeManagerLink() {} + NodeManagerLink() = default; void clear() { mList.clear(); mNext.clear(); } - template - void init(ParentT& parent, TreeOrLeafManagerT& tree) + template + void initRootChildren(RootT& root, bool serial = false) { - parent.getNodes(mList); - for (size_t i=0, n=mList.nodeCount(); i - void rebuild(ParentT& parent) + template + void initNodeChildren(ParentsT& parents, bool serial = false) { - mList.clear(); - parent.getNodes(mList); - for (size_t i=0, n=mList.nodeCount(); i mList; - NodeManagerLink mNext; + NodeManagerLink mNext; };// NodeManagerLink class @@ -290,15 +453,16 @@ template class NodeManagerLink { public: - NodeManagerLink() {} - - virtual ~NodeManagerLink() {} + NodeManagerLink() = default; /// @brief Clear all the cached tree nodes void clear() { mList.clear(); } - template - void rebuild(ParentT& parent) { mList.clear(); parent.getNodes(mList); } + template + void initRootChildren(RootT& root, bool /*serial*/ = false) { mList.initRootChildren(root); } + + template + void initNodeChildren(ParentsT& parents, bool serial = false) { mList.initNodeChildren(parents, NodeFilter(), serial); } Index64 nodeCount() const { return mList.nodeCount(); } @@ -328,17 +492,6 @@ class NodeManagerLink mList.reduce(op, threaded, grainSize); } - template - void init(ParentT& parent, TreeOrLeafManagerT& tree) - { - OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN - if (TreeOrLeafManagerT::DEPTH == 2 && NodeT::LEVEL == 0) { - tree.getNodes(mList); - } else { - parent.getNodes(mList); - } - OPENVDB_NO_UNREACHABLE_CODE_WARNING_END - } protected: NodeList mList; };// NodeManagerLink class @@ -359,19 +512,26 @@ class NodeManager static const Index LEVELS = _LEVELS; static_assert(LEVELS > 0, "expected instantiation of template specialization"); // see specialization below - using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using RootNodeType = typename CopyConstness::Type; + using NonConstChildNodeType = typename RootNodeType::ChildNodeType; + using ChildNodeType = typename CopyConstness::Type; static_assert(RootNodeType::LEVEL >= LEVELS, "number of levels exceeds root node height"); - NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { mChain.init(mRoot, tree); } + NodeManager(TreeOrLeafManagerT& tree, bool serial = false) + : mRoot(tree.root()) + { + this->rebuild(serial); + } - virtual ~NodeManager() {} + NodeManager(const NodeManager&) = delete; /// @brief Clear all the cached tree nodes void clear() { mChain.clear(); } /// @brief Clear and recache all the tree nodes from the /// tree. This is required if tree nodes have been added or removed. - void rebuild() { mChain.rebuild(mRoot); } + void rebuild(bool serial = false) { mChain.initRootChildren(mRoot, serial); } /// @brief Return a reference to the root node. const RootNodeType& root() const { return mRoot; } @@ -530,11 +690,319 @@ class NodeManager protected: RootNodeType& mRoot; - NodeManagerLink mChain; + NodeManagerLink mChain; +};// NodeManager class + + +//////////////////////////////////////////// + + +// Wraps a user-supplied DynamicNodeManager operator and stores the return +// value of the operator() method to the index of the node in a bool array +template +struct ForeachFilterOp +{ + explicit ForeachFilterOp(const OpT& op, openvdb::Index64 size) + : mOp(op) + , mValidPtr(std::make_unique(size)) + , mValid(mValidPtr.get()) { } + + ForeachFilterOp(const ForeachFilterOp& other) + : mOp(other.mOp) + , mValid(other.mValid) { } + + template + void operator()(NodeT& node, size_t idx) const + { + mValid[idx] = mOp(node, idx); + } + + bool valid(size_t idx) const { return mValid[idx]; } + + const OpT& op() const { return mOp; } private: - NodeManager(const NodeManager&) {}//disallow copy-construction -};// NodeManager class + const OpT& mOp; + std::unique_ptr mValidPtr; + bool* mValid = nullptr; +}; // struct ForeachFilterOp + + +// Wraps a user-supplied DynamicNodeManager operator and stores the return +// value of the operator() method to the index of the node in a bool array +template +struct ReduceFilterOp +{ + ReduceFilterOp(OpT& op, openvdb::Index64 size) + : mOp(&op) + , mValidPtr(std::make_unique(size)) + , mValid(mValidPtr.get()) { } + + ReduceFilterOp(const ReduceFilterOp& other) + : mOp(other.mOp) + , mValid(other.mValid) { } + + ReduceFilterOp(const ReduceFilterOp& other, tbb::split) + : mOpPtr(std::make_unique(*(other.mOp), tbb::split())) + , mOp(mOpPtr.get()) + , mValid(other.mValid) { } + + template + void operator()(NodeT& node, size_t idx) const + { + mValid[idx] = (*mOp)(node, idx); + } + + void join(const ReduceFilterOp& other) + { + mOp->join(*(other.mOp)); + } + + bool valid(size_t idx) const + { + return mValid[idx]; + } + + OpT& op() { return *mOp; } + +private: + std::unique_ptr mOpPtr; + OpT* mOp = nullptr; + std::unique_ptr mValidPtr; + bool* mValid = nullptr; +}; // struct ReduceFilterOp + + +/// @brief This class is a link in a chain that each caches tree nodes +/// of a specific type in a linear array. +/// +/// @note It is for internal use and should rarely be used directly. +template +class DynamicNodeManagerLink +{ +public: + DynamicNodeManagerLink() = default; + + template + void foreachTopDown(const NodeOpT& op, RootT& root, bool threaded, size_t grainSize) + { + if (!op(root, /*index=*/0)) return; + if (!mList.initRootChildren(root)) return; + ForeachFilterOp filterOp(op, mList.nodeCount()); + mList.foreachWithIndex(filterOp, threaded, grainSize); + mNext.foreachTopDownRecurse(filterOp, mList, threaded, grainSize); + } + + template + void foreachTopDownRecurse(const FilterOpT& filterOp, ParentT& parent, bool threaded, size_t grainSize) + { + if (!mList.initNodeChildren(parent, filterOp, !threaded)) return; + FilterOpT childFilterOp(filterOp.op(), mList.nodeCount()); + mList.foreachWithIndex(childFilterOp, threaded, grainSize); + } + + template + void reduceTopDown(NodeOpT& op, RootT& root, bool threaded, size_t grainSize) + { + if (!op(root, /*index=*/0)) return; + if (!mList.initRootChildren(root)) return; + ReduceFilterOp filterOp(op, mList.nodeCount()); + mList.reduceWithIndex(filterOp, threaded, grainSize); + mNext.reduceTopDownRecurse(filterOp, mList, threaded, grainSize); + } + + template + void reduceTopDownRecurse(FilterOpT& filterOp, ParentT& parent, bool threaded, size_t grainSize) + { + if (!mList.initNodeChildren(parent, filterOp, !threaded)) return; + FilterOpT childFilterOp(filterOp.op(), mList.nodeCount()); + mList.reduceWithIndex(childFilterOp, threaded, grainSize); + } + +protected: + NodeList mList; + DynamicNodeManagerLink mNext; +};// DynamicNodeManagerLink class + + +/// @private +/// @brief Specialization that terminates the chain of cached tree nodes +/// @note It is for internal use and should rarely be used directly. +template +class DynamicNodeManagerLink +{ +public: + DynamicNodeManagerLink() = default; + + template + void foreachTopDownRecurse(const NodeFilterOp& nodeFilterOp, ParentT& parent, bool threaded, size_t grainSize) + { + if (!mList.initNodeChildren(parent, nodeFilterOp, !threaded)) return; + mList.foreachWithIndex(nodeFilterOp.op(), threaded, grainSize); + } + + template + void reduceTopDownRecurse(NodeFilterOp& nodeFilterOp, ParentT& parent, bool threaded, size_t grainSize) + { + if (!mList.initNodeChildren(parent, nodeFilterOp, !threaded)) return; + mList.reduceWithIndex(nodeFilterOp.op(), threaded, grainSize); + } + +protected: + NodeList mList; +};// DynamicNodeManagerLink class + + +template +class DynamicNodeManager +{ +public: + static const Index LEVELS = _LEVELS; + static_assert(LEVELS > 0, + "expected instantiation of template specialization"); // see specialization below + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL >= LEVELS, "number of levels exceeds root node height"); + + explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { } + + DynamicNodeManager(const DynamicNodeManager&) = delete; + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + /// @brief Threaded method that applies a user-supplied functor + /// to all the nodes in the tree. + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @note There are two key differences to the interface of the + /// user-supplied functor to the NodeManager class - (1) the operator() + /// method aligns with the LeafManager class in expecting the index of the + /// node in a linear array of identical node types, (2) the operator() + /// method returns a boolean termination value with true indicating that + /// children of this node should be processed, false indicating the + /// early-exit termination should occur. + /// + /// @par Example: + /// @code + /// // Functor to densify the first child node in a linear array. Note + /// // this implementation also illustrates how different + /// // computation can be applied to the different node types. + /// + /// template + /// struct DensifyOp + /// { + /// using RootT = typename TreeT::RootNodeType; + /// using LeafT = typename TreeT::LeafNodeType; + /// + /// DensifyOp() = default; + /// + /// // Processes the root node. Required by the DynamicNodeManager + /// bool operator()(RootT&, size_t) const { return true; } + /// + /// // Processes the internal nodes. Required by the DynamicNodeManager + /// template + /// bool operator()(NodeT& node, size_t idx) const + /// { + /// // densify child + /// for (auto iter = node.cbeginValueAll(); iter; ++iter) { + /// const openvdb::Coord ijk = iter.getCoord(); + /// node.addChild(new typename NodeT::ChildNodeType(iter.getCoord(), NodeT::LEVEL, true)); + /// } + /// // early-exit termination for all non-zero index children + /// return idx == 0; + /// } + /// // Processes the leaf nodes. Required by the DynamicNodeManager + /// bool operator()(LeafT&, size_t) const + /// { + /// return true; + /// } + /// };// DensifyOp + /// + /// // usage: + /// DensifyOp op; + /// tree::DynamicNodeManager nodes(tree); + /// nodes.foreachTopDown(op); + /// + /// @endcode + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mChain.foreachTopDown(op, mRoot, threaded, grainSize); + } + + /// @brief Threaded method that processes nodes with a user supplied functor + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @note There are two key differences to the interface of the + /// user-supplied functor to the NodeManager class - (1) the operator() + /// method aligns with the LeafManager class in expecting the index of the + /// node in a linear array of identical node types, (2) the operator() + /// method returns a boolean termination value with true indicating that + /// children of this node should be processed, false indicating the + /// early-exit termination should occur. + /// + /// @par Example: + /// @code + /// // Functor to count nodes in a tree + /// template + /// struct NodeCountOp + /// { + /// NodeCountOp() : nodeCount(TreeType::DEPTH, 0), totalCount(0) + /// { + /// } + /// NodeCountOp(const NodeCountOp& other, tbb::split) : + /// nodeCount(TreeType::DEPTH, 0), totalCount(0) + /// { + /// } + /// void join(const NodeCountOp& other) + /// { + /// for (size_t i = 0; i < nodeCount.size(); ++i) { + /// nodeCount[i] += other.nodeCount[i]; + /// } + /// totalCount += other.totalCount; + /// } + /// // do nothing for the root node + /// bool operator()(const typename TreeT::RootNodeType& node, size_t) + /// { + /// return true; + /// } + /// // count the internal and leaf nodes + /// template + /// bool operator()(const NodeT& node, size_t) + /// { + /// ++(nodeCount[NodeT::LEVEL]); + /// ++totalCount; + /// return true; + /// } + /// std::vector nodeCount; + /// openvdb::Index64 totalCount; + /// }; + /// + /// // usage: + /// NodeCountOp op; + /// tree::DynamicNodeManager nodes(tree); + /// nodes.reduceTopDown(op); + /// + /// @endcode + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mChain.reduceTopDown(op, mRoot, threaded, grainSize); + } + +protected: + RootNodeType& mRoot; + DynamicNodeManagerLink mChain; +};// DynamicNodeManager class + //////////////////////////////////////////// @@ -546,19 +1014,20 @@ template class NodeManager { public: - using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using RootNodeType = typename CopyConstness::Type; static const Index LEVELS = 0; - NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) {} + NodeManager(TreeOrLeafManagerT& tree, bool /*serial*/ = false) : mRoot(tree.root()) { } - virtual ~NodeManager() {} + NodeManager(const NodeManager&) = delete; /// @brief Clear all the cached tree nodes void clear() {} /// @brief Clear and recache all the tree nodes from the /// tree. This is required if tree nodes have been added or removed. - void rebuild() {} + void rebuild(bool /*serial*/ = false) { } /// @brief Return a reference to the root node. const RootNodeType& root() const { return mRoot; } @@ -582,9 +1051,6 @@ class NodeManager protected: RootNodeType& mRoot; - -private: - NodeManager(const NodeManager&) {} // disallow copy-construction }; // NodeManager<0> @@ -597,29 +1063,25 @@ template class NodeManager { public: - using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using RootNodeType = typename CopyConstness::Type; static_assert(RootNodeType::LEVEL > 0, "expected instantiation of template specialization"); static const Index LEVELS = 1; - NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + NodeManager(TreeOrLeafManagerT& tree, bool serial = false) + : mRoot(tree.root()) { - OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN - if (TreeOrLeafManagerT::DEPTH == 2 && NodeT0::LEVEL == 0) { - tree.getNodes(mList0); - } else { - mRoot.getNodes(mList0); - } - OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + this->rebuild(serial); } - virtual ~NodeManager() {} + NodeManager(const NodeManager&) = delete; /// @brief Clear all the cached tree nodes void clear() { mList0.clear(); } /// @brief Clear and recache all the tree nodes from the /// tree. This is required if tree nodes have been added or removed. - void rebuild() { mList0.clear(); mRoot.getNodes(mList0); } + void rebuild(bool /*serial*/ = false) { mList0.initRootChildren(mRoot); } /// @brief Return a reference to the root node. const RootNodeType& root() const { return mRoot; } @@ -661,14 +1123,12 @@ class NodeManager protected: using NodeT1 = RootNodeType; - using NodeT0 = typename NodeT1::ChildNodeType; + using NonConstNodeT0 = typename NodeT1::ChildNodeType; + using NodeT0 = typename CopyConstness::Type; using ListT0 = NodeList; NodeT1& mRoot; ListT0 mList0; - -private: - NodeManager(const NodeManager&) {} // disallow copy-construction }; // NodeManager<1> @@ -681,35 +1141,27 @@ template class NodeManager { public: - using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using RootNodeType = typename CopyConstness::Type; static_assert(RootNodeType::LEVEL > 1, "expected instantiation of template specialization"); static const Index LEVELS = 2; - NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + NodeManager(TreeOrLeafManagerT& tree, bool serial = false) : mRoot(tree.root()) { - mRoot.getNodes(mList1); - - OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN - if (TreeOrLeafManagerT::DEPTH == 2 && NodeT0::LEVEL == 0) { - tree.getNodes(mList0); - } else { - for (size_t i=0, n=mList1.nodeCount(); irebuild(serial); } - virtual ~NodeManager() {} + NodeManager(const NodeManager&) = delete; /// @brief Clear all the cached tree nodes void clear() { mList0.clear(); mList1.clear(); } /// @brief Clear and recache all the tree nodes from the /// tree. This is required if tree nodes have been added or removed. - void rebuild() + void rebuild(bool serial = false) { - this->clear(); - mRoot.getNodes(mList1); - for (size_t i=0, n=mList1.nodeCount(); i protected: using NodeT2 = RootNodeType; - using NodeT1 = typename NodeT2::ChildNodeType; // upper level - using NodeT0 = typename NodeT1::ChildNodeType; // lower level + using NonConstNodeT1 = typename NodeT2::ChildNodeType; + using NodeT1 = typename CopyConstness::Type; // upper level + using NonConstNodeT0 = typename NodeT1::ChildNodeType; + using NodeT0 = typename CopyConstness::Type; // lower level using ListT1 = NodeList; // upper level using ListT0 = NodeList; // lower level @@ -768,9 +1222,6 @@ class NodeManager NodeT2& mRoot; ListT1 mList1; ListT0 mList0; - -private: - NodeManager(const NodeManager&) {} // disallow copy-construction }; // NodeManager<2> @@ -783,37 +1234,28 @@ template class NodeManager { public: - using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using RootNodeType = typename CopyConstness::Type; static_assert(RootNodeType::LEVEL > 2, "expected instantiation of template specialization"); static const Index LEVELS = 3; - NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + NodeManager(TreeOrLeafManagerT& tree, bool serial = false) : mRoot(tree.root()) { - mRoot.getNodes(mList2); - for (size_t i=0, n=mList2.nodeCount(); irebuild(serial); } - virtual ~NodeManager() {} + NodeManager(const NodeManager&) = delete; /// @brief Clear all the cached tree nodes void clear() { mList0.clear(); mList1.clear(); mList2.clear(); } /// @brief Clear and recache all the tree nodes from the /// tree. This is required if tree nodes have been added or removed. - void rebuild() + void rebuild(bool serial = false) { - this->clear(); - mRoot.getNodes(mList2); - for (size_t i=0, n=mList2.nodeCount(); i protected: using NodeT3 = RootNodeType; - using NodeT2 = typename NodeT3::ChildNodeType; // upper level - using NodeT1 = typename NodeT2::ChildNodeType; // mid level - using NodeT0 = typename NodeT1::ChildNodeType; // lower level + using NonConstNodeT2 = typename NodeT3::ChildNodeType; + using NodeT2 = typename CopyConstness::Type; // upper level + using NonConstNodeT1 = typename NodeT2::ChildNodeType; + using NodeT1 = typename CopyConstness::Type; // mid level + using NonConstNodeT0 = typename NodeT1::ChildNodeType; + using NodeT0 = typename CopyConstness::Type; // lower level using ListT2 = NodeList; // upper level of internal nodes using ListT1 = NodeList; // lower level of internal nodes @@ -880,9 +1325,6 @@ class NodeManager ListT2 mList2; ListT1 mList1; ListT0 mList0; - -private: - NodeManager(const NodeManager&) {} // disallow copy-construction }; // NodeManager<3> @@ -895,39 +1337,29 @@ template class NodeManager { public: - using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using NonConstRootNodeType = typename TreeOrLeafManagerT::RootNodeType; + using RootNodeType = typename CopyConstness::Type; static_assert(RootNodeType::LEVEL > 3, "expected instantiation of template specialization"); static const Index LEVELS = 4; - NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + NodeManager(TreeOrLeafManagerT& tree, bool serial = false) : mRoot(tree.root()) { - mRoot.getNodes(mList3); - for (size_t i=0, n=mList3.nodeCount(); irebuild(serial); } - virtual ~NodeManager() {} + NodeManager(const NodeManager&) = delete; // disallow copy-construction /// @brief Clear all the cached tree nodes - void clear() { mList0.clear(); mList1.clear(); mList2.clear(); mList3.clear; } + void clear() { mList0.clear(); mList1.clear(); mList2.clear(); mList3.clear(); } /// @brief Clear and recache all the tree nodes from the /// tree. This is required if tree nodes have been added or removed. - void rebuild() + void rebuild(bool serial = false) { - this->clear(); - mRoot.getNodes(mList3); - for (size_t i=0, n=mList3.nodeCount(); i protected: using NodeT4 = RootNodeType; - using NodeT3 = typename NodeT4::ChildNodeType; // upper level - using NodeT2 = typename NodeT3::ChildNodeType; // upper mid level - using NodeT1 = typename NodeT2::ChildNodeType; // lower mid level - using NodeT0 = typename NodeT1::ChildNodeType; // lower level + using NonConstNodeT3 = typename NodeT4::ChildNodeType; + using NodeT3 = typename CopyConstness::Type; // upper level + using NonConstNodeT2 = typename NodeT3::ChildNodeType; + using NodeT2 = typename CopyConstness::Type; // upper mid level + using NonConstNodeT1 = typename NodeT2::ChildNodeType; + using NodeT1 = typename CopyConstness::Type; // lower mid level + using NonConstNodeT0 = typename NodeT1::ChildNodeType; + using NodeT0 = typename CopyConstness::Type; // lower level using ListT3 = NodeList; // upper level of internal nodes using ListT2 = NodeList; // upper mid level of internal nodes @@ -1005,11 +1441,281 @@ class NodeManager ListT2 mList2; ListT1 mList1; ListT0 mList0; - -private: - NodeManager(const NodeManager&) {} // disallow copy-construction }; // NodeManager<4> + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the DynamicNodeManager with one level of nodes +template +class DynamicNodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL > 0, "expected instantiation of template specialization"); + static const Index LEVELS = 1; + + explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { } + + DynamicNodeManager(const DynamicNodeManager&) = delete; + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + // root + if (!op(mRoot, /*index=*/0)) return; + // list0 + if (!mList0.initRootChildren(mRoot)) return; + ForeachFilterOp nodeOp(op, mList0.nodeCount()); + mList0.foreachWithIndex(nodeOp, threaded, grainSize); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + // root + if (!op(mRoot, /*index=*/0)) return; + // list0 + if (!mList0.initRootChildren(mRoot)) return; + ReduceFilterOp nodeOp(op, mList0.nodeCount()); + mList0.reduceWithIndex(nodeOp, threaded, grainSize); + } + +protected: + using NodeT1 = RootNodeType; + using NodeT0 = typename NodeT1::ChildNodeType; + + using ListT0 = NodeList; + + NodeT1& mRoot; + ListT0 mList0; +};// DynamicNodeManager<1> class + + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the DynamicNodeManager with two levels of nodes +template +class DynamicNodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL > 1, "expected instantiation of template specialization"); + static const Index LEVELS = 2; + + explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { } + + DynamicNodeManager(const DynamicNodeManager&) = delete; + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + // root + if (!op(mRoot, /*index=*/0)) return; + // list1 + if (!mList1.initRootChildren(mRoot)) return; + ForeachFilterOp nodeOp(op, mList1.nodeCount()); + mList1.foreachWithIndex(nodeOp, threaded, grainSize); + // list0 + if (!mList0.initNodeChildren(mList1, nodeOp, !threaded)) return; + mList0.foreachWithIndex(op, threaded, grainSize); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + // root + if (!op(mRoot, /*index=*/0)) return; + // list1 + if (!mList1.initRootChildren(mRoot)) return; + ReduceFilterOp nodeOp(op, mList1.nodeCount()); + mList1.reduceWithIndex(nodeOp, threaded, grainSize); + // list0 + if (!mList0.initNodeChildren(mList1, nodeOp, !threaded)) return; + mList0.reduceWithIndex(op, threaded, grainSize); + } + +protected: + using NodeT2 = RootNodeType; + using NodeT1 = typename NodeT2::ChildNodeType; // upper level + using NodeT0 = typename NodeT1::ChildNodeType; // lower level + + using ListT1 = NodeList; // upper level of internal nodes + using ListT0 = NodeList; // lower level of internal nodes or leafs + + NodeT2& mRoot; + ListT1 mList1; + ListT0 mList0; +};// DynamicNodeManager<2> class + + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the DynamicNodeManager with three levels of nodes +template +class DynamicNodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL > 2, "expected instantiation of template specialization"); + static const Index LEVELS = 3; + + explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { } + + DynamicNodeManager(const DynamicNodeManager&) = delete; + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + // root + if (!op(mRoot, /*index=*/0)) return; + // list2 + if (!mList2.initRootChildren(mRoot)) return; + ForeachFilterOp nodeOp2(op, mList2.nodeCount()); + mList2.foreachWithIndex(nodeOp2, threaded, grainSize); + // list1 + if (!mList1.initNodeChildren(mList2, nodeOp2, !threaded)) return; + ForeachFilterOp nodeOp1(op, mList1.nodeCount()); + mList1.foreachWithIndex(nodeOp1, threaded, grainSize); + // list0 + if (!mList0.initNodeChildren(mList1, nodeOp1, !threaded)) return; + mList0.foreachWithIndex(op, threaded, grainSize); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + // root + if (!op(mRoot, /*index=*/0)) return; + // list2 + if (!mList2.initRootChildren(mRoot)) return; + ReduceFilterOp nodeOp2(op, mList2.nodeCount()); + mList2.reduceWithIndex(nodeOp2, threaded, grainSize); + // list1 + if (!mList1.initNodeChildren(mList2, nodeOp2, !threaded)) return; + ReduceFilterOp nodeOp1(op, mList1.nodeCount()); + mList1.reduceWithIndex(nodeOp1, threaded, grainSize); + // list0 + if (!mList0.initNodeChildren(mList1, nodeOp1, !threaded)) return; + mList0.reduceWithIndex(op, threaded, grainSize); + } + +protected: + using NodeT3 = RootNodeType; + using NodeT2 = typename NodeT3::ChildNodeType; // upper level + using NodeT1 = typename NodeT2::ChildNodeType; // mid level + using NodeT0 = typename NodeT1::ChildNodeType; // lower level + + using ListT2 = NodeList; // upper level of internal nodes + using ListT1 = NodeList; // lower level of internal nodes + using ListT0 = NodeList; // lower level of internal nodes or leafs + + NodeT3& mRoot; + ListT2 mList2; + ListT1 mList1; + ListT0 mList0; +};// DynamicNodeManager<3> class + + +//////////////////////////////////////////// + + +/// @private +/// Template specialization of the DynamicNodeManager with four levels of nodes +template +class DynamicNodeManager +{ +public: + using RootNodeType = typename TreeOrLeafManagerT::RootNodeType; + static_assert(RootNodeType::LEVEL > 3, "expected instantiation of template specialization"); + static const Index LEVELS = 4; + + explicit DynamicNodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { } + + DynamicNodeManager(const DynamicNodeManager&) = delete; + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + // root + if (!op(mRoot, /*index=*/0)) return; + // list3 + if (!mList3.initRootChildren(mRoot)) return; + ForeachFilterOp nodeOp3(op, mList3.nodeCount()); + mList3.foreachWithIndex(nodeOp3, threaded, grainSize); + // list2 + if (!mList2.initNodeChildren(mList3, nodeOp3, !threaded)) return; + ForeachFilterOp nodeOp2(op, mList2.nodeCount()); + mList2.foreachWithIndex(nodeOp2, threaded, grainSize); + // list1 + if (!mList1.initNodeChildren(mList2, nodeOp2, !threaded)) return; + ForeachFilterOp nodeOp1(op, mList1.nodeCount()); + mList1.foreachWithIndex(nodeOp1, threaded, grainSize); + // list0 + if (!mList0.initNodeChildren(mList1, nodeOp1, !threaded)) return; + mList0.foreachWithIndex(op, threaded, grainSize); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + // root + if (!op(mRoot, /*index=*/0)) return; + // list3 + if (!mList3.initRootChildren(mRoot)) return; + ReduceFilterOp nodeOp3(op, mList3.nodeCount()); + mList3.reduceWithIndex(nodeOp3, threaded, grainSize); + // list2 + if (!mList2.initNodeChildren(mList3, nodeOp3, !threaded)) return; + ReduceFilterOp nodeOp2(op, mList2.nodeCount()); + mList2.reduceWithIndex(nodeOp2, threaded, grainSize); + // list1 + if (!mList1.initNodeChildren(mList2, nodeOp2, !threaded)) return; + ReduceFilterOp nodeOp1(op, mList1.nodeCount()); + mList1.reduceWithIndex(nodeOp1, threaded, grainSize); + // list0 + if (!mList0.initNodeChildren(mList1, nodeOp1, !threaded)) return; + mList0.reduceWithIndex(op, threaded, grainSize); + } + +protected: + using NodeT4 = RootNodeType; + using NodeT3 = typename NodeT4::ChildNodeType; // upper level + using NodeT2 = typename NodeT3::ChildNodeType; // upper mid level + using NodeT1 = typename NodeT2::ChildNodeType; // lower mid level + using NodeT0 = typename NodeT1::ChildNodeType; // lower level + + using ListT3 = NodeList; // upper level of internal nodes + using ListT2 = NodeList; // upper mid level of internal nodes + using ListT1 = NodeList; // lower mid level of internal nodes + using ListT0 = NodeList; // lower level of internal nodes or leafs + + NodeT4& mRoot; + ListT3 mList3; + ListT2 mList2; + ListT1 mList1; + ListT0 mList0; +};// DynamicNodeManager<4> class + + } // namespace tree } // namespace OPENVDB_VERSION_NAME } // namespace openvdb diff --git a/openvdb/openvdb/tree/Tree.h b/openvdb/openvdb/tree/Tree.h index 513f33635a..09bad9e5f9 100644 --- a/openvdb/openvdb/tree/Tree.h +++ b/openvdb/openvdb/tree/Tree.h @@ -929,176 +929,36 @@ class Tree: public TreeBase bool prune = false); #endif - /// @brief Use sparse traversal to call the given functor with bounding box - /// information for all active tiles and leaf nodes or active voxels in the tree. - /// - /// @note The bounding boxes are guaranteed to be non-overlapping. - /// @param op a functor with a templated call operator of the form - /// template void operator()(const CoordBBox& bbox), - /// where bbox is the bounding box of either an active tile - /// (if @c LEVEL > 0), a leaf node or an active voxel. - /// The functor must also provide a templated method of the form - /// template bool descent() that returns @c false - /// if bounding boxes below the specified tree level are not to be visited. - /// In such cases of early tree termination, a bounding box is instead - /// derived from each terminating child node. - /// - /// @par Example: - /// Visit and process all active tiles and leaf nodes in a tree, but don't - /// descend to the active voxels. The smallest bounding boxes that will be - /// visited are those of leaf nodes or level-1 active tiles. - /// @code - /// { - /// struct ProcessTilesAndLeafNodes { - /// // Descend to leaf nodes, but no further. - /// template inline bool descent() { return LEVEL > 0; } - /// // Use this version to descend to voxels: - /// //template inline bool descent() { return true; } - /// - /// template - /// inline void operator()(const CoordBBox &bbox) { - /// if (LEVEL > 0) { - /// // code to process an active tile - /// } else { - /// // code to process a leaf node - /// } - /// } - /// }; - /// ProcessTilesAndLeafNodes op; - /// aTree.visitActiveBBox(op); - /// } - /// @endcode - /// @see openvdb/unittest/TestTree.cc for another example. - template void visitActiveBBox(BBoxOp& op) const { mRoot.visitActiveBBox(op); } - - /// Traverse this tree in depth-first order, and at each node call the given functor - /// with a @c DenseIterator (see Iterator.h) that points to either a child node or a - /// tile value. If the iterator points to a child node and the functor returns true, - /// do not descend to the child node; instead, continue the traversal at the next - /// iterator position. - /// @param op a functor of the form template bool op(IterT&), - /// where @c IterT is either a RootNode::ChildAllIter, - /// an InternalNode::ChildAllIter or a LeafNode::ChildAllIter - /// - /// @note There is no iterator that points to a RootNode, so to visit the root node, - /// retrieve the @c parent() of a RootNode::ChildAllIter. - /// - /// @par Example: - /// Print information about the nodes and tiles of a tree, but not individual voxels. - /// @code - /// namespace { - /// template - /// struct PrintTreeVisitor - /// { - /// using RootT = typename TreeT::RootNodeType; - /// bool visitedRoot; - /// - /// PrintTreeVisitor(): visitedRoot(false) {} - /// - /// template - /// inline bool operator()(IterT& iter) - /// { - /// if (!visitedRoot && iter.parent().getLevel() == RootT::LEVEL) { - /// visitedRoot = true; - /// std::cout << "Level-" << RootT::LEVEL << " node" << std::endl; - /// } - /// typename IterT::NonConstValueType value; - /// typename IterT::ChildNodeType* child = iter.probeChild(value); - /// if (child == nullptr) { - /// std::cout << "Tile with value " << value << std::endl; - /// return true; // no child to visit, so stop descending - /// } - /// std::cout << "Level-" << child->getLevel() << " node" << std::endl; - /// return (child->getLevel() == 0); // don't visit leaf nodes - /// } - /// - /// // The generic method, above, calls iter.probeChild(), which is not defined - /// // for LeafNode::ChildAllIter. These overloads ensure that the generic - /// // method template doesn't get instantiated for LeafNode iterators. - /// bool operator()(typename TreeT::LeafNodeType::ChildAllIter&) { return true; } - /// bool operator()(typename TreeT::LeafNodeType::ChildAllCIter&) { return true; } - /// }; - /// } - /// { - /// PrintTreeVisitor visitor; - /// tree.visit(visitor); - /// } - /// @endcode - template void visit(VisitorOp& op); - template void visit(const VisitorOp& op); - - /// Like visit(), but using @c const iterators, i.e., with - /// @param op a functor of the form template bool op(IterT&), - /// where @c IterT is either a RootNode::ChildAllCIter, - /// an InternalNode::ChildAllCIter or a LeafNode::ChildAllCIter - template void visit(VisitorOp& op) const; - template void visit(const VisitorOp& op) const; - - /// Traverse this tree and another tree in depth-first order, and for corresponding - /// subregions of index space call the given functor with two @c DenseIterators - /// (see Iterator.h), each of which points to either a child node or a tile value - /// of this tree and the other tree. If the A iterator points to a child node - /// and the functor returns a nonzero value with bit 0 set (e.g., 1), do not descend - /// to the child node; instead, continue the traversal at the next A iterator position. - /// Similarly, if the B iterator points to a child node and the functor returns a value - /// with bit 1 set (e.g., 2), continue the traversal at the next B iterator position. - /// @note The other tree must have the same index space and fan-out factors as - /// this tree, but it may have a different @c ValueType and a different topology. - /// @param other a tree of the same type as this tree - /// @param op a functor of the form - /// template int op(AIterT&, BIterT&), - /// where @c AIterT and @c BIterT are any combination of a - /// RootNode::ChildAllIter, an InternalNode::ChildAllIter or a - /// LeafNode::ChildAllIter with an @c OtherTreeType::RootNode::ChildAllIter, - /// an @c OtherTreeType::InternalNode::ChildAllIter - /// or an @c OtherTreeType::LeafNode::ChildAllIter - /// - /// @par Example: - /// Given two trees of the same type, @c aTree and @c bTree, replace leaf nodes of - /// @c aTree with corresponding leaf nodes of @c bTree, leaving @c bTree partially empty. - /// @code - /// namespace { - /// template - /// inline int stealLeafNodes(AIterT& aIter, BIterT& bIter) - /// { - /// typename AIterT::NonConstValueType aValue; - /// typename AIterT::ChildNodeType* aChild = aIter.probeChild(aValue); - /// typename BIterT::NonConstValueType bValue; - /// typename BIterT::ChildNodeType* bChild = bIter.probeChild(bValue); - /// - /// const Index aLevel = aChild->getLevel(), bLevel = bChild->getLevel(); - /// if (aChild && bChild && aLevel == 0 && bLevel == 0) { // both are leaf nodes - /// aIter.setChild(bChild); // give B's child to A - /// bIter.setValue(bValue); // replace B's child with a constant tile value - /// } - /// // Don't iterate over leaf node voxels of either A or B. - /// int skipBranch = (aLevel == 0) ? 1 : 0; - /// if (bLevel == 0) skipBranch = skipBranch | 2; - /// return skipBranch; - /// } - /// } - /// { - /// aTree.visit2(bTree, stealLeafNodes); - /// } - /// @endcode + template + [[deprecated("Use DynamicNodeManager instead")]] + void visitActiveBBox(BBoxOp& op) const { mRoot.visitActiveBBox(op); } + + template + [[deprecated("Use DynamicNodeManager instead")]] + void visit(VisitorOp& op); + template + [[deprecated("Use DynamicNodeManager instead")]] + void visit(const VisitorOp& op); + + template + [[deprecated("Use DynamicNodeManager instead")]] + void visit(VisitorOp& op) const; + template + [[deprecated("Use DynamicNodeManager instead")]] + void visit(const VisitorOp& op) const; + template + [[deprecated("Use DynamicNodeManager instead")]] void visit2(OtherTreeType& other, VisitorOp& op); template + [[deprecated("Use DynamicNodeManager instead")]] void visit2(OtherTreeType& other, const VisitorOp& op); - /// Like visit2(), but using @c const iterators, i.e., with - /// @param other a tree of the same type as this tree - /// @param op a functor of the form - /// template int op(AIterT&, BIterT&), - /// where @c AIterT and @c BIterT are any combination of a - /// RootNode::ChildAllCIter, an InternalNode::ChildAllCIter - /// or a LeafNode::ChildAllCIter with an - /// @c OtherTreeType::RootNode::ChildAllCIter, - /// an @c OtherTreeType::InternalNode::ChildAllCIter - /// or an @c OtherTreeType::LeafNode::ChildAllCIter template + [[deprecated("Use DynamicNodeManager instead")]] void visit2(OtherTreeType& other, VisitorOp& op) const; template + [[deprecated("Use DynamicNodeManager instead")]] void visit2(OtherTreeType& other, const VisitorOp& op) const; diff --git a/openvdb/openvdb/unittest/CMakeLists.txt b/openvdb/openvdb/unittest/CMakeLists.txt index 16507af881..d63c8ef729 100644 --- a/openvdb/openvdb/unittest/CMakeLists.txt +++ b/openvdb/openvdb/unittest/CMakeLists.txt @@ -102,6 +102,7 @@ set(UNITTEST_SOURCE_FILES TestMat4Metadata.cc TestMath.cc TestMeanCurvature.cc + TestMerge.cc TestMeshToVolume.cc TestMetadata.cc TestMetadataIO.cc diff --git a/openvdb/openvdb/unittest/TestMerge.cc b/openvdb/openvdb/unittest/TestMerge.cc new file mode 100644 index 0000000000..6baa446654 --- /dev/null +++ b/openvdb/openvdb/unittest/TestMerge.cc @@ -0,0 +1,1736 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include + +#include + +#include + +using namespace openvdb; + +class TestMerge: public CppUnit::TestCase +{ +public: + CPPUNIT_TEST_SUITE(TestMerge); + CPPUNIT_TEST(testTreeToMerge); + CPPUNIT_TEST(testCsgUnion); + CPPUNIT_TEST(testCsgIntersection); + CPPUNIT_TEST(testCsgDifference); + CPPUNIT_TEST_SUITE_END(); + + void testTreeToMerge(); + void testCsgUnion(); + void testCsgIntersection(); + void testCsgDifference(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestMerge); + +namespace +{ + +auto getTileCount = [](const auto& node) -> Index +{ + Index sum = 0; + for (auto iter = node.cbeginValueAll(); iter; ++iter) sum++; + return sum; +}; + +auto getActiveTileCount = [](const auto& node) -> Index +{ + Index sum = 0; + for (auto iter = node.cbeginValueOn(); iter; ++iter) sum++; + return sum; +}; + +auto getInactiveTileCount = [](const auto& node) -> Index +{ + Index sum = 0; + for (auto iter = node.cbeginValueOff(); iter; ++iter) sum++; + return sum; +}; + +auto getInsideTileCount = [](const auto& node) -> Index +{ + using ValueT = typename std::remove_reference::type::ValueType; + Index sum = 0; + for (auto iter = node.cbeginValueAll(); iter; ++iter) { + if (iter.getValue() < zeroVal()) sum++; + } + return sum; +}; + +auto getOutsideTileCount = [](const auto& node) -> Index +{ + using ValueT = typename std::remove_reference::type::ValueType; + Index sum = 0; + for (auto iter = node.cbeginValueAll(); iter; ++iter) { + if (iter.getValue() > zeroVal()) sum++; + } + return sum; +}; + +auto getChildCount = [](const auto& node) -> Index +{ + return node.childCount(); +}; + +} // namespace + + +void +TestMerge::testTreeToMerge() +{ + using RootChildNode = FloatTree::RootNodeType::ChildNodeType; + using LeafNode = FloatTree::LeafNodeType; + + { // non-const tree + FloatGrid::Ptr grid = createLevelSet(); + grid->tree().touchLeaf(Coord(8)); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().leafCount()); + + tools::TreeToMerge treeToMerge{grid->tree(), Steal()}; + CPPUNIT_ASSERT_EQUAL(&grid->constTree().root(), treeToMerge.rootPtr()); + + // probe root child + + const RootChildNode* nodePtr = treeToMerge.probeConstNode(Coord(8)); + CPPUNIT_ASSERT(nodePtr); + CPPUNIT_ASSERT_EQUAL(grid->constTree().probeConstNode(Coord(8)), nodePtr); + + // probe leaf node + + const LeafNode* leafNode = treeToMerge.probeConstNode(Coord(8)); + CPPUNIT_ASSERT(leafNode); + CPPUNIT_ASSERT_EQUAL(grid->constTree().probeConstLeaf(Coord(8)), leafNode); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().root().childCount()); + + // steal leaf node + + std::unique_ptr leafNodePtr = treeToMerge.stealOrDeepCopyNode(Coord(8)); + CPPUNIT_ASSERT(leafNodePtr); + CPPUNIT_ASSERT_EQUAL(Index(0), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(leafNodePtr->origin(), Coord(8)); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().root().childCount()); + + // steal root child + + grid->tree().touchLeaf(Coord(8)); + std::unique_ptr node2Ptr = treeToMerge.stealOrDeepCopyNode(Coord(8)); + CPPUNIT_ASSERT(node2Ptr); + CPPUNIT_ASSERT_EQUAL(Index(0), grid->tree().root().childCount()); + + // attempt to add leaf node tile (set value) + + grid->tree().touchLeaf(Coord(8)); + CPPUNIT_ASSERT_EQUAL(Index64(0), grid->tree().activeTileCount()); + treeToMerge.addTile(Coord(8), 1.6f, true); + // value has not been set + CPPUNIT_ASSERT_EQUAL(3.0f, grid->tree().probeConstLeaf(Coord(8))->getFirstValue()); + + // add root child tile + + treeToMerge.addTile(Coord(8), 1.7f, true); + CPPUNIT_ASSERT_EQUAL(Index64(1), grid->tree().activeTileCount()); + + // tile in node that does not exist + + grid->tree().clear(); + treeToMerge.addTile(Coord(0), 1.8f, true); + CPPUNIT_ASSERT_EQUAL(Index64(0), grid->tree().activeTileCount()); + } + + { // const tree + FloatGrid::Ptr grid = createLevelSet(); + grid->tree().touchLeaf(Coord(8)); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().leafCount()); + + tools::TreeToMerge treeToMerge{grid->constTree(), DeepCopy(), /*initialize=*/false}; + CPPUNIT_ASSERT(!treeToMerge.hasMask()); + treeToMerge.initializeMask(); + CPPUNIT_ASSERT(treeToMerge.hasMask()); + CPPUNIT_ASSERT_EQUAL(&grid->constTree().root(), treeToMerge.rootPtr()); + + // probe root child + + const RootChildNode* nodePtr = treeToMerge.probeConstNode(Coord(8)); + CPPUNIT_ASSERT(nodePtr); + CPPUNIT_ASSERT_EQUAL(grid->constTree().probeConstNode(Coord(8)), nodePtr); + + // probe leaf node + + const LeafNode* leafNode = treeToMerge.probeConstNode(Coord(8)); + CPPUNIT_ASSERT(leafNode); + CPPUNIT_ASSERT_EQUAL(grid->constTree().probeConstLeaf(Coord(8)), leafNode); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().root().childCount()); + + { // deep copy leaf node + tools::TreeToMerge treeToMerge2{grid->constTree(), DeepCopy()}; + std::unique_ptr leafNodePtr = treeToMerge2.stealOrDeepCopyNode(Coord(8)); + CPPUNIT_ASSERT(leafNodePtr); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().leafCount()); // leaf has not been stolen + CPPUNIT_ASSERT_EQUAL(leafNodePtr->origin(), Coord(8)); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().root().childCount()); + } + + { // deep copy root child + tools::TreeToMerge treeToMerge2{grid->constTree(), DeepCopy()}; + grid->tree().touchLeaf(Coord(8)); + std::unique_ptr node2Ptr = treeToMerge2.stealOrDeepCopyNode(Coord(8)); + CPPUNIT_ASSERT(node2Ptr); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().root().childCount()); + } + + { // add root child tile + tools::TreeToMerge treeToMerge2{grid->constTree(), DeepCopy()}; + CPPUNIT_ASSERT(treeToMerge2.probeConstNode(Coord(8))); + treeToMerge2.addTile(Coord(8), 1.7f, true); + CPPUNIT_ASSERT(!treeToMerge2.probeConstNode(Coord(8))); // tile has been added to mask + CPPUNIT_ASSERT_EQUAL(Index64(0), grid->tree().activeTileCount()); + } + + // tile in node that does not exist + + grid->tree().clear(); + treeToMerge.addTile(Coord(0), 1.8f, true); + CPPUNIT_ASSERT_EQUAL(Index64(0), grid->tree().activeTileCount()); + } + + { // non-const tree shared pointer + { // shared pointer constructor + FloatGrid::Ptr grid = createLevelSet(); + grid->tree().touchLeaf(Coord(8)); + tools::TreeToMerge treeToMerge(grid->treePtr(), Steal()); + + // verify tree shared ownership + + CPPUNIT_ASSERT(treeToMerge.treeToSteal()); + CPPUNIT_ASSERT(!treeToMerge.treeToDeepCopy()); + CPPUNIT_ASSERT(treeToMerge.rootPtr()); + CPPUNIT_ASSERT(treeToMerge.probeConstNode(Coord(8))); + } + + // empty tree + FloatTree tree; + tools::TreeToMerge treeToMerge(tree, DeepCopy()); + CPPUNIT_ASSERT(!treeToMerge.treeToSteal()); + CPPUNIT_ASSERT(treeToMerge.treeToDeepCopy()); + CPPUNIT_ASSERT(treeToMerge.rootPtr()); + CPPUNIT_ASSERT(!treeToMerge.probeConstNode(Coord(8))); + + { + FloatTree::Ptr emptyPtr; + CPPUNIT_ASSERT_THROW(treeToMerge.reset(emptyPtr, Steal()), RuntimeError); + + FloatGrid::Ptr grid = createLevelSet(); + grid->tree().touchLeaf(Coord(8)); + CPPUNIT_ASSERT_EQUAL(Index(1), grid->tree().leafCount()); + + treeToMerge.reset(grid->treePtr(), Steal()); + } + + // verify tree shared ownership + + CPPUNIT_ASSERT(treeToMerge.treeToSteal()); + CPPUNIT_ASSERT(!treeToMerge.treeToDeepCopy()); + CPPUNIT_ASSERT(treeToMerge.rootPtr()); + CPPUNIT_ASSERT(treeToMerge.probeConstNode(Coord(8))); + + // verify tree pointers are updated on reset() + + const FloatTree tree2; + tools::TreeToMerge treeToMerge2(tree2, DeepCopy()); + treeToMerge2.initializeMask(); // no-op + + CPPUNIT_ASSERT(!treeToMerge2.treeToSteal()); + CPPUNIT_ASSERT(treeToMerge2.treeToDeepCopy()); + CPPUNIT_ASSERT_EQUAL(Index(0), treeToMerge2.treeToDeepCopy()->leafCount()); + + FloatGrid::Ptr grid = createLevelSet(); + grid->tree().touchLeaf(Coord(8)); + treeToMerge2.reset(grid->treePtr(), Steal()); + + CPPUNIT_ASSERT(treeToMerge2.treeToSteal()); + CPPUNIT_ASSERT(!treeToMerge2.treeToDeepCopy()); + CPPUNIT_ASSERT_EQUAL(Index(1), treeToMerge2.treeToSteal()->leafCount()); + } +} + +void +TestMerge::testCsgUnion() +{ + { // construction + FloatTree tree1; + FloatTree tree2; + const FloatTree tree3; + + { // one non-const tree (steal) + tools::CsgUnionOp mergeOp(tree1, Steal()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // one non-const tree (deep-copy) + tools::CsgUnionOp mergeOp(tree1, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // one const tree (deep-copy) + tools::CsgUnionOp mergeOp(tree2, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // vector of tree pointers + std::vector trees{&tree1, &tree2}; + tools::CsgUnionOp mergeOp(trees, Steal()); + CPPUNIT_ASSERT_EQUAL(size_t(2), mergeOp.size()); + } + { // deque of tree pointers + std::deque trees{&tree1, &tree2}; + tools::CsgUnionOp mergeOp(trees, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(2), mergeOp.size()); + } + { // vector of TreesToMerge (to mix const and non-const trees) + std::vector> trees; + trees.emplace_back(tree1, Steal()); + trees.emplace_back(tree3, DeepCopy()); // const tree + trees.emplace_back(tree2, Steal()); + tools::CsgUnionOp mergeOp(trees); + CPPUNIT_ASSERT_EQUAL(size_t(3), mergeOp.size()); + } + { // implicit copy constructor + std::vector trees{&tree1, &tree2}; + tools::CsgUnionOp mergeOp(trees, Steal()); + tools::CsgUnionOp mergeOp2(mergeOp); + CPPUNIT_ASSERT_EQUAL(size_t(2), mergeOp2.size()); + } + { // implicit assignment operator + std::vector trees{&tree1, &tree2}; + tools::CsgUnionOp mergeOp(trees, Steal()); + tools::CsgUnionOp mergeOp2 = mergeOp; + CPPUNIT_ASSERT_EQUAL(size_t(2), mergeOp2.size()); + } + } + + { // empty merge trees + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + std::vector trees; + tools::CsgUnionOp mergeOp(trees, Steal()); + + CPPUNIT_ASSERT_EQUAL(size_t(0), mergeOp.size()); + + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(0), root.getTableSize()); + } + + { // merge two different outside root tiles from one grid into an empty grid + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), false); + root2.addTile(Coord(8192, 0, 0), grid->background(), true); + + CPPUNIT_ASSERT_EQUAL(Index(2), root2.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root2)); + + // test container constructor here + std::vector trees{&grid2->tree()}; + tools::CsgUnionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getOutsideTileCount(root)); + } + + { // merge two different outside root tiles from two grids into an empty grid + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + FloatGrid::Ptr grid3 = createLevelSet(); + auto& root3 = grid3->tree().root(); + root2.addTile(Coord(0, 0, 0), /*background=*/123.0f, false); + root3.addTile(Coord(8192, 0, 0), /*background=*/0.1f, true); + + std::vector trees{&grid2->tree(), &grid3->tree()}; + tools::CsgUnionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getOutsideTileCount(root)); + + // background values of merge trees should be ignored, only important + // that the values are greater than zero and thus an outside tile + for (auto iter = root.cbeginValueAll(); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL(grid->background(), iter.getValue()); + } + } + + { // merge the same outside root tiles from two grids into an empty grid + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + FloatGrid::Ptr grid3 = createLevelSet(); + auto& root3 = grid3->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), true); + root3.addTile(Coord(0, 0, 0), grid->background(), false); + + std::vector trees{&grid2->tree(), &grid3->tree()}; + tools::CsgUnionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // merge order is important - tile should be active + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInactiveTileCount(root)); + + root.clear(); + // reverse tree order + std::vector trees2{&grid3->tree(), &grid2->tree()}; + tools::CsgUnionOp mergeOp2(trees2, Steal()); + nodeManager.foreachTopDown(mergeOp2); + // merge order is important - tile should now be inactive + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + } + + { // merge an outside root tile to a grid which already has this tile + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), grid->background(), false); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), true); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // tile in merge grid should not replace existing tile - tile should remain inactive + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + } + + { // merge an inside root tile to a grid which has an outside tile, inside takes precedence + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), grid->background(), false); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), -123.0f, true); + + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // tile in merge grid replace existing tile - tile should now be active and inside + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getOutsideTileCount(root)); + } + + { // merge two grids with an outside and an inside tile, inside takes precedence + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), true); + FloatGrid::Ptr grid3 = createLevelSet(); + auto& root3 = grid3->tree().root(); + root3.addTile(Coord(0, 0, 0), /*inside*/-0.1f, false); + + std::vector trees{&grid2->tree(), &grid3->tree()}; + tools::CsgUnionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // tile in merge grid should not replace existing tile - tile should remain inactive + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getOutsideTileCount(root)); + } + + { // merge two child nodes into an empty grid + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(0, 0, 0), 1.0f, false)); + root2.addChild(new RootChildType(Coord(8192, 0, 0), -123.0f, true)); + + CPPUNIT_ASSERT_EQUAL(Index(2), root2.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root2)); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root)); + } + + { // merge a child node into a grid with an outside tile + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), 123.0f, true); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(0, 0, 0), 1.0f, false)); + + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root2)); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root)); + } + + { // merge a child node into a grid with an existing child node + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addChild(new RootChildType(Coord(0, 0, 0), 123.0f, false)); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(8192, 0, 0), 1.9f, false)); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root2)); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root)); + CPPUNIT_ASSERT(root.cbeginChildOn()->cbeginValueAll()); + CPPUNIT_ASSERT_EQUAL(123.0f, root.cbeginChildOn()->cbeginValueAll().getItem(0)); + CPPUNIT_ASSERT_EQUAL(1.9f, (++root.cbeginChildOn())->cbeginValueAll().getItem(0)); + } + + { // merge an inside tile and an outside tile into a grid with two child nodes + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addChild(new RootChildType(Coord(0, 0, 0), 123.0f, false)); + root.addChild(new RootChildType(Coord(8192, 0, 0), 1.9f, false)); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), 15.0f, false); // should not replace child + root2.addTile(Coord(8192, 0, 0), -25.0f, false); // should replace child + + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root2)); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT(root.cbeginChildAll().isChildNode()); + CPPUNIT_ASSERT(!(++root.cbeginChildAll()).isChildNode()); + CPPUNIT_ASSERT_EQUAL(123.0f, root.cbeginChildOn()->getFirstValue()); + // inside tile value replaced with negative background + CPPUNIT_ASSERT_EQUAL(-grid->background(), root.cbeginValueAll().getValue()); + } + + { // merge two child nodes into a grid with an inside tile and an outside tile + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), 15.0f, false); // should be replaced by child + root.addTile(Coord(8192, 0, 0), -25.0f, false); // should not be replaced by child + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(0, 0, 0), 123.0f, false)); + root2.addChild(new RootChildType(Coord(8192, 0, 0), 1.9f, false)); + + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root2)); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT(root.cbeginChildAll().isChildNode()); + CPPUNIT_ASSERT(!(++root.cbeginChildAll()).isChildNode()); + CPPUNIT_ASSERT_EQUAL(123.0f, root.cbeginChildOn()->getFirstValue()); + CPPUNIT_ASSERT_EQUAL(-grid->background(), root.cbeginValueAll().getValue()); + } + + { // merge two internal nodes into a grid with an inside tile and an outside tile + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + using LeafParentType = RootChildType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + auto rootChild = std::make_unique(Coord(0, 0, 0), 123.0f, false); + rootChild->addTile(1, -14.0f, false); + root.addChild(rootChild.release()); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + auto rootChild2 = std::make_unique(Coord(0, 0, 0), 55.0f, false); + + rootChild2->addChild(new LeafParentType(Coord(0, 0, 0), 29.0f, false)); + rootChild2->addChild(new LeafParentType(Coord(0, 0, 128), 31.0f, false)); + rootChild2->addTile(2, 17.0f, true); + rootChild2->addTile(9, -19.0f, true); + root2.addChild(rootChild2.release()); + + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(*root.cbeginChildOn())); + + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(*root2.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(*root2.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(2), getActiveTileCount(*root2.cbeginChildOn())); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(2), getInsideTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT(root.cbeginChildOn()->isChildMaskOn(0)); + CPPUNIT_ASSERT(!root.cbeginChildOn()->isChildMaskOn(1)); + CPPUNIT_ASSERT_EQUAL(29.0f, root.cbeginChildOn()->cbeginChildOn()->getFirstValue()); + CPPUNIT_ASSERT_EQUAL(-14.0f, root.cbeginChildOn()->cbeginValueAll().getValue()); + + CPPUNIT_ASSERT_EQUAL(Index(0), getChildCount(*root2.cbeginChildOn())); + } + + { // merge two internal nodes into a grid with an inside tile and an outside tile + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + using LeafParentType = RootChildType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + auto rootChild = std::make_unique(Coord(0, 0, 0), 123.0f, false); + rootChild->addTile(1, -14.0f, false); + root.addChild(rootChild.release()); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + auto rootChild2 = std::make_unique(Coord(0, 0, 0), 55.0f, false); + + rootChild2->addChild(new LeafParentType(Coord(0, 0, 0), 29.0f, false)); + rootChild2->addChild(new LeafParentType(Coord(0, 0, 128), 31.0f, false)); + rootChild2->addTile(2, 17.0f, true); + rootChild2->addTile(9, -19.0f, true); + root2.addChild(rootChild2.release()); + + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(*root.cbeginChildOn())); + + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(*root2.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(*root2.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(2), getActiveTileCount(*root2.cbeginChildOn())); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(2), getInsideTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT(root.cbeginChildOn()->isChildMaskOn(0)); + CPPUNIT_ASSERT(!root.cbeginChildOn()->isChildMaskOn(1)); + CPPUNIT_ASSERT_EQUAL(29.0f, root.cbeginChildOn()->cbeginChildOn()->getFirstValue()); + CPPUNIT_ASSERT_EQUAL(-14.0f, root.cbeginChildOn()->cbeginValueAll().getValue()); + + CPPUNIT_ASSERT_EQUAL(Index(0), getChildCount(*root2.cbeginChildOn())); + } + + { // merge a leaf node into an empty grid + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + + grid2->tree().touchLeaf(Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(Index32(0), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(0), grid2->tree().leafCount()); + } + + { // merge a leaf node into a grid with a partially constructed leaf node + using LeafT = FloatTree::LeafNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + + grid->tree().addLeaf(new LeafT(PartialCreate(), Coord(0, 0, 0))); + auto* leaf = grid2->tree().touchLeaf(Coord(0, 0, 0)); + leaf->setValueOnly(10, -2.3f); + + tools::CsgUnionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + const auto* testLeaf = grid->tree().probeConstLeaf(Coord(0, 0, 0)); + CPPUNIT_ASSERT_EQUAL(-2.3f, testLeaf->getValue(10)); + } + + { // merge three leaf nodes from different grids + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + FloatGrid::Ptr grid3 = createLevelSet(); + + auto* leaf = grid->tree().touchLeaf(Coord(0, 0, 0)); + auto* leaf2 = grid2->tree().touchLeaf(Coord(0, 0, 0)); + auto* leaf3 = grid3->tree().touchLeaf(Coord(0, 0, 0)); + + // active state from the voxel with the minimum value preserved + + leaf->setValueOnly(5, 4.0f); + leaf2->setValueOnly(5, 2.0f); + leaf2->setValueOn(5); + leaf3->setValueOnly(5, 3.0f); + + leaf->setValueOnly(7, 2.0f); + leaf->setValueOn(7); + leaf2->setValueOnly(7, 3.0f); + leaf3->setValueOnly(7, 4.0f); + + leaf->setValueOnly(9, 4.0f); + leaf->setValueOn(9); + leaf2->setValueOnly(9, 3.0f); + leaf3->setValueOnly(9, 2.0f); + + std::vector trees{&grid2->tree(), &grid3->tree()}; + tools::CsgUnionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + const auto* testLeaf = grid->tree().probeConstLeaf(Coord(0, 0, 0)); + CPPUNIT_ASSERT_EQUAL(2.0f, testLeaf->getValue(5)); + CPPUNIT_ASSERT(testLeaf->isValueOn(5)); + CPPUNIT_ASSERT_EQUAL(2.0f, testLeaf->getValue(7)); + CPPUNIT_ASSERT(testLeaf->isValueOn(7)); + CPPUNIT_ASSERT_EQUAL(2.0f, testLeaf->getValue(9)); + CPPUNIT_ASSERT(!testLeaf->isValueOn(9)); + } + + { // merge a leaf node into an empty grid from a const grid + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + + grid2->tree().touchLeaf(Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(Index32(0), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + + // merge from a const tree + + std::vector> treesToMerge; + treesToMerge.emplace_back(grid2->constTree(), DeepCopy()); + + tools::CsgUnionOp mergeOp(treesToMerge); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid->tree().leafCount()); + // leaf has been deep copied not stolen + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + } +} + +void +TestMerge::testCsgIntersection() +{ + { // construction + FloatTree tree1; + FloatTree tree2; + const FloatTree tree3; + + { // one non-const tree (steal) + tools::CsgIntersectionOp mergeOp(tree1, Steal()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // one non-const tree (deep-copy) + tools::CsgIntersectionOp mergeOp(tree1, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // one const tree (deep-copy) + tools::CsgIntersectionOp mergeOp(tree2, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // vector of tree pointers + std::vector trees{&tree1, &tree2}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + CPPUNIT_ASSERT_EQUAL(size_t(2), mergeOp.size()); + } + { // deque of tree pointers + std::deque trees{&tree1, &tree2}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + CPPUNIT_ASSERT_EQUAL(size_t(2), mergeOp.size()); + } + { // vector of TreesToMerge (to mix const and non-const trees) + std::vector> trees; + trees.emplace_back(tree1, Steal()); + trees.emplace_back(tree3, DeepCopy()); // const tree + trees.emplace_back(tree2, Steal()); + tools::CsgIntersectionOp mergeOp(trees); + CPPUNIT_ASSERT_EQUAL(size_t(3), mergeOp.size()); + } + { // implicit copy constructor + std::vector trees{&tree1, &tree2}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + tools::CsgIntersectionOp mergeOp2(mergeOp); + CPPUNIT_ASSERT_EQUAL(size_t(2), mergeOp2.size()); + } + { // implicit assignment operator + std::vector trees{&tree1, &tree2}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + tools::CsgIntersectionOp mergeOp2 = mergeOp; + CPPUNIT_ASSERT_EQUAL(size_t(2), mergeOp2.size()); + } + } + + { // empty merge trees + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + std::vector trees; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + + CPPUNIT_ASSERT_EQUAL(size_t(0), mergeOp.size()); + + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(0), root.getTableSize()); + } + + { // merge two different outside root tiles from one grid into an empty grid + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), false); + root2.addTile(Coord(8192, 0, 0), grid->background(), true); + + CPPUNIT_ASSERT_EQUAL(Index(2), root2.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root2)); + + // test container constructor here + std::vector trees{&grid2->tree()}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getOutsideTileCount(root)); + } + + { // merge two different outside root tiles from two grids into an empty grid + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + FloatGrid::Ptr grid3 = createLevelSet(); + auto& root3 = grid3->tree().root(); + root2.addTile(Coord(0, 0, 0), /*background=*/123.0f, false); + root3.addTile(Coord(8192, 0, 0), /*background=*/0.1f, true); + + std::vector trees{&grid2->tree(), &grid3->tree()}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getOutsideTileCount(root)); + + // background values of merge trees should be ignored, only important + // that the values are greater than zero and thus an outside tile + for (auto iter = root.cbeginValueAll(); iter; ++iter) { + CPPUNIT_ASSERT_EQUAL(grid->background(), iter.getValue()); + } + } + + { // merge the same outside root tiles from two grids into an empty grid + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + FloatGrid::Ptr grid3 = createLevelSet(); + auto& root3 = grid3->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), true); + root3.addTile(Coord(0, 0, 0), grid->background(), false); + + std::vector trees{&grid2->tree(), &grid3->tree()}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // merge order is important - tile should be active + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInactiveTileCount(root)); + + root.clear(); + // reverse tree order + std::vector trees2{&grid3->tree(), &grid2->tree()}; + tools::CsgIntersectionOp mergeOp2(trees2, Steal()); + nodeManager.foreachTopDown(mergeOp2); + // merge order is important - tile should now be inactive + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + } + + { // merge an outside root tile to a grid which already has this tile + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), grid->background(), false); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), true); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // tile in merge grid should not replace existing tile - tile should remain inactive + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + } + + { // merge an outside root tile to a grid which has an inside tile, outside takes precedence + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), -grid->background(), false); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), 123.0f, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getOutsideTileCount(root)); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // tile in merge grid replace existing tile - tile should now be active and outside + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + } + + { // merge two grids with an outside and an inside tile, outside takes precedence + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), -grid->background(), true); + FloatGrid::Ptr grid3 = createLevelSet(); + auto& root3 = grid3->tree().root(); + root3.addTile(Coord(0, 0, 0), /*outside*/0.1f, false); + + std::vector trees{&grid2->tree(), &grid3->tree()}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // tile in merge grid should not replace existing tile - tile should remain inactive + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + } + + { // merge two child nodes into an empty grid + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(0, 0, 0), 1.0f, false)); + root2.addChild(new RootChildType(Coord(8192, 0, 0), -123.0f, true)); + + CPPUNIT_ASSERT_EQUAL(Index(2), root2.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root2)); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root)); + } + + { // merge a child node into a grid with an outside tile + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), 123.0f, true); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(0, 0, 0), 1.0f, false)); + + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root2)); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root)); + } + + { // merge a child node into a grid with an existing child node + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addChild(new RootChildType(Coord(0, 0, 0), 123.0f, false)); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(8192, 0, 0), 1.9f, false)); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root2)); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root)); + CPPUNIT_ASSERT(root.cbeginChildOn()->cbeginValueAll()); + CPPUNIT_ASSERT_EQUAL(123.0f, root.cbeginChildOn()->cbeginValueAll().getItem(0)); + CPPUNIT_ASSERT_EQUAL(1.9f, (++root.cbeginChildOn())->cbeginValueAll().getItem(0)); + } + + { // merge an inside tile and an outside tile into a grid with two child nodes + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addChild(new RootChildType(Coord(0, 0, 0), 123.0f, false)); + root.addChild(new RootChildType(Coord(8192, 0, 0), 1.9f, false)); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), -15.0f, false); // should not replace child + root2.addTile(Coord(8192, 0, 0), 25.0f, false); // should replace child + + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root2)); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT(root.cbeginChildAll().isChildNode()); + CPPUNIT_ASSERT(!(++root.cbeginChildAll()).isChildNode()); + CPPUNIT_ASSERT_EQUAL(123.0f, root.cbeginChildOn()->getFirstValue()); + // outside tile value replaced with background + CPPUNIT_ASSERT_EQUAL(grid->background(), root.cbeginValueAll().getValue()); + } + + { // merge two child nodes into a grid with an inside tile and an outside tile + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), -15.0f, false); // should be replaced by child + root.addTile(Coord(8192, 0, 0), 25.0f, false); // should not be replaced by child + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(0, 0, 0), 123.0f, false)); + root2.addChild(new RootChildType(Coord(8192, 0, 0), 1.9f, false)); + + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root2)); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT(root.cbeginChildAll().isChildNode()); + CPPUNIT_ASSERT(!(++root.cbeginChildAll()).isChildNode()); + CPPUNIT_ASSERT_EQUAL(123.0f, root.cbeginChildOn()->getFirstValue()); + CPPUNIT_ASSERT_EQUAL(grid->background(), root.cbeginValueAll().getValue()); + } + + { // merge two internal nodes into a grid with an inside tile and an outside tile + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + using LeafParentType = RootChildType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + auto rootChild = std::make_unique(Coord(0, 0, 0), 123.0f, false); + rootChild->addTile(0, -14.0f, false); + rootChild->addTile(1, 15.0f, false); + root.addChild(rootChild.release()); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + auto rootChild2 = std::make_unique(Coord(0, 0, 0), 55.0f, false); + + rootChild2->addChild(new LeafParentType(Coord(0, 0, 0), 29.0f, false)); + rootChild2->addChild(new LeafParentType(Coord(0, 0, 128), 31.0f, false)); + rootChild2->addTile(2, -17.0f, true); + rootChild2->addTile(9, 19.0f, true); + root2.addChild(rootChild2.release()); + + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(*root.cbeginChildOn())); + + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(*root2.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(*root2.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(2), getActiveTileCount(*root2.cbeginChildOn())); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT(root.cbeginChildOn()->isChildMaskOn(0)); + CPPUNIT_ASSERT(!root.cbeginChildOn()->isChildMaskOn(1)); + CPPUNIT_ASSERT_EQUAL(29.0f, root.cbeginChildOn()->cbeginChildOn()->getFirstValue()); + CPPUNIT_ASSERT_EQUAL(15.0f, root.cbeginChildOn()->cbeginValueAll().getValue()); + + CPPUNIT_ASSERT_EQUAL(Index(0), getChildCount(*root2.cbeginChildOn())); + } + + { // merge a leaf node into an empty grid + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + + grid2->tree().touchLeaf(Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(Index32(0), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(0), grid2->tree().leafCount()); + } + + { // merge a leaf node into a grid with a partially constructed leaf node + using LeafT = FloatTree::LeafNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + + grid->tree().addLeaf(new LeafT(PartialCreate(), Coord(0, 0, 0))); + auto* leaf = grid2->tree().touchLeaf(Coord(0, 0, 0)); + leaf->setValueOnly(10, 6.4f); + + tools::CsgIntersectionOp mergeOp{grid2->tree(), Steal()}; + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + const auto* testLeaf = grid->tree().probeConstLeaf(Coord(0, 0, 0)); + CPPUNIT_ASSERT_EQUAL(6.4f, testLeaf->getValue(10)); + } + + { // merge three leaf nodes from different grids + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + FloatGrid::Ptr grid3 = createLevelSet(); + + auto* leaf = grid->tree().touchLeaf(Coord(0, 0, 0)); + auto* leaf2 = grid2->tree().touchLeaf(Coord(0, 0, 0)); + auto* leaf3 = grid3->tree().touchLeaf(Coord(0, 0, 0)); + + // active state from the voxel with the maximum value preserved + + leaf->setValueOnly(5, 4.0f); + leaf2->setValueOnly(5, 2.0f); + leaf2->setValueOn(5); + leaf3->setValueOnly(5, 3.0f); + + leaf->setValueOnly(7, 2.0f); + leaf->setValueOn(7); + leaf2->setValueOnly(7, 3.0f); + leaf3->setValueOnly(7, 4.0f); + + leaf->setValueOnly(9, 4.0f); + leaf->setValueOn(9); + leaf2->setValueOnly(9, 3.0f); + leaf3->setValueOnly(9, 2.0f); + + std::vector trees{&grid2->tree(), &grid3->tree()}; + tools::CsgIntersectionOp mergeOp(trees, Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + const auto* testLeaf = grid->tree().probeConstLeaf(Coord(0, 0, 0)); + CPPUNIT_ASSERT_EQUAL(4.0f, testLeaf->getValue(5)); + CPPUNIT_ASSERT(!testLeaf->isValueOn(5)); + CPPUNIT_ASSERT_EQUAL(4.0f, testLeaf->getValue(7)); + CPPUNIT_ASSERT(!testLeaf->isValueOn(7)); + CPPUNIT_ASSERT_EQUAL(4.0f, testLeaf->getValue(9)); + CPPUNIT_ASSERT(testLeaf->isValueOn(9)); + } + + { // merge a leaf node into an empty grid from a const grid + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + + grid2->tree().touchLeaf(Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(Index32(0), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + + // merge from a const tree + + std::vector> treesToMerge; + treesToMerge.emplace_back(grid2->constTree(), DeepCopy()); + + tools::CsgIntersectionOp mergeOp(treesToMerge); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid->tree().leafCount()); + // leaf has been deep copied not stolen + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + } +} + +void +TestMerge::testCsgDifference() +{ + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + { // construction + FloatTree tree1; + const FloatTree tree2; + + { // one non-const tree (steal) + tools::CsgDifferenceOp mergeOp(tree1, Steal()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // one non-const tree (deep-copy) + tools::CsgDifferenceOp mergeOp(tree1, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // one const tree (deep-copy) + tools::CsgDifferenceOp mergeOp(tree2, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // one non-const tree wrapped in TreeToMerge + tools::TreeToMerge tree3(tree1, Steal()); + tools::CsgDifferenceOp mergeOp(tree3); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // one const tree wrapped in TreeToMerge + tools::TreeToMerge tree4(tree2, DeepCopy()); + tools::CsgDifferenceOp mergeOp(tree4); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + } + { // implicit copy constructor + tools::CsgDifferenceOp mergeOp(tree2, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + tools::CsgDifferenceOp mergeOp2(mergeOp); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp2.size()); + } + { // implicit assignment operator + tools::CsgDifferenceOp mergeOp(tree2, DeepCopy()); + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp.size()); + tools::CsgDifferenceOp mergeOp2 = mergeOp; + CPPUNIT_ASSERT_EQUAL(size_t(1), mergeOp2.size()); + } + } + + { // merge two different outside root tiles from one grid into an empty grid (noop) + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), false); + root2.addTile(Coord(8192, 0, 0), grid->background(), true); + + CPPUNIT_ASSERT_EQUAL(Index(2), root2.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root2)); + + // test container constructor here + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(0), root.getTableSize()); + } + + { // merge an outside root tile to a grid which already has this tile + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), grid->background(), false); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), grid->background(), true); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // tile in merge grid should not replace existing tile - tile should remain inactive + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + } + + { // merge an outside root tile to a grid which has an inside tile (noop) + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), -grid->background(), false); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), 123.0f, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getOutsideTileCount(root)); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getOutsideTileCount(root)); + } + + { // merge an outside root tile to a grid which has a child (noop) + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addChild(new RootChildType(Coord(0, 0, 0), 1.0f, false)); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), 123.0f, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + } + + { // merge a child to a grid which has an outside root tile (noop) + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), 123.0f, true); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(0, 0, 0), 1.0f, false)); + + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + } + + { // merge an inside root tile to a grid which has an outside tile (noop) + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), grid->background(), false); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), -123.0f, true); + + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + } + + { // merge two grids with inside tiles, active state should be carried across + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), -0.1f, true); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), -0.2f, false); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + // inside tile should now be inactive + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + + CPPUNIT_ASSERT_EQUAL(grid->background(), root.cbeginValueAll().getValue()); + } + + { // merge an inside root tile to a grid which has a child, inside tile has precedence + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addChild(new RootChildType(Coord(0, 0, 0), 1.0f, false)); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), -123.0f, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInactiveTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + + CPPUNIT_ASSERT_EQUAL(grid->background(), root.cbeginValueAll().getValue()); + } + + { // merge a child to a grid which has an inside root tile, child should be stolen + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), -123.0f, true); + // use a different background value + FloatGrid::Ptr grid2 = createLevelSet(/*voxelSize=*/1.0, /*halfWidth=*/5); + auto& root2 = grid2->tree().root(); + auto childPtr = std::make_unique(Coord(0, 0, 0), 5.0f, false); + childPtr->addTile(Index(1), 1.3f, true); + root2.addChild(childPtr.release()); + + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getOutsideTileCount(root)); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp, true); + + CPPUNIT_ASSERT_EQUAL(Index(1), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getChildCount(root2)); + + CPPUNIT_ASSERT(!root.cbeginChildOn()->isValueOn(Index(0))); + CPPUNIT_ASSERT(root.cbeginChildOn()->isValueOn(Index(1))); + + auto iter = root.cbeginChildOn()->cbeginValueAll(); + CPPUNIT_ASSERT_EQUAL(-3.0f, iter.getValue()); + ++iter; + CPPUNIT_ASSERT_EQUAL(-1.3f, iter.getValue()); + } + + { // merge two child nodes into a grid with two inside tiles + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addTile(Coord(0, 0, 0), -2.0f, false); + root.addTile(Coord(8192, 0, 0), -4.0f, false); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addChild(new RootChildType(Coord(0, 0, 0), 1.0f, false)); + root2.addChild(new RootChildType(Coord(8192, 0, 0), -123.0f, true)); + + CPPUNIT_ASSERT_EQUAL(Index(2), root2.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root2)); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root2)); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(0), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root)); + } + + { // merge an inside tile and an outside tile into a grid with two child nodes + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + root.addChild(new RootChildType(Coord(0, 0, 0), 123.0f, false)); + root.addChild(new RootChildType(Coord(8192, 0, 0), 1.9f, false)); + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + root2.addTile(Coord(0, 0, 0), 15.0f, false); // should not replace child + root2.addTile(Coord(8192, 0, 0), -25.0f, false); // should replace child + + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(2), getTileCount(root2)); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(2), root.getTableSize()); + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(root)); + CPPUNIT_ASSERT_EQUAL(Index(1), getOutsideTileCount(root)); + CPPUNIT_ASSERT(root.cbeginChildAll().isChildNode()); + CPPUNIT_ASSERT(!(++root.cbeginChildAll()).isChildNode()); + CPPUNIT_ASSERT_EQUAL(123.0f, root.cbeginChildOn()->getFirstValue()); + // outside tile value replaced with negative background + CPPUNIT_ASSERT_EQUAL(grid->background(), root.cbeginValueAll().getValue()); + } + + { // merge two internal nodes into a grid with an inside tile and an outside tile + using RootChildType = FloatTree::RootNodeType::ChildNodeType; + using LeafParentType = RootChildType::ChildNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + auto& root = grid->tree().root(); + auto rootChild = std::make_unique(Coord(0, 0, 0), 123.0f, false); + rootChild->addTile(0, -14.0f, false); + rootChild->addTile(1, 15.0f, false); + rootChild->addTile(2, -13.0f, false); + root.addChild(rootChild.release()); + + FloatGrid::Ptr grid2 = createLevelSet(); + auto& root2 = grid2->tree().root(); + auto rootChild2 = std::make_unique(Coord(0, 0, 0), 55.0f, false); + rootChild2->addChild(new LeafParentType(Coord(0, 0, 0), 29.0f, false)); + rootChild2->addChild(new LeafParentType(Coord(0, 0, 128), 31.0f, false)); + rootChild2->addTile(2, -17.0f, true); + rootChild2->addTile(9, 19.0f, true); + root2.addChild(rootChild2.release()); + + CPPUNIT_ASSERT_EQUAL(Index(2), getInsideTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(0), getActiveTileCount(*root.cbeginChildOn())); + + CPPUNIT_ASSERT_EQUAL(Index(2), getChildCount(*root2.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(1), getInsideTileCount(*root2.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(2), getActiveTileCount(*root2.cbeginChildOn())); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(0), getInsideTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT_EQUAL(Index(1), getActiveTileCount(*root.cbeginChildOn())); + CPPUNIT_ASSERT(root.cbeginChildOn()->isChildMaskOn(0)); + CPPUNIT_ASSERT(!root.cbeginChildOn()->isChildMaskOn(1)); + CPPUNIT_ASSERT_EQUAL(-29.0f, root.cbeginChildOn()->cbeginChildOn()->getFirstValue()); + auto iter = root.cbeginChildOn()->cbeginValueAll(); + CPPUNIT_ASSERT_EQUAL(15.0f, iter.getValue()); + ++iter; + CPPUNIT_ASSERT_EQUAL(3.0f, iter.getValue()); + + CPPUNIT_ASSERT_EQUAL(Index(1), getChildCount(*root2.cbeginChildOn())); + } + + { // merge a leaf node into a grid with an inside tile + FloatGrid::Ptr grid = createLevelSet(); + grid->tree().addTile(1, Coord(0, 0, 0), -1.3f, true); + FloatGrid::Ptr grid2 = createLevelSet(); + grid2->tree().touchLeaf(Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(Index32(0), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(0), grid2->tree().leafCount()); + } + + { // merge two leaf nodes into a grid + FloatGrid::Ptr grid = createLevelSet(); + grid->tree().touchLeaf(Coord(0, 0, 0)); + FloatGrid::Ptr grid2 = createLevelSet(); + grid2->tree().touchLeaf(Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + const auto* leaf = grid->tree().probeConstLeaf(Coord(0, 0, 0)); + CPPUNIT_ASSERT(leaf); + } + + { // merge a leaf node into a grid with a partially constructed leaf node + using LeafT = FloatTree::LeafNodeType; + + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + + grid->tree().addLeaf(new LeafT(PartialCreate(), Coord(0, 0, 0))); + auto* leaf = grid2->tree().touchLeaf(Coord(0, 0, 0)); + leaf->setValueOnly(10, 6.4f); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + const auto* testLeaf = grid->tree().probeConstLeaf(Coord(0, 0, 0)); + CPPUNIT_ASSERT_EQUAL(3.0f, testLeaf->getValue(10)); + } + + { // merge two leaf nodes from different grids + FloatGrid::Ptr grid = createLevelSet(); + FloatGrid::Ptr grid2 = createLevelSet(); + + auto* leaf = grid->tree().touchLeaf(Coord(0, 0, 0)); + auto* leaf2 = grid2->tree().touchLeaf(Coord(0, 0, 0)); + + // active state from the voxel with the maximum value preserved + + leaf->setValueOnly(5, 98.0f); + leaf2->setValueOnly(5, 2.0f); + leaf2->setValueOn(5); + + leaf->setValueOnly(7, 2.0f); + leaf->setValueOn(7); + leaf2->setValueOnly(7, 100.0f); + + leaf->setValueOnly(9, 4.0f); + leaf->setValueOn(9); + leaf2->setValueOnly(9, -100.0f); + + tools::CsgDifferenceOp mergeOp(grid2->tree(), Steal()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + const auto* testLeaf = grid->tree().probeConstLeaf(Coord(0, 0, 0)); + CPPUNIT_ASSERT_EQUAL(98.0f, testLeaf->getValue(5)); + CPPUNIT_ASSERT(!testLeaf->isValueOn(5)); + CPPUNIT_ASSERT_EQUAL(2.0f, testLeaf->getValue(7)); + CPPUNIT_ASSERT(testLeaf->isValueOn(7)); + CPPUNIT_ASSERT_EQUAL(100.0f, testLeaf->getValue(9)); + CPPUNIT_ASSERT(!testLeaf->isValueOn(9)); + } + + { // merge a leaf node into a grid with an inside tile from a const tree + FloatGrid::Ptr grid = createLevelSet(); + grid->tree().addTile(1, Coord(0, 0, 0), -1.3f, true); + FloatGrid::Ptr grid2 = createLevelSet(); + grid2->tree().touchLeaf(Coord(0, 0, 0)); + + CPPUNIT_ASSERT_EQUAL(Index32(0), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + + tools::CsgDifferenceOp mergeOp(grid2->constTree(), DeepCopy()); + tree::DynamicNodeManager nodeManager(grid->tree()); + nodeManager.foreachTopDown(mergeOp); + + CPPUNIT_ASSERT_EQUAL(Index32(1), grid->tree().leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(1), grid2->tree().leafCount()); + } +} diff --git a/openvdb/openvdb/unittest/TestNodeManager.cc b/openvdb/openvdb/unittest/TestNodeManager.cc index 17b2f2c4be..71ca7920a3 100644 --- a/openvdb/openvdb/unittest/TestNodeManager.cc +++ b/openvdb/openvdb/unittest/TestNodeManager.cc @@ -17,9 +17,13 @@ class TestNodeManager: public CppUnit::TestFixture CPPUNIT_TEST_SUITE(TestNodeManager); CPPUNIT_TEST(testAll); + CPPUNIT_TEST(testConst); + CPPUNIT_TEST(testDynamic); CPPUNIT_TEST_SUITE_END(); void testAll(); + void testConst(); + void testDynamic(); }; @@ -151,3 +155,202 @@ TestNodeManager::testAll() } } + + +void +TestNodeManager::testConst() +{ + using namespace openvdb; + + const Vec3f center(0.35f, 0.35f, 0.35f); + const int dim = 128, half_width = 5; + const float voxel_size = 1.0f/dim; + + FloatGrid::Ptr grid = FloatGrid::create(/*background=*/half_width*voxel_size); + const FloatTree& tree = grid->constTree(); + + tree::NodeManager manager(tree); + + NodeCountOp topDownOp; + manager.reduceTopDown(topDownOp); + + std::vector nodeCount; + for (openvdb::Index i=0; i +struct ExpandOp +{ + using RootT = typename TreeT::RootNodeType; + using LeafT = typename TreeT::LeafNodeType; + + explicit ExpandOp(bool zeroOnly = false) : mZeroOnly(zeroOnly) { } + + // do nothing for the root node + bool operator()(RootT&, size_t = 1) const { return true; } + + // count the internal and leaf nodes + template + bool operator()(NodeT& node, size_t idx = 1) const + { + for (auto iter = node.cbeginValueAll(); iter; ++iter) { + const openvdb::Coord ijk = iter.getCoord(); + if (ijk.x() < 256 && ijk.y() < 256 && ijk.z() < 256) { + node.addChild(new typename NodeT::ChildNodeType(iter.getCoord(), NodeT::LEVEL, true)); + } + } + + if (mZeroOnly) return idx == 0; + return true; + } + + bool operator()(LeafT& leaf, size_t /*idx*/ = 1) const + { + for (auto iter = leaf.beginValueAll(); iter; ++iter) { + iter.setValue(iter.pos()); + } + + return true; + } + + bool mZeroOnly = false; +};// ExpandOp + +template +struct RootOnlyOp +{ + using RootT = typename TreeT::RootNodeType; + + RootOnlyOp() = default; + RootOnlyOp(const RootOnlyOp&, tbb::split) { } + void join(const RootOnlyOp&) { } + + // do nothing for the root node but return false + bool operator()(RootT&, size_t) const { return false; } + + // throw on internal or leaf nodes + template + bool operator()(NodeOrLeafT&, size_t) const + { + OPENVDB_THROW(openvdb::RuntimeError, "Should not process nodes below root."); + } +};// RootOnlyOp + +template +struct SumOp { + using RootT = typename TreeT::RootNodeType; + using LeafT = typename TreeT::LeafNodeType; + + explicit SumOp(bool zeroOnly = false) : mZeroOnly(zeroOnly) { } + SumOp(const SumOp& other, tbb::split): totalCount(0), mZeroOnly(other.mZeroOnly) { } + void join(const SumOp& other) + { + totalCount += other.totalCount; + } + // do nothing for the root node + bool operator()(const typename TreeT::RootNodeType&, size_t /*idx*/ = 0) { return true; } + // count the internal nodes + template + bool operator()(const NodeT& node, size_t idx = 0) + { + for (auto iter = node.cbeginValueAll(); iter; ++iter) { + totalCount += *iter; + } + if (mZeroOnly) return idx == 0; + return true; + } + // count the leaf nodes + bool operator()(const LeafT& leaf, size_t /*idx*/ = 0) + { + for (auto iter = leaf.cbeginValueAll(); iter; ++iter) { + totalCount += *iter; + } + return true; + } + openvdb::Index64 totalCount = openvdb::Index64(0); + bool mZeroOnly = false; +};// SumOp + +}//unnamed namespace + +void +TestNodeManager::testDynamic() +{ + using openvdb::Coord; + using openvdb::Index32; + using openvdb::Index64; + using openvdb::Int32Tree; + + using RootNodeType = Int32Tree::RootNodeType; + using Internal1NodeType = RootNodeType::ChildNodeType; + + Int32Tree sourceTree(0); + + auto child = + std::make_unique(Coord(0, 0, 0), /*value=*/1.0f); + + CPPUNIT_ASSERT(sourceTree.root().addChild(child.release())); + CPPUNIT_ASSERT_EQUAL(Index32(0), sourceTree.leafCount()); + CPPUNIT_ASSERT_EQUAL(Index32(2), sourceTree.nonLeafCount()); + + ExpandOp expandOp; + + { // use NodeManager::foreachTopDown + Int32Tree tree(sourceTree); + openvdb::tree::NodeManager manager(tree); + CPPUNIT_ASSERT_EQUAL(Index64(1), manager.nodeCount()); + manager.foreachTopDown(expandOp); + CPPUNIT_ASSERT_EQUAL(Index32(0), tree.leafCount()); + + // first level has been expanded, but node manager cache does not include the new nodes + SumOp sumOp; + manager.reduceBottomUp(sumOp); + CPPUNIT_ASSERT_EQUAL(Index64(32760), sumOp.totalCount); + } + + { // use DynamicNodeManager::foreachTopDown and filter out nodes below root + Int32Tree tree(sourceTree); + openvdb::tree::DynamicNodeManager manager(tree); + RootOnlyOp rootOnlyOp; + CPPUNIT_ASSERT_NO_THROW(manager.foreachTopDown(rootOnlyOp)); + CPPUNIT_ASSERT_NO_THROW(manager.reduceTopDown(rootOnlyOp)); + } + + { // use DynamicNodeManager::foreachTopDown + Int32Tree tree(sourceTree); + openvdb::tree::DynamicNodeManager manager(tree); + manager.foreachTopDown(expandOp); + CPPUNIT_ASSERT_EQUAL(Index32(32768), tree.leafCount()); + + SumOp sumOp; + manager.reduceTopDown(sumOp); + CPPUNIT_ASSERT_EQUAL(Index64(4286611448), sumOp.totalCount); + + SumOp zeroSumOp(true); + manager.reduceTopDown(zeroSumOp); + CPPUNIT_ASSERT_EQUAL(Index64(535855096), zeroSumOp.totalCount); + } + + { // use DynamicNodeManager::foreachTopDown but filter nodes with non-zero index + Int32Tree tree(sourceTree); + openvdb::tree::DynamicNodeManager manager(tree); + ExpandOp zeroExpandOp(true); + manager.foreachTopDown(zeroExpandOp); + CPPUNIT_ASSERT_EQUAL(Index32(32768), tree.leafCount()); + + SumOp sumOp; + manager.reduceTopDown(sumOp); + CPPUNIT_ASSERT_EQUAL(Index64(550535160), sumOp.totalCount); + } +} diff --git a/openvdb/openvdb/unittest/TestTree.cc b/openvdb/openvdb/unittest/TestTree.cc index 4cb3ae43ae..eeeaf42829 100644 --- a/openvdb/openvdb/unittest/TestTree.cc +++ b/openvdb/openvdb/unittest/TestTree.cc @@ -2608,6 +2608,8 @@ struct BBoxOp void TestTree::testProcessBBox() { + OPENVDB_NO_DEPRECATION_WARNING_BEGIN + using openvdb::Coord; using openvdb::CoordBBox; //check two leaf nodes and two tiles at each level 1, 2 and 3 @@ -2635,6 +2637,8 @@ TestTree::testProcessBBox() CPPUNIT_ASSERT(op.bbox[i] == bbox[i]); } } + + OPENVDB_NO_DEPRECATION_WARNING_END } void diff --git a/openvdb/openvdb/unittest/TestTreeVisitor.cc b/openvdb/openvdb/unittest/TestTreeVisitor.cc index b9a8af27fb..1343ef47ff 100644 --- a/openvdb/openvdb/unittest/TestTreeVisitor.cc +++ b/openvdb/openvdb/unittest/TestTreeVisitor.cc @@ -172,6 +172,8 @@ template void TestTreeVisitor::visitTree() { + OPENVDB_NO_DEPRECATION_WARNING_BEGIN + TreeT tree = createTestTree(); { // Traverse the tree, accumulating node counts. @@ -202,6 +204,8 @@ TestTreeVisitor::visitTree() CPPUNIT_ASSERT_EQUAL(0U, visitor.leafCount()); // leaf nodes were skipped CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.nonLeafCount()); } + + OPENVDB_NO_DEPRECATION_WARNING_END } @@ -280,6 +284,8 @@ class Visitor2 void TestTreeVisitor::testVisit2Trees() { + OPENVDB_NO_DEPRECATION_WARNING_BEGIN + using TreeT = openvdb::FloatTree; using Tree2T = openvdb::VectorTree; using ValueT = TreeT::ValueType; @@ -341,4 +347,6 @@ TestTreeVisitor::testVisit2Trees() CPPUNIT_ASSERT_EQUAL(0U, visitor.bLeafCount()); CPPUNIT_ASSERT_EQUAL(tree2.nonLeafCount(), visitor.aNonLeafCount()); CPPUNIT_ASSERT_EQUAL(tree.nonLeafCount(), visitor.bNonLeafCount()); + + OPENVDB_NO_DEPRECATION_WARNING_END } diff --git a/openvdb/openvdb/unittest/TestUtil.cc b/openvdb/openvdb/unittest/TestUtil.cc index 4e25d676a7..064de57cb3 100644 --- a/openvdb/openvdb/unittest/TestUtil.cc +++ b/openvdb/openvdb/unittest/TestUtil.cc @@ -32,35 +32,20 @@ class TestUtil: public CppUnit::TestCase CPPUNIT_TEST(testFormats); CPPUNIT_TEST(testCpuTimer); CPPUNIT_TEST(testPagedArray); - CPPUNIT_TEST(testPagedArrayPushBack); CPPUNIT_TEST_SUITE_END(); void testCpuTimer(); void testFormats(); void testPagedArray(); - void testPagedArrayPushBack(); using RangeT = tbb::blocked_range; - // Multi-threading ArrayT::push_back - template - struct ArrayPushBack { - ArrayPushBack(ArrayT& array) : mArray(&array) {} - void parallel(size_t size) {tbb::parallel_for(RangeT(size_t(0), size, mArray->pageSize()), *this);} - void serial(size_t size) { (*this)(RangeT(size_t(0), size)); } - void unsafe(size_t size) { for (size_t i=0; i!=size; ++i) mArray->push_back_unsafe(i); } - void operator()(const RangeT& r) const { - for (size_t i=r.begin(), n=r.end(); i!=n; ++i) mArray->push_back(i); - } - ArrayT* mArray; - }; - // Multi-threading ArrayT::ValueBuffer::push_back template struct BufferPushBack { BufferPushBack(ArrayT& array) : mBuffer(array) {} void parallel(size_t size) { - tbb::parallel_for(RangeT(size_t(0), size, mBuffer.parent().pageSize()), *this); + tbb::parallel_for(RangeT(size_t(0), size, 256*mBuffer.pageSize()), *this); } void serial(size_t size) { (*this)(RangeT(size_t(0), size)); } void operator()(const RangeT& r) const { @@ -77,7 +62,7 @@ class TestUtil: public CppUnit::TestCase void parallel(size_t size) { typename ArrayT::ValueBuffer exemplar(*mArray);//dummy used for initialization mPool = new PoolT(exemplar);//thread local storage pool of ValueBuffers - tbb::parallel_for(RangeT(size_t(0), size, mArray->pageSize()), *this); + tbb::parallel_for(RangeT(size_t(0), size, 256*mArray->pageSize()), *this); for (auto i=mPool->begin(); i!=mPool->end(); ++i) i->flush(); delete mPool; } @@ -170,57 +155,6 @@ TestUtil::testCpuTimer() } } -void -TestUtil::testPagedArrayPushBack() -{ -#ifdef BENCHMARK_PAGED_ARRAY - const size_t problemSize = 256000; - openvdb::util::CpuTimer timer; - std::cerr << "\nProblem size for benchmark: " << problemSize << std::endl; -#else - const size_t problemSize = 256000; -#endif - {//parallel PagedArray::push_back - using ArrayT = openvdb::util::PagedArray; - ArrayT d; -#ifdef BENCHMARK_PAGED_ARRAY - timer.start("3: Parallel PagedArray::push_back with default page size"); -#endif - {// for some reason this: - ArrayPushBack tmp(d); - tmp.parallel(problemSize); - }// is faster than: - //tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), - // [&d](const tbb::blocked_range &range){ - // for (size_t i=range.begin(); i!=range.end(); ++i) d.push_back(i);}); -#ifdef BENCHMARK_PAGED_ARRAY - timer.stop(); -#endif - CPPUNIT_ASSERT_EQUAL(size_t(10), d.log2PageSize()); - CPPUNIT_ASSERT_EQUAL(size_t(1)<> log2PageSize - CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); - CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); - -#ifdef BENCHMARK_PAGED_ARRAY - timer.start("parallel sort with a default page size"); -#endif - d.sort(); -#ifdef BENCHMARK_PAGED_ARRAY - timer.stop(); -#endif - for (size_t i=0, n=d.size(); i> log2PageSize - CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); - CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); - } -} - void TestUtil::testPagedArray() { @@ -233,158 +167,51 @@ TestUtil::testPagedArray() #endif {//serial PagedArray::push_back (check return value) - openvdb::util::PagedArray d; + openvdb::util::PagedArray d; + CPPUNIT_ASSERT(d.isEmpty()); CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); - CPPUNIT_ASSERT_EQUAL(size_t(8), d.log2PageSize()); + CPPUNIT_ASSERT_EQUAL(size_t(10), d.log2PageSize()); CPPUNIT_ASSERT_EQUAL(size_t(1)<; - ArrayT d; -#ifdef BENCHMARK_PAGED_ARRAY - timer.start("1: Serial PagedArray::push_back with default page size"); -#endif - {// for some reason this: - ArrayPushBack tmp(d); - tmp.serial(problemSize); - }// is faster than: - //for (size_t i=0; i> log2PageSize - CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); - CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); - for (size_t i=0, n=d.size(); i; - ArrayT d; #ifdef BENCHMARK_PAGED_ARRAY timer.start("2: Serial PagedArray::push_back_unsafe with default page size"); #endif - {// for some reason this: - ArrayPushBack tmp(d); - tmp.unsafe(problemSize); - }// is faster than: - //openvdb::util::PagedArray d; - //for (size_t i=0; i d; + for (size_t i=0; i; - ArrayT d; -#ifdef BENCHMARK_PAGED_ARRAY - timer.start("3: Parallel PagedArray::push_back with default page size"); -#endif - //{// for some reason this: - // ArrayPushBack tmp(d); - // tmp.parallel(problemSize); - //}// is faster than: - tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), - [&d](const tbb::blocked_range &range){ - for (size_t i=range.begin(); i!=range.end(); ++i) d.push_back(i);}); -#ifdef BENCHMARK_PAGED_ARRAY - timer.stop(); -#endif - CPPUNIT_ASSERT_EQUAL(size_t(10), d.log2PageSize()); - CPPUNIT_ASSERT_EQUAL(size_t(1)<> log2PageSize - CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); - CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); - -#ifdef BENCHMARK_PAGED_ARRAY - timer.start("parallel sort with a default page size"); -#endif - d.sort(); -#ifdef BENCHMARK_PAGED_ARRAY - timer.stop(); -#endif - for (size_t i=0, n=d.size(); i> log2PageSize - CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); - CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); - - - } - {//parallel PagedArray::push_back with page size of only 8 - using ArrayT = openvdb::util::PagedArray; - ArrayT d; -#ifdef BENCHMARK_PAGED_ARRAY - timer.start("4: Parallel PagedArray::push_back with page size of only 8"); -#endif - {// for some reason this: - ArrayPushBack tmp(d); - tmp.parallel(problemSize); - }// is faster than: - //tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), - // [&d](const tbb::blocked_range &range){ - // for (size_t i=range.begin(); i!=range.end(); ++i) d.push_back(i);}); -#ifdef BENCHMARK_PAGED_ARRAY - timer.stop(); -#endif - CPPUNIT_ASSERT_EQUAL(size_t(3), d.log2PageSize()); - CPPUNIT_ASSERT_EQUAL(size_t(1)<> log2PageSize - CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); - CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); - -#ifdef BENCHMARK_PAGED_ARRAY - timer.start("parallel sort with a page size of only 8"); -#endif - d.sort(); -#ifdef BENCHMARK_PAGED_ARRAY - timer.stop(); -#endif - for (size_t i=0, n=d.size(); i> log2PageSize - CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); - CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); - } #ifdef BENCHMARK_PAGED_ARRAY {//benchmark against a std::vector timer.start("5: Serial std::vector::push_back"); @@ -429,7 +256,7 @@ TestUtil::testPagedArray() #endif {//serial PagedArray::ValueBuffer::push_back - using ArrayT = openvdb::util::PagedArray; + using ArrayT = openvdb::util::PagedArray; ArrayT d; CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); @@ -445,13 +272,8 @@ TestUtil::testPagedArray() #ifdef BENCHMARK_PAGED_ARRAY timer.start("9: Serial PagedArray::ValueBuffer::push_back"); #endif - {// for some reason this: - BufferPushBack tmp(d); - tmp.serial(problemSize); - // is faster than: - //ArrayT::ValueBuffer buffer(d); - //for (size_t i=0, n=problemSize; i tmp(d); + tmp.serial(problemSize); #ifdef BENCHMARK_PAGED_ARRAY timer.stop(); @@ -486,14 +308,8 @@ TestUtil::testPagedArray() #ifdef BENCHMARK_PAGED_ARRAY timer.start("10: Parallel PagedArray::ValueBuffer::push_back"); #endif - {// for some reason this: - BufferPushBack tmp(d); - tmp.parallel(problemSize); - }// is faster than: - //tbb::parallel_for(tbb::blocked_range(0, problemSize, d.pageSize()), - // [&d](const tbb::blocked_range &r){ - // typename ArrayT::ValueBuffer buffer(d); - // for (size_t i=r.begin(), n=r.end(); i!=n; ++i) buffer.push_back(i);}); + BufferPushBack tmp(d); + tmp.parallel(problemSize); #ifdef BENCHMARK_PAGED_ARRAY timer.stop(); #endif @@ -522,7 +338,7 @@ TestUtil::testPagedArray() #endif for (size_t i=0, n=d.size()-1; i<=n; ++i) CPPUNIT_ASSERT_EQUAL(n-i, d[i]); - CPPUNIT_ASSERT_EQUAL(problemSize, d.push_back(1)); + CPPUNIT_ASSERT_EQUAL(problemSize, d.push_back_unsafe(1)); CPPUNIT_ASSERT_EQUAL(problemSize+1, d.size()); CPPUNIT_ASSERT_EQUAL(size_t(1)<> log2PageSize @@ -539,7 +355,8 @@ TestUtil::testPagedArray() ArrayT d; CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); { - ArrayT::ValueBuffer vc(d); + //ArrayT::ValueBuffer vc(d); + auto vc = d.getBuffer(); vc.push_back(1); vc.push_back(2); CPPUNIT_ASSERT_EQUAL(size_t(0), d.size()); @@ -607,7 +424,7 @@ TestUtil::testPagedArray() CPPUNIT_ASSERT_EQUAL((d.size()-1)>>d.log2PageSize(), d.pageCount()-1); CPPUNIT_ASSERT_EQUAL(d.pageCount()*d.pageSize(), d.capacity()); CPPUNIT_ASSERT(!d.isPartiallyFull()); - d.push_back(problemSize); + d.push_back_unsafe(problemSize); CPPUNIT_ASSERT(d.isPartiallyFull()); tbb::parallel_for(tbb::blocked_range(problemSize+1, 2*problemSize+1, d2.pageSize()), @@ -649,9 +466,9 @@ TestUtil::testPagedArray() for (size_t i=0, n=d.size(); i array; - for (int i=0; i<100000; ++i) array.push_back(i); + for (int i=0; i<100000; ++i) array.push_back_unsafe(i); for (int i=0; i<100000; ++i) CPPUNIT_ASSERT_EQUAL(i, array[i]); } {//2A diff --git a/openvdb/openvdb/util/PagedArray.h b/openvdb/openvdb/util/PagedArray.h index 9a8cf82a4f..30ee844993 100644 --- a/openvdb/openvdb/util/PagedArray.h +++ b/openvdb/openvdb/util/PagedArray.h @@ -54,31 +54,17 @@ namespace util { /// There are three fundamentally different ways to insert elements to /// this container - each with different advanteges and disadvanteges. /// -/// The simplest way to insert elements is to use PagedArray::push_back e.g. +/// The simplest way to insert elements is to use PagedArray::push_back_unsafe +/// which is @a not thread-safe: /// @code /// PagedArray array; -/// for (size_t i=0; i<100000; ++i) array.push_back(i); +/// for (size_t i=0; i<100000; ++i) array.push_back_unsafe(i); /// @endcode -/// or with TBB task-based multi-threading -/// @code -/// PagedArray array; -/// tbb::parallel_for( -/// tbb::blocked_range(0, 10000, array.pageSize()), -/// [&array](const tbb::blocked_range& range) { -/// for (size_t i=range.begin(); i!=range.end(); ++i) array.push_back(i); -/// } -/// ); -/// @endcode -/// PagedArray::push_back has the advantage that it's thread-safe and -/// preserves the ordering of the inserted elements. In fact it returns -/// the linear offset to the added element which can then be used for -/// fast O(1) random access. The disadvantage is it's the slowest of -/// the three different ways of inserting elements. /// -/// The fastest way (by far) to insert elements by means of a PagedArray::ValueBuffer, e.g. +/// The fastest way (by far) to insert elements is by means of a PagedArray::ValueBuffer: /// @code /// PagedArray array; -/// PagedArray::ValueBuffer buffer(array); +/// auto buffer = array.getBuffer(); /// for (size_t i=0; i<100000; ++i) buffer.push_back(i); /// buffer.flush(); /// @endcode @@ -87,17 +73,17 @@ namespace util { /// PagedArray array; /// { /// //local scope of a single thread -/// PagedArray::ValueBuffer buffer(array); +/// auto buffer = array.getBuffer(); /// for (size_t i=0; i<100000; ++i) buffer.push_back(i); /// } /// @endcode -/// or with TBB task-based multi-threading +/// or with TBB task-based multi-threading: /// @code /// PagedArray array; /// tbb::parallel_for( /// tbb::blocked_range(0, 10000, array.pageSize()), /// [&array](const tbb::blocked_range& range) { -/// PagedArray::ValueBuffer buffer(array); +/// auto buffer = array.getBuffer(); /// for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i); /// } /// ); @@ -106,19 +92,19 @@ namespace util { /// to fewer concurrent instantiations of partially full ValueBuffers) /// @code /// PagedArray array; -/// PagedArray::ValueBuffer exemplar(array);//dummy used for initialization +/// auto exemplar = array.getBuffer();//dummy used for initialization /// tbb::enumerable_thread_specific::ValueBuffer> /// pool(exemplar);//thread local storage pool of ValueBuffers /// tbb::parallel_for( /// tbb::blocked_range(0, 10000, array.pageSize()), /// [&pool](const tbb::blocked_range& range) { -/// PagedArray::ValueBuffer &buffer = pool.local(); +/// auto &buffer = pool.local(); /// for (size_t i=range.begin(); i!=range.end(); ++i) buffer.push_back(i); /// } /// ); /// for (auto i=pool.begin(); i!=pool.end(); ++i) i->flush(); /// @endcode -/// This technique generally outperforms PagedArray::push_back, +/// This technique generally outperforms PagedArray::push_back_unsafe, /// std::vector::push_back, std::deque::push_back and even /// tbb::concurrent_vector::push_back. Additionally it /// is thread-safe as long as each thread has it's own instance of a @@ -156,6 +142,7 @@ template class PagedArray { private: + static_assert(Log2PageSize > 1UL, "Expected Log2PageSize > 1"); class Page; // must allow mutiple threads to call operator[] as long as only one thread calls push_back @@ -188,34 +175,26 @@ class PagedArray /// make sure to create an instance per thread! class ValueBuffer; + /// @return a new instance of a ValueBuffer which supports thread-safe push_back! + ValueBuffer getBuffer() { return ValueBuffer(*this); } + /// Const std-compliant iterator class ConstIterator; /// Non-const std-compliant iterator class Iterator; - /// @brief Thread safe insertion, adds a new element at - /// the end and increases the container size by one and - /// returns the linear offset for the inserted element. - /// - /// @param value value to be added to this PagedArray - /// - /// @details Constant time complexity. May allocate a new page. - size_t push_back(const ValueType& value) + /// @brief This method is deprecated and will be removed shortly! + OPENVDB_DEPRECATED size_t push_back(const ValueType& value) { - const size_t index = mSize.fetch_and_increment(); - if (index >= mCapacity) this->grow(index); - (*mPageTable[index >> Log2PageSize])[index] = value; - return index; + return this->push_back_unsafe(value); } - /// @brief Slightly faster than the thread-safe push_back above. - /// /// @param value value to be added to this PagedArray /// /// @note For best performance consider using the ValueBuffer! /// - /// @warning Not thread-safe! + /// @warning Not thread-safe and mostly intended for debugging! size_t push_back_unsafe(const ValueType& value) { const size_t index = mSize.fetch_and_increment(); @@ -599,6 +578,7 @@ ValueBuffer PagedArrayType& parent() const { return *mParent; } /// @brief Return the current number of elements cached in this buffer. size_t size() const { return mSize; } + static size_t pageSize() { return 1UL << Log2PageSize; } private: PagedArray* mParent; Page* mPage; diff --git a/openvdb/openvdb/version.h b/openvdb/openvdb/version.h index 33d43090bd..480ff5f2b8 100644 --- a/openvdb/openvdb/version.h +++ b/openvdb/openvdb/version.h @@ -48,8 +48,8 @@ // Library major, minor and patch version numbers #define OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER 7 -#define OPENVDB_LIBRARY_MINOR_VERSION_NUMBER 1 -#define OPENVDB_LIBRARY_PATCH_VERSION_NUMBER 1 +#define OPENVDB_LIBRARY_MINOR_VERSION_NUMBER 2 +#define OPENVDB_LIBRARY_PATCH_VERSION_NUMBER 0 // If OPENVDB_ABI_VERSION_NUMBER is already defined (e.g., via -DOPENVDB_ABI_VERSION_NUMBER=N) // use that ABI version. Otherwise, use this library version's default ABI. diff --git a/openvdb_ax/CMakeLists.txt b/openvdb_ax/CMakeLists.txt index cc17906fec..d59ff837c6 100644 --- a/openvdb_ax/CMakeLists.txt +++ b/openvdb_ax/CMakeLists.txt @@ -98,20 +98,6 @@ message(STATUS "Found LLVM: ${LLVM_DIR} (found suitable version \"${LLVM_PACKAGE ######################################################################### -# Configure other dependencies - -if(OPENVDB_AX_SHARED AND NOT Boost_USE_STATIC_LIBS) - # @note Both of these must be set for Boost 1.70 (VFX2020) to link against - # boost shared libraries (more specifically libraries built with -fPIC). - # http://boost.2283326.n4.nabble.com/CMake-config-scripts-broken-in-1-70-td4708957.html - # https://github.com/boostorg/boost_install/commit/160c7cb2b2c720e74463865ef0454d4c4cd9ae7c - set(BUILD_SHARED_LIBS ON) - set(Boost_USE_STATIC_LIBS OFF) -endif() -find_package(Boost REQUIRED COMPONENTS random) - -######################################################################### - set(OPENVDB_AX_LIBRARY_SOURCE_FILES openvdb_ax/ast/Parse.cc openvdb_ax/ast/PrintTree.cc @@ -123,12 +109,13 @@ set(OPENVDB_AX_LIBRARY_SOURCE_FILES openvdb_ax/codegen/PointComputeGenerator.cc openvdb_ax/codegen/PointFunctions.cc openvdb_ax/codegen/StandardFunctions.cc + openvdb_ax/codegen/Types.cc openvdb_ax/codegen/VolumeComputeGenerator.cc openvdb_ax/codegen/VolumeFunctions.cc openvdb_ax/compiler/Compiler.cc + openvdb_ax/compiler/Logger.cc openvdb_ax/compiler/PointExecutable.cc openvdb_ax/compiler/VolumeExecutable.cc - openvdb_ax/compiler/Logger.cc openvdb_ax/math/OpenSimplexNoise.cc ) @@ -145,7 +132,6 @@ endif() set(OPENVDB_AX_AST_INCLUDE_FILES openvdb_ax/ast/AST.h openvdb_ax/ast/Parse.h - openvdb_ax/ast/Literals.h openvdb_ax/ast/PrintTree.h openvdb_ax/ast/Scanners.h openvdb_ax/ast/Tokens.h @@ -159,6 +145,7 @@ set(OPENVDB_AX_CODEGEN_INCLUDE_FILES openvdb_ax/codegen/Functions.h openvdb_ax/codegen/FunctionTypes.h openvdb_ax/codegen/PointComputeGenerator.h + openvdb_ax/codegen/PointLeafLocalData.h openvdb_ax/codegen/SymbolTable.h openvdb_ax/codegen/Types.h openvdb_ax/codegen/Utils.h @@ -167,14 +154,13 @@ set(OPENVDB_AX_CODEGEN_INCLUDE_FILES ) set(OPENVDB_AX_COMPILER_INCLUDE_FILES + openvdb_ax/compiler/AttributeRegistry.h openvdb_ax/compiler/Compiler.h openvdb_ax/compiler/CompilerOptions.h openvdb_ax/compiler/CustomData.h - openvdb_ax/compiler/LeafLocalData.h + openvdb_ax/compiler/Logger.h openvdb_ax/compiler/PointExecutable.h - openvdb_ax/compiler/AttributeRegistry.h openvdb_ax/compiler/VolumeExecutable.h - openvdb_ax/compiler/Logger.h ) # @todo CMake >= 3.12, use an object library to consolidate shared/static @@ -208,7 +194,6 @@ endif() set(OPENVDB_AX_CORE_DEPENDENT_LIBS ${OPENVDB_LIB} ${LLVM_LIBS} - Boost::random ) ########################################################################## diff --git a/openvdb_ax/openvdb_ax/ast/AST.h b/openvdb_ax/openvdb_ax/ast/AST.h index 0fc3b51f7b..de36c630ac 100644 --- a/openvdb_ax/openvdb_ax/ast/AST.h +++ b/openvdb_ax/openvdb_ax/ast/AST.h @@ -27,7 +27,6 @@ #ifndef OPENVDB_AX_AST_HAS_BEEN_INCLUDED #define OPENVDB_AX_AST_HAS_BEEN_INCLUDED -#include "Literals.h" #include "Tokens.h" #include @@ -2255,11 +2254,11 @@ struct Value : public ValueBase using UniquePtr = std::unique_ptr>; using Type = T; - using LiteralLimitsT = LiteralLimits; /// @brief Integers and Floats store their value as ContainerType, which is /// guaranteed to be at least large enough to represent the maximum /// possible supported type for the requested precision. - using ContainerType = typename LiteralLimitsT::ContainerT; + using ContainerType = typename std::conditional< + std::is_integral::value, uint64_t, T>::type; /// @brief The list of supported numerical constants. /// @note Strings are specialized and handled separately @@ -2272,36 +2271,17 @@ struct Value : public ValueBase std::is_same::value; static_assert(IsSupported, "Incompatible ast::Value node instantiated."); - /// @brief Construct a numerical Value from a given string. The string - /// should be a positive integer or floating point number. - /// Exponent-signs are supported for floating point numbers. - /// @note If the maximum container type cannot be used to store the value - /// or if the string is invalid, the resulting value is the maximum - /// possible container type for scalars or infinity for floats and - /// mText is initialized to the original string - /// @note This constructor should not be used to construct booleans - Value(const std::string number) - : mValue(LiteralLimitsT::onLimitOverflow()) - , mText(nullptr) { - try { - mValue = LiteralLimitsT::convert(number); - } - catch (std::out_of_range&) { - mText.reset(new std::string(number)); - } - } /// @brief Directly construct a Value from a source integer, float or /// boolean, guaranteeing valid construction. Note that the provided /// argument should not be negative Value(const ContainerType value) - : mValue(value), mText(nullptr) {} + : mValue(value) {} /// @brief Deep copy constructor for a Value /// @note No parent information needs updating as a Value is a "leaf /// level" node (contains no children) /// @param other A const reference to another Value to deep copy Value(const Value& other) - : mValue(other.mValue) - , mText(other.mText ? new std::string(*other.mText) : nullptr) {} + : mValue(other.mValue) {} ~Value() override = default; /// @copybrief Node::copy() @@ -2318,47 +2298,35 @@ struct Value : public ValueBase /// @copybrief Node::nodename() const char* nodename() const override { if (std::is_same::value) return "boolean literal"; - if (std::is_same::value) return "short (16bit) literal"; - if (std::is_same::value) return "integer (32bit) literal"; - if (std::is_same::value) return "long (64bit) literal"; + if (std::is_same::value) return "int16 literal"; + if (std::is_same::value) return "int32 literal"; + if (std::is_same::value) return "int64 literal"; if (std::is_same::value) return "float (32bit) literal"; if (std::is_same::value) return "double (64bit) literal"; } /// @copybrief Node::subname() const char* subname() const override { if (std::is_same::value) return "bool"; - if (std::is_same::value) return "shrt"; - if (std::is_same::value) return "int"; - if (std::is_same::value) return "long"; + if (std::is_same::value) return "i16"; + if (std::is_same::value) return "i32"; + if (std::is_same::value) return "i64"; if (std::is_same::value) return "flt"; - if (std::is_same::value) return "dble"; + if (std::is_same::value) return "dbl"; } /// @copybrief Node::basetype() const ValueBase* basetype() const override { return this; } - /// @brief Query whether or not this Value ended up with a ContainerType - /// overflow by checking to see if mText was initialized. This - /// overflow is specific to the highest precision ContainerType, not - /// necessarily the value type T. - /// @return True if mText was set - inline bool overflow() const { return static_cast(this->text()); } - /// @brief Access any text stored on the Value. This set if construction - /// of the value failed from the provided string argument - /// @return A const pointer to the text. Can be a nullptr - inline const std::string* text() const { return mText.get(); } /// @brief Access the value as its stored type /// @return The value as its stored ContainerType inline ContainerType asContainerType() const { return mValue; } /// @brief Access the value as its requested (templated) type /// @return The value as its templed type T inline T value() const { return static_cast(mValue); } + private: // A container of a max size defined by LiteralValueContainer to hold values // which may be out of scope. This is only used for warnings - ContainerType mValue; - // The original string representation of the variable used for warnings if - // it's unable to be represented (usually due to overflow) - std::unique_ptr mText; + const ContainerType mValue; }; /// @brief Specialization of Values for strings diff --git a/openvdb_ax/openvdb_ax/ast/Literals.h b/openvdb_ax/openvdb_ax/ast/Literals.h deleted file mode 100644 index 615f894752..0000000000 --- a/openvdb_ax/openvdb_ax/ast/Literals.h +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright Contributors to the OpenVDB Project -// SPDX-License-Identifier: MPL-2.0 - -/// @file ast/Literals.h -/// -/// @authors Nick Avramoussis -/// -/// @brief Basic string to numerical conversion for the parser. -/// - -#ifndef OPENVDB_AX_AST_LITERALS_HAS_BEEN_INCLUDED -#define OPENVDB_AX_AST_LITERALS_HAS_BEEN_INCLUDED - -#include - -#include -#include -#include -#include -#include - -namespace openvdb { -OPENVDB_USE_VERSION_NAMESPACE -namespace OPENVDB_VERSION_NAME { - -namespace ax { - -/// @brief The backend representation of strings in AX. This is also how -/// strings are passed from the AX code generation to functions. -struct AXString -{ - // usually size_t. Used to match the implementation of std:string - using SizeType = std::allocator::size_type; - const char* ptr = nullptr; - SizeType size = 0; -}; - -/// @brief Literal language limits and overflow behaviour - -/// @brief Struct which manages numeric limits and overflow behaviour for a target numeric type T -/// @note Used by the AST value node for handling numeric overflows -template -struct LiteralLimits -{ - using Type = T; - using ContainerT = T; -}; - -template -struct LiteralLimits::value>::type> -{ - using Type = T; - using ContainerT = uint64_t; - /// @brief The maximum value which can be converted from a string before - /// being unable to be represented as an integral - inline static ContainerT limit() { return std::numeric_limits::max(); } - /// @brief The value which is used if the string representation cannot be cast to - /// a valid integral. Note that this is NOT the signed integer wrap behaviour. - inline static ContainerT onLimitOverflow() { return limit(); } - /// @brief The language conversion method for converting a string to an integer - /// @param number String holding a number to be converted - /// @note See ast::Value for exception handling - inline static ContainerT convert(const std::string& number) { - assert(number.empty() || number[0] != '-'); - return std::stoull(number); - } -}; - -template -struct LiteralLimits::value>::type> -{ - using Type = T; - using ContainerT = double; - - /// @brief The maximum value which can be converted from a string before - /// being unable to be represented as an floating point value - inline static ContainerT limit() { return std::numeric_limits::max(); } - /// @brief The value which is used if the string representation cannot be held in - /// a value defined by the above limit - inline static ContainerT onLimitOverflow() { return std::numeric_limits::infinity(); } - /// @brief The language conversion method for converting a string to an integer - /// @param number String holding a number to be converted - /// @note See ast::Value for exception handling - inline static ContainerT convert(const std::string& number) { - assert(number.empty() || number[0] != '-'); - return std::stod(number); - } -}; - -} // namespace ax -} // namespace OPENVDB_VERSION_NAME -} // namespace openvdb - -#endif // OPENVDB_AX_AST_LITERALS_HAS_BEEN_INCLUDED - diff --git a/openvdb_ax/openvdb_ax/ast/Tokens.h b/openvdb_ax/openvdb_ax/ast/Tokens.h index aaa85eb008..7df86629bc 100644 --- a/openvdb_ax/openvdb_ax/ast/Tokens.h +++ b/openvdb_ax/openvdb_ax/ast/Tokens.h @@ -32,9 +32,9 @@ enum CoreType { BOOL = 0, CHAR, - SHORT, - INT, - LONG, + INT16, + INT32, + INT64, FLOAT, DOUBLE, // @@ -86,11 +86,14 @@ inline CoreType tokenFromTypeString(const std::string& type) if (type == "quatf") return QUATF; if (type == "quatd") return QUATD; } + else if (type[0] == 'i') { + if (type == "int16") return INT16; + if (type == "int") return INT32; + if (type == "int32") return INT32; + if (type == "int64") return INT64; + } else if (type == "bool") return BOOL; else if (type == "char") return CHAR; - else if (type == "short") return SHORT; - else if (type == "int") return INT; - else if (type == "long") return LONG; else if (type == "float") return FLOAT; else if (type == "double") return DOUBLE; else if (type == "string") return STRING; @@ -108,11 +111,6 @@ inline CoreType tokenFromTypeString(const std::string& type) if (type == "mat4s") return MAT4F; } else if (type == "quats") return QUATF; - else if (type[0] == 'i') { - if (type == "int16") return SHORT; - if (type == "int32") return INT; - if (type == "int64") return LONG; - } return UNKNOWN; } @@ -122,9 +120,9 @@ inline std::string typeStringFromToken(const CoreType type) switch (type) { case BOOL : return "bool"; case CHAR : return "char"; - case SHORT : return "short"; - case INT : return "int"; - case LONG : return "long"; + case INT16 : return "int16"; + case INT32 : return "int32"; + case INT64 : return "int64"; case FLOAT : return "float"; case DOUBLE : return "double"; case VEC2I : return "vec2i"; @@ -149,29 +147,6 @@ inline std::string typeStringFromToken(const CoreType type) } } -template CoreType tokenFromType() { return UNKNOWN; } -template<> inline CoreType tokenFromType() { return BOOL; } -template<> inline CoreType tokenFromType() { return CHAR; } -template<> inline CoreType tokenFromType() { return SHORT; } -template<> inline CoreType tokenFromType() { return INT; } -template<> inline CoreType tokenFromType() { return LONG; } -template<> inline CoreType tokenFromType() { return FLOAT; } -template<> inline CoreType tokenFromType() { return DOUBLE; } -template<> inline CoreType tokenFromType>() { return VEC2I; } -template<> inline CoreType tokenFromType>() { return VEC2F; } -template<> inline CoreType tokenFromType>() { return VEC2D; } -template<> inline CoreType tokenFromType>() { return VEC3I; } -template<> inline CoreType tokenFromType>() { return VEC3F; } -template<> inline CoreType tokenFromType>() { return VEC3D; } -template<> inline CoreType tokenFromType>() { return VEC4I; } -template<> inline CoreType tokenFromType>() { return VEC4F; } -template<> inline CoreType tokenFromType>() { return VEC4D; } -template<> inline CoreType tokenFromType>() { return MAT3F; } -template<> inline CoreType tokenFromType>() { return MAT3D; } -template<> inline CoreType tokenFromType>() { return MAT4F; } -template<> inline CoreType tokenFromType>() { return MAT4D; } -template<> inline CoreType tokenFromType() { return STRING; } - enum OperatorToken { //////////////////////////////////////////////////////////////// diff --git a/openvdb_ax/openvdb_ax/ast/Visitor.h b/openvdb_ax/openvdb_ax/ast/Visitor.h index 4d30c3b30e..182a24aa1c 100644 --- a/openvdb_ax/openvdb_ax/ast/Visitor.h +++ b/openvdb_ax/openvdb_ax/ast/Visitor.h @@ -15,7 +15,6 @@ #define OPENVDB_AX_AST_VISITOR_HAS_BEEN_INCLUDED #include "AST.h" -#include "Literals.h" #include "Tokens.h" #include @@ -123,9 +122,9 @@ struct Visitor inline bool postOrderNodes() const { return true; } /// @brief Default behavior option. Reverses the traversal order of child - /// nodes. If true, child nodes are accessed from first to last - /// index .i.e. 0 -> Node::children(). If false, child nodes are - /// accessed from last to first .i.e. Node::children() -> 0 + /// nodes. If true, child nodes are accessed from last to first + /// index .i.e. Node::children() -> 0. If false, child nodes are + /// accessed from first to last .i.e. 0 -> Node::children() inline bool reverseChildVisits() const { return false; } /// @brief Default behavior option. Controls whether nodes visit themselves diff --git a/openvdb_ax/openvdb_ax/ax.cc b/openvdb_ax/openvdb_ax/ax.cc index e678fb9152..6e6b51cc85 100644 --- a/openvdb_ax/openvdb_ax/ax.cc +++ b/openvdb_ax/openvdb_ax/ax.cc @@ -63,12 +63,13 @@ void run(const char* ax, openvdb::GridPtrVec& grids) { if (grids.empty()) return; // Check the type of all grids. If they are all points, run for point data. - // Otherwise, run for numerical volumes. - bool points = true; + // Otherwise, run for numerical volumes. Throw if the container has both. + const bool points = grids.front()->isType(); for (auto& grid : grids) { - if (!grid->isType()) { - points = false; - break; + if (points ^ grid->isType()) { + OPENVDB_THROW(AXCompilerError, + "Unable to process both OpenVDB Points and OpenVDB Volumes in " + "a single invocation of ax::run()"); } } // Construct a logger that will output errors to cerr and suppress warnings @@ -201,3 +202,4 @@ void uninitialize() } // namespace ax } // namespace OPENVDB_VERSION_NAME } // namespace openvdb + diff --git a/openvdb_ax/openvdb_ax/ax.h b/openvdb_ax/openvdb_ax/ax.h index aeee4d4649..a8bfc41301 100644 --- a/openvdb_ax/openvdb_ax/ax.h +++ b/openvdb_ax/openvdb_ax/ax.h @@ -92,3 +92,4 @@ void run(const char* ax, openvdb::GridPtrVec& grids); } // namespace openvdb #endif // OPENVDB_AX_AX_HAS_BEEN_INCLUDED + diff --git a/openvdb_ax/openvdb_ax/cmd/openvdb_ax.cc b/openvdb_ax/openvdb_ax/cmd/openvdb_ax.cc index 83d37fe4d6..cfb7f4caf0 100644 --- a/openvdb_ax/openvdb_ax/cmd/openvdb_ax.cc +++ b/openvdb_ax/openvdb_ax/cmd/openvdb_ax.cc @@ -211,7 +211,7 @@ void printFunctions(const bool namesOnly, // only include non internal functions and apply any search // criteria - std::map functionMap; + std::map functionMap; for (const auto& iter : registry->map()) { if (iter.second.isInternal()) continue; if (!search.empty() && iter.first.find(search) == std::string::npos) { @@ -247,7 +247,7 @@ void printFunctions(const bool namesOnly, llvm::LLVMContext C; for (const auto& iter : functionMap) { - const openvdb::ax::codegen::FunctionGroup::Ptr function = iter.second; + const openvdb::ax::codegen::FunctionGroup* const function = iter.second; const char* cdocs = function->doc(); if (!cdocs || cdocs[0] == '\0') { cdocs = ""; diff --git a/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc b/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc index 5049b75916..b190ac0934 100644 --- a/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc +++ b/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.cc @@ -94,6 +94,7 @@ std::string ComputeKernel::getDefaultName() { return "ax.compute"; } //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// +namespace codegen_internal { ComputeGenerator::ComputeGenerator(llvm::Module& module, const FunctionOptions& options, @@ -177,12 +178,20 @@ bool ComputeGenerator::visit(const ast::ConditionalStatement* cond) // generate conditional if (!this->traverse(cond->condition())) return false; - llvm::Value* condition = mValues.top(); mValues.pop(); + llvm::Value* condition = mValues.top(); + if (condition->getType()->isPointerTy()) { condition = mBuilder.CreateLoad(condition); } - condition = boolComparison(condition, mBuilder); + llvm::Type* conditionType = condition->getType(); + // check the type of the condition branch is bool-convertable + if (conditionType->isFloatingPointTy() || conditionType->isIntegerTy()) { + condition = boolComparison(condition, mBuilder); + } else { + if (!mLog.error("cannot convert non-scalar type to bool in condition", cond)) return false; + } + mValues.pop(); const bool hasElse = cond->hasFalse(); llvm::BasicBlock* thenBlock = llvm::BasicBlock::Create(mContext, "then", mFunction); llvm::BasicBlock* elseBlock = hasElse ? llvm::BasicBlock::Create(mContext, "else", mFunction) : postIfBlock; @@ -218,8 +227,16 @@ bool ComputeGenerator::visit(const ast::TernaryOperator* tern) llvm::Type* trueType = trueValue->getType(); bool truePtr = trueType->isPointerTy(); - llvm::Value* boolCondition = truePtr ? + llvm::Type* conditionType = truePtr ? trueType->getPointerElementType() : trueType; + llvm::Value* boolCondition = nullptr; + // check the type of the condition branch is bool-convertable + if (conditionType->isFloatingPointTy() || conditionType->isIntegerTy()) { + boolCondition = truePtr ? boolComparison(mBuilder.CreateLoad(trueValue), mBuilder) : boolComparison(trueValue, mBuilder); + } + else { + if (!mLog.error("cannot convert non-scalar type to bool in condition", tern)) return false; + } llvm::BasicBlock* trueBlock = llvm::BasicBlock::Create(mContext, "ternary_true", mFunction); llvm::BasicBlock* falseBlock = llvm::BasicBlock::Create(mContext, "ternary_false", mFunction); @@ -246,7 +263,7 @@ bool ComputeGenerator::visit(const ast::TernaryOperator* tern) if (!this->traverse(tern->falseBranch())) return false; llvm::BranchInst* falseBranch = mBuilder.CreateBr(returnBlock); - llvm::Value* falseValue = mValues.top(); mValues.pop(); + llvm::Value* falseValue = mValues.top(); llvm::Type* falseType = falseValue->getType(); // if both variables of same type do no casting or loading @@ -352,20 +369,25 @@ bool ComputeGenerator::visit(const ast::TernaryOperator* tern) // void type ternary acts like if-else statement // push void value to stop use of return from this expression mBuilder.SetInsertPoint(returnBlock); + mValues.pop(); mValues.push(falseValue); return true; } // reset to continue block mBuilder.SetInsertPoint(returnBlock); - llvm::PHINode* ternary = mBuilder.CreatePHI(trueValue->getType(), 2, "ternary"); + // if any errors have occurred these may not be the same type + if (falseValue->getType() == trueValue->getType()) { + llvm::PHINode* ternary = mBuilder.CreatePHI(trueValue->getType(), 2, "ternary"); - // if nesting branches the blocks for true and false branches may have been updated - // so get these again rather than reusing trueBlock/falseBlock - ternary->addIncoming(trueValue, trueBranch->getParent()); - ternary->addIncoming(falseValue, falseBranch->getParent()); + // if nesting branches the blocks for true and false branches may have been updated + // so get these again rather than reusing trueBlock/falseBlock + ternary->addIncoming(trueValue, trueBranch->getParent()); + ternary->addIncoming(falseValue, falseBranch->getParent()); - mValues.push(ternary); + mValues.pop(); + mValues.push(ternary); + } return true; } @@ -428,7 +450,14 @@ bool ComputeGenerator::visit(const ast::Loop* loop) if (condition->getType()->isPointerTy()) { condition = mBuilder.CreateLoad(condition); } - condition = boolComparison(condition, mBuilder); + llvm::Type* conditionType = condition->getType(); + // check the type of the condition branch is bool-convertable + if (conditionType->isFloatingPointTy() || conditionType->isIntegerTy()) { + condition = boolComparison(condition, mBuilder); + } + else { + if (!mLog.error("cannot convert non-scalar type to bool in condition", loop)) return false; + } mBuilder.CreateCondBr(condition, bodyBlock, postLoopBlock); // reset to post loop block @@ -495,12 +524,75 @@ bool ComputeGenerator::visit(const ast::Keyword* node) bool ComputeGenerator::visit(const ast::BinaryOperator* node) { - llvm::Value* rhs = mValues.top(); mValues.pop(); - llvm::Value* lhs = mValues.top(); mValues.pop(); - llvm::Value* result = nullptr; - if (!this->binaryExpression(result, lhs, rhs, node->operation(), node)) return false; + if (!this->traverse(node->lhs())) return false; + + openvdb::ax::ast::tokens::OperatorToken opToken = node->operation(); + // if AND or OR, need to handle short-circuiting + if (opToken == openvdb::ax::ast::tokens::OperatorToken::AND + || opToken == openvdb::ax::ast::tokens::OperatorToken::OR) { + + llvm::Value* lhs = mValues.top(); mValues.pop(); + llvm::Type* lhsType = lhs->getType(); + if (lhsType->isPointerTy()) { + lhs = mBuilder.CreateLoad(lhs); + lhsType = lhsType->getPointerElementType(); + } + + if (lhsType->isFloatingPointTy() || lhsType->isIntegerTy()) { + lhs = boolComparison(lhs, mBuilder); + } + else { + if (!mLog.error("cannot convert non-scalar lhs type to bool", node)) return false; + } + + llvm::BasicBlock* rhsBlock = llvm::BasicBlock::Create(mContext, "binary_rhs", mFunction); + llvm::BasicBlock* returnBlock = llvm::BasicBlock::Create(mContext, "binary_return", mFunction); + llvm::BranchInst* lhsBranch = nullptr; + + if (opToken == openvdb::ax::ast::tokens::OperatorToken::AND) { + lhsBranch = mBuilder.CreateCondBr(lhs, rhsBlock, returnBlock); + } + else { + lhsBranch = mBuilder.CreateCondBr(lhs, returnBlock, rhsBlock); + } + + mBuilder.SetInsertPoint(rhsBlock); + if (!this->traverse(node->rhs())) return false; + + llvm::Value* rhs = mValues.top(); mValues.pop(); + llvm::Type* rhsType = rhs->getType(); + if (rhsType->isPointerTy()) { + rhs = mBuilder.CreateLoad(rhs); + rhsType = rhsType->getPointerElementType(); + } + + if (rhsType->isFloatingPointTy() || rhsType->isIntegerTy()) { + rhs = boolComparison(rhs, mBuilder); + } + else { + if (!mLog.error("cannot convert non-scalar rhs type to bool", node)) return false; + } - if (result) mValues.push(result); + llvm::BranchInst* rhsBranch = mBuilder.CreateBr(returnBlock); + + mBuilder.SetInsertPoint(returnBlock); + llvm::PHINode* result = mBuilder.CreatePHI(LLVMType::get(mContext), 2, "binary_op"); + result->addIncoming(lhs, lhsBranch->getParent()); + result->addIncoming(rhs, rhsBranch->getParent()); + mValues.push(result); + } + else { + if (!this->traverse(node->rhs())) return false; + llvm::Value* rhs = mValues.top(); mValues.pop(); + llvm::Value* lhs = mValues.top(); + llvm::Value* result = nullptr; + if (!this->binaryExpression(result, lhs, rhs, node->operation(), node)) return false; + + if (result) { + mValues.pop(); + mValues.push(result); + } + } return true; } @@ -520,7 +612,7 @@ bool ComputeGenerator::visit(const ast::UnaryOperator* node) return true; } - llvm::Value* value = mValues.top(); mValues.pop(); + llvm::Value* value = mValues.top(); llvm::Type* type = value->getType(); if (type->isPointerTy()) { type = type->getPointerElementType(); @@ -599,7 +691,10 @@ bool ComputeGenerator::visit(const ast::UnaryOperator* node) if (!mLog.error("value is not a scalar or vector", node)) return false; } - if (result) mValues.push(result); + if (result) { + mValues.pop(); + mValues.push(result); + } return true; } @@ -638,7 +733,6 @@ bool ComputeGenerator::visit(const ast::Crement* node) if (!mLog.error("unable to assign to an rvalue", node)) return false; return true; } - mValues.pop(); llvm::Value* rvalue = mBuilder.CreateLoad(value); llvm::Type* type = rvalue->getType(); @@ -661,6 +755,7 @@ bool ComputeGenerator::visit(const ast::Crement* node) // decide what to put on the expression stack } + mValues.pop(); if (node->post()) mValues.push(rvalue); else mValues.push(value); @@ -669,8 +764,7 @@ bool ComputeGenerator::visit(const ast::Crement* node) bool ComputeGenerator::visit(const ast::FunctionCall* node) { - - const FunctionGroup::Ptr function = this->getFunction(node->name()); + const FunctionGroup* const function = this->getFunction(node->name()); if (!function) { if (!mLog.error("unable to locate function \"" + node->name() + "\".", node)) return false; } @@ -1040,11 +1134,10 @@ bool ComputeGenerator::visit(const ast::Value* node) return true; } -FunctionGroup::Ptr ComputeGenerator::getFunction(const std::string &identifier, +const FunctionGroup* ComputeGenerator::getFunction(const std::string &identifier, const bool allowInternal) { - FunctionGroup::Ptr function = mFunctionRegistry.getOrInsert(identifier, mOptions, allowInternal); - return function; + return mFunctionRegistry.getOrInsert(identifier, mOptions, allowInternal); } template @@ -1053,19 +1146,14 @@ ComputeGenerator::visit(const ast::Value* node) { using ContainerT = typename ast::Value::ContainerType; - const ValueType literal = static_cast(node->value()); - - static const ContainerT max = static_cast(std::numeric_limits::max()); - if (node->text()) { - if (!mLog.warning("integer constant is too large to be represented: " + *(node->text()) - + " will be converted to \"" + std::to_string(literal) + "\"", node)) return false; - } - else if (node->asContainerType() > max) { - if (!mLog.warning("signed integer overflow " + std::to_string(node->asContainerType()) - + " cannot be represented in type '" + openvdb::typeNameAsString() + "'", node)) return false; + static const ContainerT max = + static_cast(std::numeric_limits::max()); + if (node->asContainerType() > max) { + if (!mLog.warning("signed integer overflow in integer literal " + + std::to_string(node->asContainerType()), node)) return false; } - llvm::Constant* value = LLVMType::get(mContext, literal); + llvm::Constant* value = LLVMType::get(mContext, node->value()); mValues.push(value); return true; } @@ -1074,23 +1162,8 @@ template typename std::enable_if::value, bool>::type ComputeGenerator::visit(const ast::Value* node) { - using ContainerT = typename ast::Value::ContainerType; - - assert(std::isinf(node->value()) || node->value() >= static_cast(0.0)); - const ValueType literal = static_cast(node->value()); - - static const ContainerT max = static_cast(std::numeric_limits::max()); - if (node->text()) { - if (!mLog.warning("floating constant exceeds range of double: " + *(node->text()) - + " will be converted to inf.", node)) return false; - } - else if (node->asContainerType() > max) { - if (!mLog.warning("floating point overflow " + std::to_string(node->asContainerType()) - + " cannot be represented in type '" + openvdb::typeNameAsString() - + "' and will be converted to \"" + std::to_string(literal) + "\"", node)) return false; - } - - llvm::Constant* value = LLVMType::get(mContext, literal); + assert(std::isinf(node->value()) || node->value() >= 0.0); + llvm::Constant* value = LLVMType::get(mContext, node->value()); mValues.push(value); return true; } @@ -1195,6 +1268,17 @@ bool ComputeGenerator::assignExpression(llvm::Value* lhs, llvm::Value*& rhs, con assert(rsize == lsize || (rsize == 1 || lsize == 1)); const size_t resultsize = std::max(lsize, rsize); + if (ltype != rtype) { + llvm::Type* letype = ltype->isArrayTy() ? ltype->getArrayElementType() : ltype; + llvm::Type* retype = rtype->isArrayTy() ? rtype->getArrayElementType() : rtype; + if (letype != retype) { + llvm::Type* highest = typePrecedence(letype, retype); + if (highest != letype) { + if (!mLog.warning("implicit conversion in assignment (possible truncation)", node)) return false; + } + } + } + // compute the componentwise precision llvm::Type* opprec = ltype->isArrayTy() ? ltype->getArrayElementType() : ltype; @@ -1399,12 +1483,26 @@ bool ComputeGenerator::binaryExpression(llvm::Value*& result, llvm::Value* lhs, assert(rtype->isArrayTy() || rtype->isFloatingPointTy() || rtype->isIntegerTy()); assert(rsize == lsize || (rsize == 1 || lsize == 1)); + if (op == ast::tokens::DIVIDE || op == ast::tokens::MODULO) { + if (llvm::Constant* c = llvm::dyn_cast(rhs)) { + if (c->isZeroValue()) { + if (op == ast::tokens::DIVIDE) { + if (!mLog.warning("division by zero is undefined", node)) return false; + } + else { + if (!mLog.warning("modulo by zero is undefined", node)) return false; + } + } + } + } + // compute the componentwise precision llvm::Type* opprec = ltype->isArrayTy() ? ltype->getArrayElementType() : ltype; opprec = rtype->isArrayTy() ? typePrecedence(opprec, rtype->getArrayElementType()) : typePrecedence(opprec, rtype); + // if bool, the lowest precision and subsequent result should be int32 // for arithmetic, bitwise and certain other ops // @note - no bool containers, so if the type is a container, it can't @@ -1560,6 +1658,8 @@ bool ComputeGenerator::binaryExpression(llvm::Value*& result, llvm::Value* lhs, } +} // namespace codegen_internal + } // namespace codegen } // namespace ax } // namespace OPENVDB_VERSION_NAME diff --git a/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.h b/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.h index dad09ecf26..710649a047 100644 --- a/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.h +++ b/openvdb_ax/openvdb_ax/codegen/ComputeGenerator.h @@ -64,6 +64,7 @@ struct ComputeKernel /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// +namespace codegen_internal { struct ComputeGenerator : public ast::Visitor { @@ -107,6 +108,16 @@ struct ComputeGenerator : public ast::Visitor return true; } + /// @brief Custom traversal of binary operators + /// @note This overrides the default traversal to handle + /// short-circuiting in logical AND and OR + bool traverse(const ast::BinaryOperator* bin) + { + if (!bin) return true; + if (!this->visit(bin)) return false; + return true; + } + /// @brief Custom traversal of ternary operators /// @note This overrides the default traversal to handle /// branching between different code paths @@ -176,7 +187,7 @@ struct ComputeGenerator : public ast::Visitor protected: - FunctionGroup::Ptr getFunction(const std::string& identifier, + const FunctionGroup* getFunction(const std::string& identifier, const bool allowInternal = false); bool binaryExpression(llvm::Value*& result, llvm::Value* lhs, llvm::Value* rhs, @@ -210,6 +221,8 @@ struct ComputeGenerator : public ast::Visitor FunctionRegistry& mFunctionRegistry; }; +} // codegen_internal + } // namespace codegen } // namespace ax } // namespace OPENVDB_VERSION_NAME diff --git a/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.cc b/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.cc index 72949b6185..ac446bd9fe 100644 --- a/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.cc +++ b/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.cc @@ -42,18 +42,18 @@ void FunctionRegistry::insertAndCreate(const std::string& identifier, inserted.first->second.create(op); } -FunctionGroup::Ptr FunctionRegistry::getOrInsert(const std::string& identifier, +const FunctionGroup* FunctionRegistry::getOrInsert(const std::string& identifier, const FunctionOptions& op, const bool allowInternalAccess) { auto iter = mMap.find(identifier); - if (iter == mMap.end()) return FunctionGroup::Ptr(); + if (iter == mMap.end()) return nullptr; FunctionRegistry::RegisteredFunction& reg = iter->second; - if (!allowInternalAccess && reg.isInternal()) return FunctionGroup::Ptr(); + if (!allowInternalAccess && reg.isInternal()) return nullptr; if (!reg.function()) reg.create(op); - FunctionGroup::Ptr function = reg.function(); + const FunctionGroup* const function = reg.function(); // initialize function dependencies if necessary @@ -64,7 +64,7 @@ FunctionGroup::Ptr FunctionRegistry::getOrInsert(const std::string& identifier, // if the function ptr doesn't exist, create it with getOrInsert. // This provides internal access and ensures handling of cyclical // dependencies do not cause a problem - FunctionGroup::Ptr internal = this->get(dep, true); + const FunctionGroup* const internal = this->get(dep, true); if (!internal) this->getOrInsert(dep, op, true); } } @@ -73,11 +73,11 @@ FunctionGroup::Ptr FunctionRegistry::getOrInsert(const std::string& identifier, return function; } -FunctionGroup::Ptr FunctionRegistry::get(const std::string& identifier, const bool allowInternalAccess) const +const FunctionGroup* FunctionRegistry::get(const std::string& identifier, const bool allowInternalAccess) const { auto iter = mMap.find(identifier); - if (iter == mMap.end()) return FunctionGroup::Ptr(); - if (!allowInternalAccess && iter->second.isInternal()) return FunctionGroup::Ptr(); + if (iter == mMap.end()) return nullptr; + if (!allowInternalAccess && iter->second.isInternal()) return nullptr; return iter->second.function(); } diff --git a/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.h b/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.h index 4eb0572fbe..44c20ba658 100644 --- a/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.h +++ b/openvdb_ax/openvdb_ax/codegen/FunctionRegistry.h @@ -28,46 +28,38 @@ namespace OPENVDB_VERSION_NAME { namespace ax { namespace codegen { -/// @brief The global function registry which is used for function code generation. -/// Each time a function is visited within the AST, its identifier is used -/// as a key into this registry for the corresponding function retrieval -/// and execution. -/// -/// Functions can be inserted into the registry using insert() with a given -/// identifier and pointer to a function base. -/// +/// @brief The function registry which is used for function code generation. +/// Each time a function is visited within the AST, its identifier is used as +/// a key into this registry for the corresponding function retrieval and +/// execution. Functions can be inserted into the registry using insert() with +/// a given identifier and pointer. class FunctionRegistry { public: - - using ConstructorT = FunctionGroup::Ptr(*)(const FunctionOptions&); + using ConstructorT = FunctionGroup::UniquePtr(*)(const FunctionOptions&); using Ptr = std::shared_ptr; using UniquePtr = std::unique_ptr; - /// @brief An object to represent a registered function, storing its constructor, - /// a pointer to the function definition and whether it should only be available - // internally (i.e. to a developer, not a user) + /// @brief An object to represent a registered function, storing its + /// constructor, a pointer to the function definition and whether it + /// should only be available internally (i.e. to a developer, not a user) /// struct RegisteredFunction { /// @brief Constructor /// @param creator The function definition used to create this function /// @param internal Whether the function should be only internally accessible - /// RegisteredFunction(const ConstructorT& creator, const bool internal = false) : mConstructor(creator), mFunction(), mInternal(internal) {} /// @brief Create a function object using this creator of this function /// @param op The current function options - /// inline void create(const FunctionOptions& op) { mFunction = mConstructor(op); } /// @brief Return a pointer to this function definition - /// - inline FunctionGroup::Ptr function() const { return mFunction; } + inline const FunctionGroup* function() const { return mFunction.get(); } /// @brief Check whether this function should be only internally accesible - /// inline bool isInternal() const { return mInternal; } private: @@ -78,66 +70,61 @@ class FunctionRegistry using RegistryMap = std::unordered_map; - /// @brief Insert and register a function base object to a function identifier. - /// @note Throws if the identifier is already registered - /// - /// @param identifier The function identifier to register - /// @param creator The function base to link to the provided identifier - /// @param internal Whether to mark the function as only internally accessible + /// @brief Insert and register a function object to a function identifier. + /// @note Throws if the identifier is already registered /// - void insert(const std::string& identifier, const ConstructorT creator, const bool internal = false); - - /// @brief Insert and register a function base object to a function identifier. - /// @note Throws if the identifier is already registered - /// - /// @param identifier The function identifier to register - /// @param creator The function base to link to the provided identifier - /// @param op FunctionOptions to pass the function constructor - /// @param internal Whether to mark the function as only internally accessible + /// @param identifier The function identifier to register + /// @param creator The function to link to the provided identifier + /// @param internal Whether to mark the function as only internally accessible + void insert(const std::string& identifier, + const ConstructorT creator, + const bool internal = false); + + /// @brief Insert and register a function object to a function identifier. + /// @note Throws if the identifier is already registered /// + /// @param identifier The function identifier to register + /// @param creator The function to link to the provided identifier + /// @param op FunctionOptions to pass the function constructor + /// @param internal Whether to mark the function as only internally accessible void insertAndCreate(const std::string& identifier, const ConstructorT creator, const FunctionOptions& op, const bool internal = false); - /// @brief Return the corresponding function object from a provided function identifier - /// @note Returns a nullptr if no such function identifier has been registered or if the - /// function is marked as internal + /// @brief Return the corresponding function from a provided function identifier + /// @note Returns a nullptr if no such function identifier has been + /// registered or if the function is marked as internal /// /// @param identifier The function identifier /// @param op FunctionOptions to pass the function constructor - /// @param allowInternalAccess Whether to look in the 'internal' functions - /// - FunctionGroup::Ptr getOrInsert(const std::string& identifier, + /// @param allowInternalAccess Whether to look in the 'internal' functions + const FunctionGroup* getOrInsert(const std::string& identifier, const FunctionOptions& op, const bool allowInternalAccess); - /// @brief Return the corresponding function object from a provided function identifier - /// @note Returns a nullptr if no such function identifier has been registered or if the - /// function is marked as internal - /// - /// @param identifier The function identifier - /// @param allowInternalAccess Whether to look in the 'internal' functions + /// @brief Return the corresponding function from a provided function identifier + /// @note Returns a nullptr if no such function identifier has been + /// registered or if the function is marked as internal /// - FunctionGroup::Ptr get(const std::string& identifier, + /// @param identifier The function identifier + /// @param allowInternalAccess Whether to look in the 'internal' functions + const FunctionGroup* get(const std::string& identifier, const bool allowInternalAccess) const; - /// @brief Force the (re)creations of all function objects for all registered functions - /// @param op The current function options - /// @param verify Checks functions are created and have valid identifiers/symbols - /// + /// @brief Force the (re)creations of all function objects for all + /// registered functions + /// @param op The current function options + /// @param verify Checks functions are created and have valid identifiers/symbols void createAll(const FunctionOptions& op, const bool verify = false); /// @brief Return a const reference to the current registry map - /// inline const RegistryMap& map() const { return mMap; } /// @brief Return whether or not the registry is empty - /// inline bool empty() const { return mMap.empty(); } /// @brief Clear the underlying function registry - /// inline void clear() { mMap.clear(); } private: diff --git a/openvdb_ax/openvdb_ax/codegen/FunctionTypes.h b/openvdb_ax/openvdb_ax/codegen/FunctionTypes.h index 1dfb612275..ade7cb5489 100644 --- a/openvdb_ax/openvdb_ax/codegen/FunctionTypes.h +++ b/openvdb_ax/openvdb_ax/codegen/FunctionTypes.h @@ -778,6 +778,7 @@ struct IRFunctionSRet : public SRetFunction> struct FunctionGroup { using Ptr = std::shared_ptr; + using UniquePtr = std::unique_ptr; using FunctionList = std::vector; FunctionGroup(const char* name, @@ -1031,7 +1032,7 @@ struct FunctionBuilder inline FunctionBuilder& setDocumentation(const char* doc) { mDoc = doc; return *this; } inline FunctionBuilder& setPreferredImpl(DeclPreferrence pref) { mDeclPref = pref; return *this; } - inline FunctionGroup::Ptr get() const + inline FunctionGroup::UniquePtr get() const { for (auto& decl : mCFunctions) { const auto& s = mSettings.at(decl.get()); @@ -1076,7 +1077,7 @@ struct FunctionBuilder functions.insert(functions.end(), mCFunctions.begin(), mCFunctions.end()); } - FunctionGroup::Ptr group(new FunctionGroup(mName, mDoc, functions)); + FunctionGroup::UniquePtr group(new FunctionGroup(mName, mDoc, functions)); return group; } diff --git a/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.cc b/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.cc index d4ef8bdea5..fcfb6bee17 100644 --- a/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.cc +++ b/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.cc @@ -59,6 +59,8 @@ std::string PointRangeKernel::getDefaultName() { return "ax.compute.pointrange"; /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// +namespace codegen_internal { + PointComputeGenerator::PointComputeGenerator(llvm::Module& module, const FunctionOptions& options, FunctionRegistry& functionRegistry, @@ -230,7 +232,7 @@ AttributeRegistry::Ptr PointComputeGenerator::generate(const ast::Tree& tree) const bool usingString = type == strType; llvm::Value* handlePtr = this->attributeHandleFromToken(token); - const FunctionGroup::Ptr function = this->getFunction("setattribute", true); + const FunctionGroup* const function = this->getFunction("setattribute", true); // load the result (if its a scalar) if (type->isIntegerTy() || type->isFloatingPointTy()) { @@ -288,7 +290,7 @@ void PointComputeGenerator::getAttributeValue(const std::string& globalName, llv const bool usingString = type == "string"; if (usingString) { - const FunctionGroup::Ptr function = this->getFunction("strattribsize", true); + const FunctionGroup* const function = this->getFunction("strattribsize", true); llvm::Value* size = function->execute({handlePtr, pointidx, leafdata}, mBuilder); @@ -318,7 +320,7 @@ void PointComputeGenerator::getAttributeValue(const std::string& globalName, llv if (usingString) args.emplace_back(leafdata); - const FunctionGroup::Ptr function = this->getFunction("getattribute", true); + const FunctionGroup* const function = this->getFunction("getattribute", true); function->execute(args, mBuilder); } @@ -346,6 +348,7 @@ llvm::Value* PointComputeGenerator::attributeHandleFromToken(const std::string& return mBuilder.CreateLoad(handlePtr); } +} // namespace codegen_internal } // namespace codegen } // namespace ax diff --git a/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.h b/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.h index ff3b115a6b..334cb88627 100644 --- a/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.h +++ b/openvdb_ax/openvdb_ax/codegen/PointComputeGenerator.h @@ -75,6 +75,7 @@ struct PointRangeKernel : public PointKernel /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// +namespace codegen_internal { /// @brief Visitor object which will generate llvm IR for a syntax tree which has been generated from /// AX that targets point grids. The IR will represent 2 functions : one that executes over @@ -106,6 +107,8 @@ struct PointComputeGenerator : public ComputeGenerator void getAttributeValue(const std::string& globalName, llvm::Value* location); }; +} // namespace namespace codegen_internal + } // namespace codegen } // namespace ax } // namespace OPENVDB_VERSION_NAME diff --git a/openvdb_ax/openvdb_ax/codegen/PointFunctions.cc b/openvdb_ax/openvdb_ax/codegen/PointFunctions.cc index be59b44c13..865c43dd8d 100644 --- a/openvdb_ax/openvdb_ax/codegen/PointFunctions.cc +++ b/openvdb_ax/openvdb_ax/codegen/PointFunctions.cc @@ -16,10 +16,10 @@ #include "FunctionTypes.h" #include "Types.h" #include "Utils.h" +#include "PointLeafLocalData.h" #include "../ast/Tokens.h" #include "../compiler/CompilerOptions.h" -#include "../compiler/LeafLocalData.h" #include "../Exceptions.h" #include @@ -65,7 +65,7 @@ groupHandle(const std::string& name, void** groupHandles, const void* const data } -inline FunctionGroup::Ptr ax_ingroup(const FunctionOptions& op) +inline FunctionGroup::UniquePtr ax_ingroup(const FunctionOptions& op) { static auto ingroup = [](const AXString* const name, @@ -87,8 +87,8 @@ inline FunctionGroup::Ptr ax_ingroup(const FunctionOptions& op) // If the handle doesn't exist, check to see if any new groups have // been added - const openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + const codegen_internal::PointLeafLocalData* const leafData = + static_cast(leafDataPtr); handle = leafData->get(nameStr); return handle ? handle->get(static_cast(index)) : false; }; @@ -117,7 +117,7 @@ inline FunctionGroup::Ptr ax_ingroup(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axingroup(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axingroup(const FunctionOptions& op) { static auto generate = [op](const std::vector& args, @@ -155,7 +155,7 @@ inline FunctionGroup::Ptr axingroup(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axeditgroup(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axeditgroup(const FunctionOptions& op) { static auto editgroup = [](const AXString* const name, @@ -178,8 +178,8 @@ inline FunctionGroup::Ptr axeditgroup(const FunctionOptions& op) } if (!handle) { - openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + codegen_internal::PointLeafLocalData* const leafData = + static_cast(leafDataPtr); // If we are setting membership and the handle doesnt exist, create in in // the set of new data thats being added @@ -211,7 +211,7 @@ inline FunctionGroup::Ptr axeditgroup(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axaddtogroup(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axaddtogroup(const FunctionOptions& op) { static auto generate = [op](const std::vector& args, @@ -251,7 +251,7 @@ inline FunctionGroup::Ptr axaddtogroup(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axremovefromgroup(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axremovefromgroup(const FunctionOptions& op) { static auto generate = [op](const std::vector& args, @@ -290,7 +290,7 @@ inline FunctionGroup::Ptr axremovefromgroup(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axdeletepoint(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axdeletepoint(const FunctionOptions& op) { static auto generate = [op](const std::vector&, @@ -321,7 +321,7 @@ inline FunctionGroup::Ptr axdeletepoint(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axsetattribute(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axsetattribute(const FunctionOptions& op) { static auto setattribptr = [](void* attributeHandle, uint64_t index, const auto value) @@ -355,8 +355,8 @@ inline FunctionGroup::Ptr axsetattribute(const FunctionOptions& op) const std::string s(value->ptr, value->size); AttributeHandleType* const handle = static_cast(attributeHandle); - openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + codegen_internal::PointLeafLocalData* const leafData = + static_cast(leafDataPtr); // Check to see if the string exists in the metadata cache. If so, set the string and // remove any new data associated with it, otherwise set the new data @@ -436,7 +436,7 @@ inline FunctionGroup::Ptr axsetattribute(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axgetattribute(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axgetattribute(const FunctionOptions& op) { static auto getattrib = [](void* attributeHandle, uint64_t index, auto value) @@ -472,8 +472,8 @@ inline FunctionGroup::Ptr axgetattribute(const FunctionOptions& op) AttributeHandleType* const handle = static_cast(attributeHandle); - const openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + const codegen_internal::PointLeafLocalData* const leafData = + static_cast(leafDataPtr); std::string data; if (!leafData->getNewStringData(&(handle->array()), index, data)) { @@ -538,7 +538,7 @@ inline FunctionGroup::Ptr axgetattribute(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axstrattribsize(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axstrattribsize(const FunctionOptions& op) { static auto strattribsize = [](void* attributeHandle, @@ -553,8 +553,8 @@ inline FunctionGroup::Ptr axstrattribsize(const FunctionOptions& op) const AttributeHandleType* const handle = static_cast(attributeHandle); - const openvdb::ax::compiler::LeafLocalData* const leafData = - static_cast(leafDataPtr); + const codegen_internal::PointLeafLocalData* const leafData = + static_cast(leafDataPtr); std::string data; if (!leafData->getNewStringData(&(handle->array()), index, data)) { diff --git a/openvdb_ax/openvdb_ax/compiler/LeafLocalData.h b/openvdb_ax/openvdb_ax/codegen/PointLeafLocalData.h similarity index 97% rename from openvdb_ax/openvdb_ax/compiler/LeafLocalData.h rename to openvdb_ax/openvdb_ax/codegen/PointLeafLocalData.h index 0cd63cee6d..ac1ef37032 100644 --- a/openvdb_ax/openvdb_ax/compiler/LeafLocalData.h +++ b/openvdb_ax/openvdb_ax/codegen/PointLeafLocalData.h @@ -1,7 +1,7 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -/// @file compiler/LeafLocalData.h +/// @file codegen/PointLeafLocalData.h /// /// @authors Nick Avramoussis /// @@ -23,7 +23,9 @@ OPENVDB_USE_VERSION_NAMESPACE namespace OPENVDB_VERSION_NAME { namespace ax { -namespace compiler { +namespace codegen { + +namespace codegen_internal { /// @brief Various functions can request the use and initialization of point data from within @@ -38,9 +40,9 @@ namespace compiler { /// maps are used for temporary storage per point. The maps use the string array /// pointers as a key for later synchronization. /// -struct LeafLocalData +struct PointLeafLocalData { - using UniquePtr = std::unique_ptr; + using UniquePtr = std::unique_ptr; using GroupArrayT = openvdb::points::GroupAttributeArray; using GroupHandleT = openvdb::points::GroupWriteHandle; @@ -55,7 +57,7 @@ struct LeafLocalData /// @param count The number of points within the current leaf, used to initialize /// the size of new arrays /// - LeafLocalData(const size_t count) + PointLeafLocalData(const size_t count) : mPointCount(count) , mArrays() , mOffset(0) @@ -221,6 +223,8 @@ struct LeafLocalData StringArrayMap mStringMap; }; +} // codegen_internal + } // namespace compiler } // namespace ax } // namespace OPENVDB_VERSION_NAME diff --git a/openvdb_ax/openvdb_ax/codegen/StandardFunctions.cc b/openvdb_ax/openvdb_ax/codegen/StandardFunctions.cc index 486ca640a6..7f162c34f3 100644 --- a/openvdb_ax/openvdb_ax/codegen/StandardFunctions.cc +++ b/openvdb_ax/openvdb_ax/codegen/StandardFunctions.cc @@ -20,15 +20,13 @@ #include "../compiler/CompilerOptions.h" #include "../compiler/CustomData.h" -#include -#include -#include - #include #include #include +#include +#include #include #include #include @@ -48,7 +46,7 @@ namespace // that fit into an unsigned int, and then shift those bytes out of the // hash. We repeat until we have no bits left in the hash. template -inline SeedType hashToSeed(std::size_t hash) { +inline SeedType hashToSeed(size_t hash) { SeedType seed = 0; do { seed ^= (SeedType) hash; @@ -77,7 +75,7 @@ struct SimplexNoise /////////////////////////////////////////////////////////////////////////// #define DEFINE_LLVM_FP_INTRINSIC(Identifier, Doc) \ - inline FunctionGroup::Ptr llvm_##Identifier(const FunctionOptions& op) \ + inline FunctionGroup::UniquePtr llvm_##Identifier(const FunctionOptions& op) \ { \ static auto generate = \ [](const std::vector& args, \ @@ -105,7 +103,7 @@ struct SimplexNoise } \ #define DEFINE_AX_C_FP_BINDING(Identifier, Doc) \ - inline FunctionGroup::Ptr ax##Identifier(const FunctionOptions& op) \ + inline FunctionGroup::UniquePtr ax##Identifier(const FunctionOptions& op) \ { \ return FunctionBuilder(#Identifier) \ .addSignature((double(*)(double))(std::Identifier)) \ @@ -143,7 +141,7 @@ DEFINE_LLVM_FP_INTRINSIC(round, "Computes the nearest integer value to arg (in f // pow created explicitly as it takes two arguments and performs slighlty different // calls for integer exponents -inline FunctionGroup::Ptr llvm_pow(const FunctionOptions& op) +inline FunctionGroup::UniquePtr llvm_pow(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -180,7 +178,7 @@ inline FunctionGroup::Ptr llvm_pow(const FunctionOptions& op) DEFINE_AX_C_FP_BINDING(cbrt, "Computes the cubic root of the input.") -inline FunctionGroup::Ptr axabs(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axabs(const FunctionOptions& op) { auto generate = [op](const std::vector& args, @@ -222,7 +220,7 @@ inline FunctionGroup::Ptr axabs(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axdot(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axdot(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -266,7 +264,7 @@ inline FunctionGroup::Ptr axdot(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axcross(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axcross(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -326,7 +324,7 @@ inline FunctionGroup::Ptr axcross(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axlengthsq(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axlengthsq(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -376,7 +374,7 @@ inline FunctionGroup::Ptr axlengthsq(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axlength(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axlength(const FunctionOptions& op) { auto generate = [op](const std::vector& args, @@ -421,7 +419,7 @@ inline FunctionGroup::Ptr axlength(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axnormalize(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axnormalize(const FunctionOptions& op) { auto generate = [op](const std::vector& args, @@ -482,27 +480,99 @@ inline FunctionGroup::Ptr axnormalize(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axlerp(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axlerp(const FunctionOptions& op) { static auto generate = [](const std::vector& args, llvm::IRBuilder<>& B) -> llvm::Value* { assert(args.size() == 3); - llvm::Value* a = args[0]; - llvm::Value* b = args[1]; - llvm::Value* x = args[2]; + llvm::Value* a = args[0], *b = args[1], *t = args[2]; + llvm::Value* zero = llvm::ConstantFP::get(a->getType(), 0.0); llvm::Value* one = llvm::ConstantFP::get(a->getType(), 1.0); - llvm::Value* result = binaryOperator(one, x, ast::tokens::MINUS, B); - result = binaryOperator(result, a, ast::tokens::MULTIPLY, B); - llvm::Value* right = binaryOperator(x, b, ast::tokens::MULTIPLY, B); - result = binaryOperator(result, right, ast::tokens::PLUS, B); - return result; + llvm::Function* base = B.GetInsertBlock()->getParent(); + + // @todo short-circuit? + llvm::Value* a1 = binaryOperator(a, zero, ast::tokens::LESSTHANOREQUAL, B); + llvm::Value* b1 = binaryOperator(b, zero, ast::tokens::MORETHANOREQUAL, B); + llvm::Value* a2 = binaryOperator(a, zero, ast::tokens::MORETHANOREQUAL, B); + llvm::Value* b2 = binaryOperator(b, zero, ast::tokens::LESSTHANOREQUAL, B); + a1 = binaryOperator(a1, b1, ast::tokens::AND, B); + a2 = binaryOperator(a2, b2, ast::tokens::AND, B); + a1 = binaryOperator(a1, a2, ast::tokens::OR, B); + + llvm::BasicBlock* then = llvm::BasicBlock::Create(B.getContext(), "then", base); + llvm::BasicBlock* post = llvm::BasicBlock::Create(B.getContext(), "post", base); + B.CreateCondBr(a1, then, post); + + B.SetInsertPoint(then); + llvm::Value* r = binaryOperator(one, t, ast::tokens::MINUS, B); + r = binaryOperator(r, a, ast::tokens::MULTIPLY, B); + llvm::Value* right = binaryOperator(t, b, ast::tokens::MULTIPLY, B); + r = binaryOperator(r, right, ast::tokens::PLUS, B); + B.CreateRet(r); + + B.SetInsertPoint(post); + + llvm::Value* tisone = binaryOperator(t, one, ast::tokens::EQUALSEQUALS, B); + + then = llvm::BasicBlock::Create(B.getContext(), "then", base); + post = llvm::BasicBlock::Create(B.getContext(), "post", base); + B.CreateCondBr(tisone, then, post); + + B.SetInsertPoint(then); + B.CreateRet(b); + + B.SetInsertPoint(post); + + // if nlerp + llvm::Value* x = binaryOperator(b, a, ast::tokens::MINUS, B); + x = binaryOperator(t, x, ast::tokens::MULTIPLY, B); + x = binaryOperator(a, x, ast::tokens::PLUS, B); + + then = llvm::BasicBlock::Create(B.getContext(), "then", base); + post = llvm::BasicBlock::Create(B.getContext(), "post", base); + + a1 = binaryOperator(t, one, ast::tokens::MORETHAN, B); + a2 = binaryOperator(b, a, ast::tokens::MORETHAN, B); + a1 = binaryOperator(a1, a2, ast::tokens::EQUALSEQUALS, B); + B.CreateCondBr(a1, then, post); + + B.SetInsertPoint(then); + b1 = binaryOperator(b, x, ast::tokens::LESSTHAN, B); + B.CreateRet(B.CreateSelect(b1, x, b)); + + B.SetInsertPoint(post); + b1 = binaryOperator(x, b, ast::tokens::LESSTHAN, B); + return B.CreateRet(B.CreateSelect(b1, x, b)); }; - static auto lerp = [](auto a, auto b, auto x) -> auto { + // This lerp implementation is taken from clang and matches the C++20 standard + static auto lerp = [](auto a, auto b, auto t) -> auto + { using ValueT = decltype(a); - return (ValueT(1.0) - x) * a + x * b; + // If there is a zero crossing with a,b do the more precise + // linear interpolation. This is only monotonic if there is + // a zero crossing + // Exact, monotonic, bounded, determinate, and (for a=b=0) + // consistent + if ((a <= 0 && b >= 0) || (a >= 0 && b <= 0)) { + return t * b + (ValueT(1.0) - t) * a; + } + // If t is exactly 1, return b (as the second impl doesn't + // guarantee this due to fp arithmetic) + if (t == ValueT(1.0)) return b; + // less precise interpolation when there is no crossing + // Exact at t=0, monotonic except near t=1, bounded, + // determinate, and consistent + const auto x = a + t * (b - a); + // Ensure b is preferred to another equal value (i.e. -0. vs. +0.), + // which avoids returning -0 for t==1 but +0 for other nearby + // values of t. This branching also stops nans being returns from + // inf inputs + // monotonic near t=1 + if ((t > ValueT(1.0)) == (b > a)) return b < x ? x : b; + else return x < b ? x : b; }; return FunctionBuilder("lerp") @@ -515,14 +585,14 @@ inline FunctionGroup::Ptr axlerp(const FunctionOptions& op) .addFunctionAttribute(llvm::Attribute::AlwaysInline) .setConstantFold(op.mConstantFoldCBindings) .setPreferredImpl(op.mPrioritiseIR ? FunctionBuilder::IR : FunctionBuilder::C) - .setDocumentation("Performs bilinear interpolation between the values. If the amount is " - "outside the range 0 to 1, the values will be extrapolated linearly. If " - "amount is 0, the first value is returned. If it is 1, the second value " - "is returned.") + .setDocumentation("Performs bilinear interpolation between the values. If the " + "amount is outside the range 0 to 1, the values will be extrapolated linearly. " + "If amount is 0, the first value is returned. If it is 1, the second value " + "is returned. This implementation is guaranteed to be monotonic.") .get(); } -inline FunctionGroup::Ptr axmin(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axmin(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -553,7 +623,7 @@ inline FunctionGroup::Ptr axmin(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axmax(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axmax(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -584,7 +654,7 @@ inline FunctionGroup::Ptr axmax(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axclamp(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axclamp(const FunctionOptions& op) { auto generate = [op](const std::vector& args, @@ -619,7 +689,7 @@ inline FunctionGroup::Ptr axclamp(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axfit(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axfit(const FunctionOptions& op) { auto generate = [op](const std::vector& args, @@ -708,24 +778,79 @@ inline FunctionGroup::Ptr axfit(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axrand(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axrand(const FunctionOptions& op) { struct Rand { - static double rand(const uint32_t* seed) + static double rand(const std::mt19937_64::result_type* seed) { using ThreadLocalEngineContainer = - tbb::enumerable_thread_specific; + tbb::enumerable_thread_specific; + static ThreadLocalEngineContainer ThreadLocalEngines; + static std::uniform_real_distribution Generator(0.0,1.0); + std::mt19937_64& engine = ThreadLocalEngines.local(); + if (seed) { + engine.seed(static_cast(*seed)); + } + return Generator(engine); + } + + static double rand() { return Rand::rand(nullptr); } + + static double rand(double seed) + { + const std::mt19937_64::result_type hash = + static_cast(std::hash()(seed)); + return Rand::rand(&hash); + } + + static double rand(int64_t seed) + { + const std::mt19937_64::result_type hash = + static_cast(seed); + return Rand::rand(&hash); + } + }; + return FunctionBuilder("rand") + .addSignature((double(*)())(Rand::rand)) + .addSignature((double(*)(double))(Rand::rand)) + .addSignature((double(*)(int64_t))(Rand::rand)) + .setArgumentNames({"seed"}) + // We can't constant fold rand even if it's been called with a constant as + // it will leave the generator in a different state in comparison to other + // threads and, as it relies on an internal state, doesn't respect branching + // etc. We also can't use a different generate for constant calls as subsequent + // calls to rand() without an argument won't know to advance the generator. + .setConstantFold(false) + .setPreferredImpl(op.mPrioritiseIR ? FunctionBuilder::IR : FunctionBuilder::C) + .setDocumentation("Creates a random number based on the provided " + "seed. The number will be in the range of 0 to 1. The same number is " + "produced for the same seed. Note that if rand is called without a seed " + "the previous state of the random number generator is advanced for the " + "currently processing element. This state is determined by the last call to " + "rand() with a given seed. If rand is not called with a seed, the generator " + "advances continuously across different elements which can produce non-" + "deterministic results. It is important that rand is always called with a " + "seed at least once for deterministic results.") + .get(); +} + +inline FunctionGroup::UniquePtr axrand32(const FunctionOptions& op) +{ + struct Rand + { + static double rand(const std::mt19937::result_type* seed) + { + using ThreadLocalEngineContainer = + tbb::enumerable_thread_specific; // Obtain thread-local engine (or create if it doesn't exist already). static ThreadLocalEngineContainer ThreadLocalEngines; - static boost::uniform_01 Generator; - - boost::mt19937& engine = ThreadLocalEngines.local(); + static std::uniform_real_distribution Generator(0.0,1.0); + std::mt19937& engine = ThreadLocalEngines.local(); if (seed) { - engine.seed(static_cast(*seed)); + engine.seed(static_cast(*seed)); } - // Once we have seeded the random number generator, we then evaluate it, // which returns a floating point number in the range [0,1) return Generator(engine); @@ -735,13 +860,13 @@ inline FunctionGroup::Ptr axrand(const FunctionOptions& op) static double rand(double seed) { - // We initially hash the double-precision seed with `boost::hash`. The + // We initially hash the double-precision seed with `std::hash`. The // important thing about the hash is that it produces a "reliable" hash value, // taking into account a number of special cases for floating point numbers // (e.g. -0 and +0 must return the same hash value, etc). Other than these // special cases, this function will usually just copy the binary // representation of a float into the resultant `size_t` - const size_t hash = boost::hash()(seed); + const size_t hash = std::hash()(seed); // Now that we have a reliable hash (with special floating-point cases taken // care of), we proceed to use this hash to seed a random number generator. @@ -750,7 +875,7 @@ inline FunctionGroup::Ptr axrand(const FunctionOptions& op) // // So, we must convert it. I should note that the OpenVDB math libraries will // do this for us, but its implementation static_casts `size_t` to `unsigned int`, - // and because `boost::hash` returns a binary copy of the original + // and because `std::hash` returns a binary copy of the original // double-precision number in almost all cases, this ends up producing noticable // patterns in the result (e.g. by truncating the upper 4 bytes, values of 1.0, // 2.0, 3.0, and 4.0 all return the same hash value because their lower 4 bytes @@ -758,18 +883,22 @@ inline FunctionGroup::Ptr axrand(const FunctionOptions& op) // // We use the `hashToSeed` function to reduce our `size_t` to an `unsigned int`, // whilst taking all bits in the `size_t` into account. - const uint32_t uintseed = hashToSeed(hash); + // On some architectures std::uint_fast32_t may be size_t, but we always hash to + // be consistent. + const std::mt19937::result_type uintseed = + static_cast(hashToSeed(hash)); return Rand::rand(&uintseed); } static double rand(int32_t seed) { - const uint32_t uintseed = static_cast(seed); + const std::mt19937::result_type uintseed = + static_cast(seed); return Rand::rand(&uintseed); } }; - return FunctionBuilder("rand") + return FunctionBuilder("rand32") .addSignature((double(*)())(Rand::rand)) .addSignature((double(*)(double))(Rand::rand)) .addSignature((double(*)(int32_t))(Rand::rand)) @@ -781,19 +910,23 @@ inline FunctionGroup::Ptr axrand(const FunctionOptions& op) // calls to rand() without an argument won't know to advance the generator. .setConstantFold(false) .setPreferredImpl(op.mPrioritiseIR ? FunctionBuilder::IR : FunctionBuilder::C) - .setDocumentation("Creates a random number based on the provided " + .setDocumentation("Creates a random number based on the provided 32 bit " "seed. The number will be in the range of 0 to 1. The same number is " - "produced for the same seed. Note that if rand is called without a seed " - "the previous state of the random number generator is advanced for the " - "currently processing element. This state is determined by the last call to " - "rand() with a given seed. If rand is not called with a seed, the generator " - "advances continuously across different elements which can produce non-" - "deterministic results. It is important that rand is always called with a " - "seed at least once for deterministic results.") + "produced for the same seed. " + "NOTE: This function does not share the same random number generator as " + "rand(). rand32() may provide a higher throughput on some architectures, " + "but will produce different results to rand(). " + "NOTE: If rand32 is called without a seed the previous state of the random " + "number generator is advanced for the currently processing element. This " + "state is determined by the last call to rand32() with a given seed. If " + "rand32 is not called with a seed, the generator advances continuously " + "across different elements which can produce non-deterministic results. " + "It is important that rand32 is always called with a seed at least once " + "for deterministic results.") .get(); } -inline FunctionGroup::Ptr axsign(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axsign(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -839,7 +972,7 @@ inline FunctionGroup::Ptr axsign(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axsignbit(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axsignbit(const FunctionOptions& op) { return FunctionBuilder("signbit") .addSignature((bool(*)(double))(std::signbit)) @@ -862,7 +995,7 @@ inline FunctionGroup::Ptr axsignbit(const FunctionOptions& op) // Matrix math -inline FunctionGroup::Ptr axdeterminant(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axdeterminant(const FunctionOptions& op) { // 3 by 3 determinant static auto generate3 = @@ -954,7 +1087,7 @@ inline FunctionGroup::Ptr axdeterminant(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axdiag(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axdiag(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1068,7 +1201,7 @@ inline FunctionGroup::Ptr axdiag(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axidentity3(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axidentity3(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1096,7 +1229,7 @@ inline FunctionGroup::Ptr axidentity3(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axidentity4(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axidentity4(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1124,7 +1257,7 @@ inline FunctionGroup::Ptr axidentity4(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axmmmult(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axmmmult(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1185,7 +1318,7 @@ inline FunctionGroup::Ptr axmmmult(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axpolardecompose(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axpolardecompose(const FunctionOptions& op) { static auto polardecompose = [](auto in, auto orth, auto symm) -> bool { bool success = false; @@ -1221,7 +1354,7 @@ inline FunctionGroup::Ptr axpolardecompose(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axpostscale(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axpostscale(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1268,7 +1401,7 @@ inline FunctionGroup::Ptr axpostscale(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axpretransform(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axpretransform(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1337,7 +1470,7 @@ inline FunctionGroup::Ptr axpretransform(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axprescale(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axprescale(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1383,7 +1516,7 @@ inline FunctionGroup::Ptr axprescale(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axtrace(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axtrace(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1434,7 +1567,7 @@ inline FunctionGroup::Ptr axtrace(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axtransform(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axtransform(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1503,7 +1636,7 @@ inline FunctionGroup::Ptr axtransform(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axtranspose(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axtranspose(const FunctionOptions& op) { static auto generate = [](const std::vector& args, @@ -1558,7 +1691,7 @@ inline FunctionGroup::Ptr axtranspose(const FunctionOptions& op) // Noise -inline FunctionGroup::Ptr axsimplexnoise(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axsimplexnoise(const FunctionOptions& op) { static auto simplexnoisex = [](double x) -> double { return SimplexNoise::noise(x, 0.0, 0.0); @@ -1593,7 +1726,7 @@ inline FunctionGroup::Ptr axsimplexnoise(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axcurlsimplexnoise(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axcurlsimplexnoise(const FunctionOptions& op) { using CurlSimplexNoiseV3D = void(double(*)[3], const double(*)[3]); using CurlSimplexNoiseD = void(double(*)[3], double, double, double); @@ -1648,7 +1781,7 @@ DEFINE_AX_C_FP_BINDING(cosh, "Computes the hyperbolic cosine of the input.") DEFINE_AX_C_FP_BINDING(sinh, "Computes the hyperbolic sine of the input.") DEFINE_AX_C_FP_BINDING(tanh, "Computes the hyperbolic tangent of the input.") -inline FunctionGroup::Ptr axtan(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axtan(const FunctionOptions& op) { // @todo consider using this IR implementation over std::tan, however // we then lose constant folding (as results don't match). Ideally @@ -1686,7 +1819,7 @@ inline FunctionGroup::Ptr axtan(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axatan2(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axatan2(const FunctionOptions& op) { return FunctionBuilder("atan2") .addSignature((double(*)(double,double))(std::atan2)) @@ -1708,7 +1841,7 @@ inline FunctionGroup::Ptr axatan2(const FunctionOptions& op) // String -inline FunctionGroup::Ptr axatoi(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axatoi(const FunctionOptions& op) { // WARNING: decltype removes the throw identifer from atoi. We should // use this are automatically update the function attributes as appropriate @@ -1727,7 +1860,7 @@ inline FunctionGroup::Ptr axatoi(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axatof(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axatof(const FunctionOptions& op) { // WARNING: decltype removes the throw identifer from atof. We should // use this are automatically update the function attributes as appropriate @@ -1746,7 +1879,7 @@ inline FunctionGroup::Ptr axatof(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axhash(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axhash(const FunctionOptions& op) { static auto hash = [](const AXString* axstr) -> int64_t { const std::string str(axstr->ptr, axstr->size); @@ -1772,7 +1905,7 @@ inline FunctionGroup::Ptr axhash(const FunctionOptions& op) // Utility -inline FunctionGroup::Ptr axprint(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axprint(const FunctionOptions& op) { static auto print = [](auto v) { std::cout << v << std::endl; }; static auto printv = [](auto* v) { std::cout << *v << std::endl; }; @@ -1822,7 +1955,7 @@ inline FunctionGroup::Ptr axprint(const FunctionOptions& op) // Custom -inline FunctionGroup::Ptr ax_external(const FunctionOptions& op) +inline FunctionGroup::UniquePtr ax_external(const FunctionOptions& op) { static auto find = [](auto out, const void* const data, const AXString* const name) { @@ -1853,7 +1986,7 @@ inline FunctionGroup::Ptr ax_external(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axexternal(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axexternal(const FunctionOptions& op) { auto generate = [op](const std::vector& args, @@ -1892,7 +2025,7 @@ inline FunctionGroup::Ptr axexternal(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axexternalv(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axexternalv(const FunctionOptions& op) { auto generate = [op](const std::vector& args, @@ -1976,6 +2109,7 @@ void insertStandardFunctions(FunctionRegistry& registry, add("min", axmin); add("normalize", axnormalize); add("rand", axrand); + add("rand32", axrand32); add("sign", axsign); add("signbit", axsignbit); diff --git a/openvdb_ax/openvdb_ax/codegen/Types.cc b/openvdb_ax/openvdb_ax/codegen/Types.cc new file mode 100644 index 0000000000..edc7148e72 --- /dev/null +++ b/openvdb_ax/openvdb_ax/codegen/Types.cc @@ -0,0 +1,140 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +/// @file codegen/Types.cc +/// +/// @authors Nick Avramoussis +/// + +#include "Types.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace ax { +namespace codegen { + +/// @brief Returns an llvm IntegerType given a requested size and context +/// @param size The number of bits of the integer type +/// @param C The LLVMContext to request the Type from. +/// +llvm::IntegerType* +llvmIntType(const uint32_t size, llvm::LLVMContext& C) +{ + switch (size) { + case 1 : return llvm::cast(LLVMType::get(C)); + case 8 : return llvm::cast(LLVMType::get(C)); + case 16 : return llvm::cast(LLVMType::get(C)); + case 32 : return llvm::cast(LLVMType::get(C)); + case 64 : return llvm::cast(LLVMType::get(C)); + default : return llvm::Type::getIntNTy(C, size); + } +} + + +/// @brief Returns an llvm floating point Type given a requested size and context +/// @param size The size of the float to request, i.e. float - 32, double - 64 etc. +/// @param C The LLVMContext to request the Type from. +/// +llvm::Type* +llvmFloatType(const uint32_t size, llvm::LLVMContext& C) +{ + switch (size) { + case 32 : return LLVMType::get(C); + case 64 : return LLVMType::get(C); + default : OPENVDB_THROW(AXCodeGenError, + "Invalid float size requested from LLVM Context"); + } +} + +/// @brief Returns an llvm type representing a type defined by a string. +/// @note For string types, this function returns the element type, not the +/// object type! The llvm type representing a char block of memory +/// is LLVMType::get(C); +/// @param type The name of the type to request. +/// @param C The LLVMContext to request the Type from. +/// +llvm::Type* +llvmTypeFromToken(const ast::tokens::CoreType& type, + llvm::LLVMContext& C) +{ + switch (type) { + case ast::tokens::BOOL : return LLVMType::get(C); + case ast::tokens::INT16 : return LLVMType::get(C); + case ast::tokens::INT32 : return LLVMType::get(C); + case ast::tokens::INT64 : return LLVMType::get(C); + case ast::tokens::FLOAT : return LLVMType::get(C); + case ast::tokens::DOUBLE : return LLVMType::get(C); + case ast::tokens::VEC2I : return LLVMType::get(C); + case ast::tokens::VEC2F : return LLVMType::get(C); + case ast::tokens::VEC2D : return LLVMType::get(C); + case ast::tokens::VEC3I : return LLVMType::get(C); + case ast::tokens::VEC3F : return LLVMType::get(C); + case ast::tokens::VEC3D : return LLVMType::get(C); + case ast::tokens::VEC4I : return LLVMType::get(C); + case ast::tokens::VEC4F : return LLVMType::get(C); + case ast::tokens::VEC4D : return LLVMType::get(C); + case ast::tokens::MAT3F : return LLVMType::get(C); + case ast::tokens::MAT3D : return LLVMType::get(C); + case ast::tokens::MAT4F : return LLVMType::get(C); + case ast::tokens::MAT4D : return LLVMType::get(C); + case ast::tokens::STRING : return LLVMType::get(C); + case ast::tokens::UNKNOWN : + default : + OPENVDB_THROW(AXCodeGenError, + "Token type not recognised in request for LLVM type"); + } +} + +ast::tokens::CoreType +tokenFromLLVMType(const llvm::Type* type) +{ + if (type->isPointerTy()) { + type = type->getPointerElementType(); + } + if (type->isIntegerTy(1)) return ast::tokens::BOOL; + if (type->isIntegerTy(16)) return ast::tokens::INT16; + if (type->isIntegerTy(32)) return ast::tokens::INT32; + if (type->isIntegerTy(64)) return ast::tokens::INT64; + if (type->isFloatTy()) return ast::tokens::FLOAT; + if (type->isDoubleTy()) return ast::tokens::DOUBLE; + if (type->isArrayTy()) { + const ast::tokens::CoreType elementType = + tokenFromLLVMType(type->getArrayElementType()); + const size_t size = type->getArrayNumElements(); + if (size == 2) { + if (elementType == ast::tokens::INT32) return ast::tokens::VEC2I; + if (elementType == ast::tokens::FLOAT) return ast::tokens::VEC2F; + if (elementType == ast::tokens::DOUBLE) return ast::tokens::VEC2D; + } + else if (size == 3) { + if (elementType == ast::tokens::INT32) return ast::tokens::VEC3I; + if (elementType == ast::tokens::FLOAT) return ast::tokens::VEC3F; + if (elementType == ast::tokens::DOUBLE) return ast::tokens::VEC3D; + } + else if (size == 4) { + if (elementType == ast::tokens::INT32) return ast::tokens::VEC4I; + if (elementType == ast::tokens::FLOAT) return ast::tokens::VEC4F; + if (elementType == ast::tokens::DOUBLE) return ast::tokens::VEC4D; + } + else if (size == 9) { + if (elementType == ast::tokens::FLOAT) return ast::tokens::MAT3F; + if (elementType == ast::tokens::DOUBLE) return ast::tokens::MAT3D; + } + else if (size == 16) { + if (elementType == ast::tokens::FLOAT) return ast::tokens::MAT4F; + if (elementType == ast::tokens::DOUBLE) return ast::tokens::MAT4D; + } + } + if (type == LLVMType::get(type->getContext())) { + return ast::tokens::STRING; + } + return ast::tokens::UNKNOWN; +} + +} // namespace codegen +} // namespace ax +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + diff --git a/openvdb_ax/openvdb_ax/codegen/Types.h b/openvdb_ax/openvdb_ax/codegen/Types.h index 153d249349..3f6460421f 100644 --- a/openvdb_ax/openvdb_ax/codegen/Types.h +++ b/openvdb_ax/openvdb_ax/codegen/Types.h @@ -12,8 +12,8 @@ #define OPENVDB_AX_CODEGEN_TYPES_HAS_BEEN_INCLUDED #include "../ast/Tokens.h" -#include "../ast/Literals.h" #include "../Exceptions.h" +#include "../compiler/CustomData.h" // for AXString #include #include @@ -34,6 +34,12 @@ namespace OPENVDB_VERSION_NAME { namespace ax { namespace codegen { +template struct int_t; +template <> struct int_t<8> { using type = int8_t; }; +template <> struct int_t<16> { using type = int16_t; }; +template <> struct int_t<32> { using type = int32_t; }; +template <> struct int_t<64> { using type = int64_t; }; + /// @brief LLVM type mapping from pod types /// @note LLVM Types do not store information about the value sign, only meta /// information about the primitive type (i.e. float, int, pointer) and @@ -211,8 +217,8 @@ struct LLVMType } }; -/// @note void* implemented as signed int8_t* to match clang IR generation -template <> struct LLVMType : public LLVMType {}; +/// @note void* implemented as signed int_t* to match clang IR generation +template <> struct LLVMType : public LLVMType::type*> {}; template struct LLVMType : public LLVMType {}; template struct LLVMType : public LLVMType {}; @@ -305,119 +311,29 @@ struct FunctionTraits /// @param size The number of bits of the integer type /// @param C The LLVMContext to request the Type from. /// -inline llvm::IntegerType* -llvmIntType(const uint32_t size, llvm::LLVMContext& C) -{ - switch (size) { - case 1 : return llvm::cast(LLVMType::get(C)); - case 8 : return llvm::cast(LLVMType::get(C)); - case 16 : return llvm::cast(LLVMType::get(C)); - case 32 : return llvm::cast(LLVMType::get(C)); - case 64 : return llvm::cast(LLVMType::get(C)); - default : return llvm::Type::getIntNTy(C, size); - } -} - +llvm::IntegerType* llvmIntType(const uint32_t size, llvm::LLVMContext& C); /// @brief Returns an llvm floating point Type given a requested size and context /// @param size The size of the float to request, i.e. float - 32, double - 64 etc. /// @param C The LLVMContext to request the Type from. /// -inline llvm::Type* -llvmFloatType(const uint32_t size, llvm::LLVMContext& C) -{ - switch (size) { - case 32 : return LLVMType::get(C); - case 64 : return LLVMType::get(C); - default : OPENVDB_THROW(AXCodeGenError, - "Invalid float size requested from LLVM Context"); - } -} +llvm::Type* llvmFloatType(const uint32_t size, llvm::LLVMContext& C); /// @brief Returns an llvm type representing a type defined by a string. /// @note For string types, this function returns the element type, not the /// object type! The llvm type representing a char block of memory /// is LLVMType::get(C); -/// @param type The name of the type to request. +/// @param type The AX token type /// @param C The LLVMContext to request the Type from. /// -inline llvm::Type* -llvmTypeFromToken(const ast::tokens::CoreType& type, - llvm::LLVMContext& C) -{ - switch (type) { - case ast::tokens::BOOL : return LLVMType::get(C); - case ast::tokens::SHORT : return LLVMType::get(C); - case ast::tokens::INT : return LLVMType::get(C); - case ast::tokens::LONG : return LLVMType::get(C); - case ast::tokens::FLOAT : return LLVMType::get(C); - case ast::tokens::DOUBLE : return LLVMType::get(C); - case ast::tokens::VEC2I : return LLVMType::get(C); - case ast::tokens::VEC2F : return LLVMType::get(C); - case ast::tokens::VEC2D : return LLVMType::get(C); - case ast::tokens::VEC3I : return LLVMType::get(C); - case ast::tokens::VEC3F : return LLVMType::get(C); - case ast::tokens::VEC3D : return LLVMType::get(C); - case ast::tokens::VEC4I : return LLVMType::get(C); - case ast::tokens::VEC4F : return LLVMType::get(C); - case ast::tokens::VEC4D : return LLVMType::get(C); - case ast::tokens::MAT3F : return LLVMType::get(C); - case ast::tokens::MAT3D : return LLVMType::get(C); - case ast::tokens::MAT4F : return LLVMType::get(C); - case ast::tokens::MAT4D : return LLVMType::get(C); - case ast::tokens::STRING : return LLVMType::get(C); - case ast::tokens::UNKNOWN : - default : - OPENVDB_THROW(AXCodeGenError, - "Token type not recognised in request for LLVM type"); - } -} +llvm::Type* llvmTypeFromToken(const ast::tokens::CoreType& type, llvm::LLVMContext& C); -inline ast::tokens::CoreType -tokenFromLLVMType(const llvm::Type* type) -{ - if (type->isPointerTy()) { - type = type->getPointerElementType(); - } - if (type->isIntegerTy(1)) return ast::tokens::BOOL; - if (type->isIntegerTy(16)) return ast::tokens::SHORT; - if (type->isIntegerTy(32)) return ast::tokens::INT; - if (type->isIntegerTy(64)) return ast::tokens::LONG; - if (type->isFloatTy()) return ast::tokens::FLOAT; - if (type->isDoubleTy()) return ast::tokens::DOUBLE; - if (type->isArrayTy()) { - const ast::tokens::CoreType elementType = - tokenFromLLVMType(type->getArrayElementType()); - const size_t size = type->getArrayNumElements(); - if (size == 2) { - if (elementType == ast::tokens::INT) return ast::tokens::VEC2I; - if (elementType == ast::tokens::FLOAT) return ast::tokens::VEC2F; - if (elementType == ast::tokens::DOUBLE) return ast::tokens::VEC2D; - } - else if (size == 3) { - if (elementType == ast::tokens::INT) return ast::tokens::VEC3I; - if (elementType == ast::tokens::FLOAT) return ast::tokens::VEC3F; - if (elementType == ast::tokens::DOUBLE) return ast::tokens::VEC3D; - } - else if (size == 4) { - if (elementType == ast::tokens::INT) return ast::tokens::VEC4I; - if (elementType == ast::tokens::FLOAT) return ast::tokens::VEC4F; - if (elementType == ast::tokens::DOUBLE) return ast::tokens::VEC4D; - } - else if (size == 9) { - if (elementType == ast::tokens::FLOAT) return ast::tokens::MAT3F; - if (elementType == ast::tokens::DOUBLE) return ast::tokens::MAT3D; - } - else if (size == 16) { - if (elementType == ast::tokens::FLOAT) return ast::tokens::MAT4F; - if (elementType == ast::tokens::DOUBLE) return ast::tokens::MAT4D; - } - } - if (type == LLVMType::get(type->getContext())) { - return ast::tokens::STRING; - } - return ast::tokens::UNKNOWN; -} +/// @brief Return a corresponding AX token which represents the given LLVM Type. +/// @note If the type does not exist in AX, ast::tokens::UNKNOWN is returned. +/// Must not be a nullptr. +/// @param type a valid LLVM Type +/// +ast::tokens::CoreType tokenFromLLVMType(const llvm::Type* type); } // namespace codegen } // namespace ax diff --git a/openvdb_ax/openvdb_ax/codegen/Utils.h b/openvdb_ax/openvdb_ax/codegen/Utils.h index 28c269768b..824759f357 100644 --- a/openvdb_ax/openvdb_ax/codegen/Utils.h +++ b/openvdb_ax/openvdb_ax/codegen/Utils.h @@ -193,13 +193,8 @@ typePrecedence(llvm::Type* const typeA, if (typeA->isIntegerTy(1)) return typeA; if (typeB->isIntegerTy(1)) return typeB; - std::cerr << "Attempted to compare "; - typeA->print(llvm::errs()); - std::cerr << " to "; - typeB->print(llvm::errs()); - std::cerr << std::endl; - - OPENVDB_THROW(AXCodeGenError, "Invalid LLVM type precedence"); + assert(false && "invalid LLVM type precedence"); + return nullptr; } /// @brief Returns a CastFunction which represents the corresponding instruction @@ -282,14 +277,8 @@ llvmArithmeticConversion(const llvm::Type* const sourceType, } #undef BIND_ARITHMETIC_CAST_OP - - std::cerr << "Attempted to convert "; - sourceType->print(llvm::errs()); - std::cerr << " to "; - targetType->print(llvm::errs()); - std::cerr << std::endl; - - OPENVDB_THROW(AXCodeGenError, "Invalid LLVM type conversion"); + assert(false && "invalid LLVM type conversion"); + return CastFunction(); } /// @brief Returns a BinaryFunction representing the corresponding instruction to @@ -320,11 +309,9 @@ llvmBinaryConversion(const llvm::Type* const type, // See http://stackoverflow.com/questions/5346160/llvm-irbuildercreateudiv-createsdiv-createexactudiv if (type->isFloatingPointTy()) { - const ast::tokens::OperatorType opType = ast::tokens::operatorType(token); - if (opType == ast::tokens::LOGICAL || opType == ast::tokens::BITWISE) { - OPENVDB_THROW(AXCodeGenError, "Unable to perform operation \"" - + ast::tokens::operatorNameFromToken(token) + "\" on floating points values"); - } + assert(!(ast::tokens::operatorType(token) == ast::tokens::LOGICAL || + ast::tokens::operatorType(token) == ast::tokens::BITWISE) + && "unable to perform logical or bitwise operation on floating point values"); if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateFAdd); else if (token == ast::tokens::MINUS) return BIND_BINARY_OP(CreateFSub); @@ -337,8 +324,7 @@ llvmBinaryConversion(const llvm::Type* const type, else if (token == ast::tokens::LESSTHAN) return BIND_BINARY_OP(CreateFCmpOLT); else if (token == ast::tokens::MORETHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOGE); else if (token == ast::tokens::LESSTHANOREQUAL) return BIND_BINARY_OP(CreateFCmpOLE); - OPENVDB_THROW(AXCodeGenError, "Unrecognised binary operator \"" + - ast::tokens::operatorNameFromToken(token) + "\""); + assert(false && "unrecognised binary operator"); } else if (type->isIntegerTy()) { if (token == ast::tokens::PLUS) return BIND_BINARY_OP(CreateAdd); // No Unsigned/Signed Wrap @@ -359,19 +345,12 @@ llvmBinaryConversion(const llvm::Type* const type, else if (token == ast::tokens::BITAND) return BIND_BINARY_OP(CreateAnd); else if (token == ast::tokens::BITOR) return BIND_BINARY_OP(CreateOr); else if (token == ast::tokens::BITXOR) return BIND_BINARY_OP(CreateXor); - OPENVDB_THROW(AXCodeGenError, "Unrecognised binary operator \"" + - ast::tokens::operatorNameFromToken(token) + "\""); + assert(false && "unrecognised binary operator"); } #undef BIND_BINARY_OP - - std::cerr << "Attempted to generate a binary operator \"" - << ast::tokens::operatorNameFromToken(token) << "\"" - << "with type "; - - type->print(llvm::errs()); - - OPENVDB_THROW(AXCodeGenError, "Invalid LLVM type for binary operation"); + assert(false && "invalid LLVM type for binary operation"); + return BinaryFunction(); } /// @brief Returns true if the llvm Type 'from' can be safely cast to the llvm @@ -555,16 +534,12 @@ boolComparison(llvm::Value* value, if (type->isFloatingPointTy()) return builder.CreateFCmpONE(value, llvm::ConstantFP::get(type, 0.0)); else if (type->isIntegerTy(1)) return builder.CreateICmpNE(value, llvm::ConstantInt::get(type, 0)); else if (type->isIntegerTy()) return builder.CreateICmpNE(value, llvm::ConstantInt::getSigned(type, 0)); - - std::cerr << "Attempted to convert "; - type->print(llvm::errs()); - std::cerr << std::endl; - - OPENVDB_THROW(AXCodeGenError, "Invalid type for bool conversion"); + assert(false && "Invalid type for bool conversion"); + return nullptr; } -/// @ brief Performs a binary operation on two loaded llvm scalar values. The type of -/// operation performed is defined by the token (see the list of supported +/// @ brief Performs a binary operation on two loaded llvm scalar values of the same type. +/// The type of operation performed is defined by the token (see the list of supported /// tokens in ast/Tokens.h. Returns a loaded llvm scalar result /// /// @param lhs The left hand side value of the binary operation @@ -577,31 +552,17 @@ boolComparison(llvm::Value* value, inline llvm::Value* binaryOperator(llvm::Value* lhs, llvm::Value* rhs, const ast::tokens::OperatorToken& token, - llvm::IRBuilder<>& builder, - std::string* warnings = nullptr) + llvm::IRBuilder<>& builder) { llvm::Type* lhsType = lhs->getType(); - if (lhsType != rhs->getType()) { - std::string error; - llvm::raw_string_ostream os(error); - os << "LHS Type: "; lhsType->print(os); os << ", "; - os << "RHS Type: "; rhs->getType()->print(os); os << " "; - OPENVDB_THROW(AXCodeGenError, "Mismatching argument types for binary operation \"" + - ast::tokens::operatorNameFromToken(token) + "\". " + os.str()); - } + assert(lhsType == rhs->getType()); const ast::tokens::OperatorType opType = ast::tokens::operatorType(token); if (opType == ast::tokens::LOGICAL) { lhs = boolComparison(lhs, builder); rhs = boolComparison(rhs, builder); - lhsType = lhs->getType(); - } - else if (opType == ast::tokens::BITWISE && lhsType->isFloatingPointTy()) { - rhs = arithmeticConversion(rhs, LLVMType::get(builder.getContext()), builder); - lhs = arithmeticConversion(lhs, LLVMType::get(builder.getContext()), builder); - lhsType = lhs->getType(); - if (warnings) *warnings = std::string("Implicit cast from float to int."); + lhsType = lhs->getType(); // now bool type } const BinaryFunction llvmBinaryFunction = llvmBinaryConversion(lhsType, token); diff --git a/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.cc b/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.cc index d9b302fa63..44815f1630 100644 --- a/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.cc +++ b/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.cc @@ -41,6 +41,7 @@ std::string VolumeKernel::getDefaultName() { return "ax.compute.voxel"; } /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// +namespace codegen_internal { VolumeComputeGenerator::VolumeComputeGenerator(llvm::Module& module, const FunctionOptions& options, @@ -180,7 +181,7 @@ AttributeRegistry::Ptr VolumeComputeGenerator::generate(const ast::Tree& tree) value = mBuilder.CreateLoad(value); } - const FunctionGroup::Ptr function = this->getFunction("setvoxel", true); + const FunctionGroup* const function = this->getFunction("setvoxel", true); function->execute({accessor, coordis, value}, mBuilder); mBuilder.CreateBr(continueBlock); @@ -234,7 +235,7 @@ void VolumeComputeGenerator::getAccessorValue(const std::string& globalName, llv llvm::Value* accessor = mBuilder.CreateLoad(accessorPtr); llvm::Value* transform = mBuilder.CreateLoad(transformPtr); - const FunctionGroup::Ptr function = this->getFunction("getvoxel", true); + const FunctionGroup* const function = this->getFunction("getvoxel", true); function->execute({accessor, transform, coordws, location}, mBuilder); } @@ -263,6 +264,7 @@ llvm::Value* VolumeComputeGenerator::accessorHandleFromToken(const std::string& /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// +} // namespace codegen_internal } // namespace codegen } // namespace ax diff --git a/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.h b/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.h index 0b2884fa4f..e994f7f284 100644 --- a/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.h +++ b/openvdb_ax/openvdb_ax/codegen/VolumeComputeGenerator.h @@ -64,6 +64,8 @@ struct VolumeKernel /////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////// +namespace codegen_internal { + /// @brief Visitor object which will generate llvm IR for a syntax tree which has been generated /// from AX that targets volumes. The IR will represent a single function. It is mainly /// used by the Compiler class. @@ -93,6 +95,8 @@ struct VolumeComputeGenerator : public ComputeGenerator void getAccessorValue(const std::string&, llvm::Value*); }; +} // namespace codegen_internal + } // namespace codegen } // namespace ax } // namespace OPENVDB_VERSION_NAME diff --git a/openvdb_ax/openvdb_ax/codegen/VolumeFunctions.cc b/openvdb_ax/openvdb_ax/codegen/VolumeFunctions.cc index 16cc3372fe..8e56a59332 100644 --- a/openvdb_ax/openvdb_ax/codegen/VolumeFunctions.cc +++ b/openvdb_ax/openvdb_ax/codegen/VolumeFunctions.cc @@ -46,7 +46,7 @@ inline void verifyContext(const llvm::Function* const F, const std::string& name } -inline FunctionGroup::Ptr axgetvoxelpws(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axgetvoxelpws(const FunctionOptions& op) { static auto generate = [](const std::vector&, llvm::IRBuilder<>& B) -> llvm::Value* @@ -69,7 +69,7 @@ inline FunctionGroup::Ptr axgetvoxelpws(const FunctionOptions& op) } template -inline FunctionGroup::Ptr axgetcoord(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axgetcoord(const FunctionOptions& op) { static_assert(Index <= 2, "Invalid index for axgetcoord"); @@ -96,7 +96,7 @@ inline FunctionGroup::Ptr axgetcoord(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axsetvoxel(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axsetvoxel(const FunctionOptions& op) { static auto setvoxelptr = [](void* accessor, @@ -229,7 +229,7 @@ inline FunctionGroup::Ptr axsetvoxel(const FunctionOptions& op) .get(); } -inline FunctionGroup::Ptr axgetvoxel(const FunctionOptions& op) +inline FunctionGroup::UniquePtr axgetvoxel(const FunctionOptions& op) { static auto getvoxel = [](void* accessor, diff --git a/openvdb_ax/openvdb_ax/compiler/Compiler.cc b/openvdb_ax/openvdb_ax/compiler/Compiler.cc index 61feb5d097..37b458a7de 100644 --- a/openvdb_ax/openvdb_ax/compiler/Compiler.cc +++ b/openvdb_ax/openvdb_ax/compiler/Compiler.cc @@ -362,7 +362,7 @@ void initializeGlobalFunctions(const codegen::FunctionRegistry& registry, /// @note Depending on how functions are inserted into LLVM (Linkage Type) in /// the future, InstallLazyFunctionCreator may be required for (const auto& iter : registry.map()) { - const codegen::FunctionGroup::Ptr function = iter.second.function(); + const codegen::FunctionGroup* const function = iter.second.function(); if (!function) continue; const codegen::FunctionGroup::FunctionList& list = function->list(); @@ -522,9 +522,8 @@ registerExternalGlobals(const codegen::SymbolTable& globals, CustomData::Ptr& da [&](const ast::tokens::CoreType type, const std::string& name, CustomData& data) -> llvm::Constant* { switch (type) { case ast::tokens::BOOL : return initializeMetadataPtr(data, name, C); - case ast::tokens::SHORT : return initializeMetadataPtr(data, name, C); - case ast::tokens::INT : return initializeMetadataPtr(data, name, C); - case ast::tokens::LONG : return initializeMetadataPtr(data, name, C); + case ast::tokens::INT32 : return initializeMetadataPtr(data, name, C); + case ast::tokens::INT64 : return initializeMetadataPtr(data, name, C); case ast::tokens::FLOAT : return initializeMetadataPtr(data, name, C); case ast::tokens::DOUBLE : return initializeMetadataPtr(data, name, C); case ast::tokens::VEC2I : return initializeMetadataPtr>(data, name, C); @@ -651,7 +650,7 @@ Compiler::compile(const ast::Tree& syntaxTree, module->setTargetTriple(TM->getTargetTriple().normalize()); } - codegen::PointComputeGenerator + codegen::codegen_internal::PointComputeGenerator codeGenerator(*module, mCompilerOptions.mFunctionOptions, *mFunctionRegistry, logger); AttributeRegistry::Ptr attributes = codeGenerator.generate(*tree); @@ -747,7 +746,7 @@ Compiler::compile(const ast::Tree& syntaxTree, module->setTargetTriple(TM->getTargetTriple().normalize()); } - codegen::VolumeComputeGenerator + codegen::codegen_internal::VolumeComputeGenerator codeGenerator(*module, mCompilerOptions.mFunctionOptions, *mFunctionRegistry, logger); AttributeRegistry::Ptr attributes = codeGenerator.generate(syntaxTree); diff --git a/openvdb_ax/openvdb_ax/compiler/CustomData.h b/openvdb_ax/openvdb_ax/compiler/CustomData.h index b39a1771cf..e7d21adb5e 100644 --- a/openvdb_ax/openvdb_ax/compiler/CustomData.h +++ b/openvdb_ax/openvdb_ax/compiler/CustomData.h @@ -12,8 +12,6 @@ #ifndef OPENVDB_AX_COMPILER_CUSTOM_DATA_HAS_BEEN_INCLUDED #define OPENVDB_AX_COMPILER_CUSTOM_DATA_HAS_BEEN_INCLUDED -#include "../ast/Literals.h" - #include #include #include @@ -27,6 +25,16 @@ namespace OPENVDB_VERSION_NAME { namespace ax { +/// @brief The backend representation of strings in AX. This is also how +/// strings are passed from the AX code generation to functions. +struct AXString +{ + // usually size_t. Used to match the implementation of std:string + using SizeType = std::allocator::size_type; + const char* ptr = nullptr; + SizeType size = 0; +}; + /// @brief The custom data class is a simple container for named openvdb metadata. Its primary use /// case is passing arbitrary "external" data to an AX executable object when calling /// Compiler::compile. For example, it is the mechanism by which we pass data held inside of a diff --git a/openvdb_ax/openvdb_ax/compiler/Logger.h b/openvdb_ax/openvdb_ax/compiler/Logger.h index f4bcf868c1..cd93236b28 100644 --- a/openvdb_ax/openvdb_ax/compiler/Logger.h +++ b/openvdb_ax/openvdb_ax/compiler/Logger.h @@ -1,7 +1,7 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -/// @file codegen/Logger.h +/// @file compiler/Logger.h /// /// @authors Richard Jones /// diff --git a/openvdb_ax/openvdb_ax/compiler/PointExecutable.cc b/openvdb_ax/openvdb_ax/compiler/PointExecutable.cc index cadcb445b3..d7216bee5b 100644 --- a/openvdb_ax/openvdb_ax/compiler/PointExecutable.cc +++ b/openvdb_ax/openvdb_ax/compiler/PointExecutable.cc @@ -4,12 +4,12 @@ /// @file compiler/PointExecutable.cc #include "PointExecutable.h" -#include "LeafLocalData.h" #include "../Exceptions.h" // @TODO refactor so we don't have to include PointComputeGenerator.h, // but still have the functions defined in one place #include "../codegen/PointComputeGenerator.h" +#include "../codegen/PointLeafLocalData.h" #include @@ -41,6 +41,7 @@ namespace { using KernelFunctionPtr = std::add_pointer::type; using FunctionTraitsT = codegen::PointKernel::FunctionTraitsT; using ReturnT = FunctionTraitsT::ReturnType; +using PointLeafLocalData = codegen::codegen_internal::PointLeafLocalData; /// @brief The arguments of the generated function /// @@ -90,7 +91,7 @@ struct PointFunctionArguments PointFunctionArguments(const KernelFunctionPtr function, const CustomData* const customData, const points::AttributeSet& attributeSet, - compiler::LeafLocalData* const leafLocalData) + PointLeafLocalData* const leafLocalData) : mFunction(function) , mCustomData(customData) , mAttributeSet(&attributeSet) @@ -162,7 +163,7 @@ struct PointFunctionArguments #else std::vector> mGroupHandles; #endif - compiler::LeafLocalData* const mLeafLocalData; + PointLeafLocalData* const mLeafLocalData; }; @@ -227,9 +228,9 @@ inline bool supported(const ast::tokens::CoreType type) switch (type) { case ast::tokens::BOOL : return true; case ast::tokens::CHAR : return true; - case ast::tokens::SHORT : return true; - case ast::tokens::INT : return true; - case ast::tokens::LONG : return true; + case ast::tokens::INT16 : return true; + case ast::tokens::INT32 : return true; + case ast::tokens::INT64 : return true; case ast::tokens::FLOAT : return true; case ast::tokens::DOUBLE : return true; case ast::tokens::VEC2I : return true; @@ -264,9 +265,9 @@ addAttributeHandle(PointFunctionArguments& args, switch (type) { case ast::tokens::BOOL : return addAttributeHandleTyped(args, leaf, name, write); case ast::tokens::CHAR : return addAttributeHandleTyped(args, leaf, name, write); - case ast::tokens::SHORT : return addAttributeHandleTyped(args, leaf, name, write); - case ast::tokens::INT : return addAttributeHandleTyped(args, leaf, name, write); - case ast::tokens::LONG : return addAttributeHandleTyped(args, leaf, name, write); + case ast::tokens::INT16 : return addAttributeHandleTyped(args, leaf, name, write); + case ast::tokens::INT32 : return addAttributeHandleTyped(args, leaf, name, write); + case ast::tokens::INT64 : return addAttributeHandleTyped(args, leaf, name, write); case ast::tokens::FLOAT : return addAttributeHandleTyped(args, leaf, name, write); case ast::tokens::DOUBLE : return addAttributeHandleTyped(args, leaf, name, write); case ast::tokens::VEC2I : return addAttributeHandleTyped>(args, leaf, name, write); @@ -303,7 +304,7 @@ struct PointExecuterOp const KernelFunctionPtr computeFunction, const math::Transform& transform, const GroupIndex& groupIndex, - std::vector& leafLocalData, + std::vector& leafLocalData, const std::string& positionAttribute, const std::pair& positionAccess) : mAttributeRegistry(attributeRegistry) @@ -338,7 +339,7 @@ struct PointExecuterOp const size_t count = leaf.getLastValue(); const points::AttributeSet& set = leaf.attributeSet(); auto& leafLocalData = mLeafLocalData[idx]; - leafLocalData.reset(new compiler::LeafLocalData(count)); + leafLocalData.reset(new PointLeafLocalData(count)); PointFunctionArguments args(mComputeFunction, mCustomData, set, leafLocalData.get()); @@ -429,7 +430,7 @@ struct PointExecuterOp const KernelFunctionPtr mComputeFunction; const math::Transform& mTransform; const GroupIndex& mGroupIndex; - std::vector& mLeafLocalData; + std::vector& mLeafLocalData; const std::string& mPositionAttribute; const std::pair& mPositionAccess; }; @@ -442,9 +443,9 @@ void appendMissingAttributes(points::PointDataGrid& grid, switch (type) { case ast::tokens::BOOL : return points::TypedAttributeArray::attributeType(); case ast::tokens::CHAR : return points::TypedAttributeArray::attributeType(); - case ast::tokens::SHORT : return points::TypedAttributeArray::attributeType(); - case ast::tokens::INT : return points::TypedAttributeArray::attributeType(); - case ast::tokens::LONG : return points::TypedAttributeArray::attributeType(); + case ast::tokens::INT16 : return points::TypedAttributeArray::attributeType(); + case ast::tokens::INT32 : return points::TypedAttributeArray::attributeType(); + case ast::tokens::INT64 : return points::TypedAttributeArray::attributeType(); case ast::tokens::FLOAT : return points::TypedAttributeArray::attributeType(); case ast::tokens::DOUBLE : return points::TypedAttributeArray::attributeType(); case ast::tokens::VEC2I : return points::TypedAttributeArray>::attributeType(); @@ -592,7 +593,7 @@ void PointExecutable::execute(openvdb::points::PointDataGrid& grid) const const math::Transform& transform = grid.transform(); LeafManagerT leafManager(grid.tree()); - std::vector leafLocalData(leafManager.leafCount()); + std::vector leafLocalData(leafManager.leafCount()); const bool threaded = mSettings->mGrainSize > 0; PointExecuterOp executerOp(*mAttributeRegistry, @@ -627,7 +628,7 @@ void PointExecutable::execute(openvdb::points::PointDataGrid& grid) const leafManager.foreach( [&groups, &leafLocalData, newStrings] (auto& leaf, size_t idx) { - compiler::LeafLocalData::UniquePtr& data = leafLocalData[idx]; + PointLeafLocalData::UniquePtr& data = leafLocalData[idx]; for (const auto& name : groups) { @@ -653,7 +654,7 @@ void PointExecutable::execute(openvdb::points::PointDataGrid& grid) const if (newStrings) { const MetaMap& metadata = leaf.attributeSet().descriptor().getMetadata(); - const compiler::LeafLocalData::StringArrayMap& stringArrayMap = data->getStringArrayMap(); + const PointLeafLocalData::StringArrayMap& stringArrayMap = data->getStringArrayMap(); for (const auto& arrayIter : stringArrayMap) { points::StringAttributeWriteHandle::Ptr handle = diff --git a/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.cc b/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.cc index 08b80abbd6..9b236dd354 100644 --- a/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.cc +++ b/openvdb_ax/openvdb_ax/compiler/VolumeExecutable.cc @@ -171,9 +171,9 @@ inline bool supported(const ast::tokens::CoreType type) { switch (type) { case ast::tokens::BOOL : return true; - case ast::tokens::SHORT : return true; - case ast::tokens::INT : return true; - case ast::tokens::LONG : return true; + case ast::tokens::INT16 : return true; + case ast::tokens::INT32 : return true; + case ast::tokens::INT64 : return true; case ast::tokens::FLOAT : return true; case ast::tokens::DOUBLE : return true; case ast::tokens::VEC2I : return true; @@ -204,9 +204,9 @@ retrieveAccessor(VolumeFunctionArguments& args, assert(supported(type) && "Could not retrieve accessor from unsupported type"); switch (type) { case ast::tokens::BOOL : { args.addAccessor(static_cast*>(grid)->tree()); return; } - case ast::tokens::SHORT : { args.addAccessor(static_cast*>(grid)->tree()); return; } - case ast::tokens::INT : { args.addAccessor(static_cast*>(grid)->tree()); return; } - case ast::tokens::LONG : { args.addAccessor(static_cast*>(grid)->tree()); return; } + case ast::tokens::INT16 : { args.addAccessor(static_cast*>(grid)->tree()); return; } + case ast::tokens::INT32 : { args.addAccessor(static_cast*>(grid)->tree()); return; } + case ast::tokens::INT64 : { args.addAccessor(static_cast*>(grid)->tree()); return; } case ast::tokens::FLOAT : { args.addAccessor(static_cast*>(grid)->tree()); return; } case ast::tokens::DOUBLE : { args.addAccessor(static_cast*>(grid)->tree()); return; } case ast::tokens::VEC2D : { args.addAccessor(static_cast>*>(grid)->tree()); return; } @@ -235,9 +235,9 @@ createGrid(const ast::tokens::CoreType& type) assert(supported(type) && "Could not retrieve accessor from unsupported type"); switch (type) { case ast::tokens::BOOL : return ConverterT::create(); - case ast::tokens::SHORT : return ConverterT::create(); - case ast::tokens::INT : return ConverterT::create(); - case ast::tokens::LONG : return ConverterT::create(); + case ast::tokens::INT16 : return ConverterT::create(); + case ast::tokens::INT32 : return ConverterT::create(); + case ast::tokens::INT64 : return ConverterT::create(); case ast::tokens::FLOAT : return ConverterT::create(); case ast::tokens::DOUBLE : return ConverterT::create(); case ast::tokens::VEC2D : return ConverterT>::create(); diff --git a/openvdb_ax/openvdb_ax/grammar/axlexer.l b/openvdb_ax/openvdb_ax/grammar/axlexer.l index 62cd5e592d..8f3cc4c76a 100644 --- a/openvdb_ax/openvdb_ax/grammar/axlexer.l +++ b/openvdb_ax/openvdb_ax/grammar/axlexer.l @@ -10,11 +10,12 @@ SPDX-License-Identifier: MPL-2.0 */ %{ - #include "openvdb_ax/Exceptions.h" #include "openvdb_ax/ast/Parse.h" + #include "openvdb_ax/compiler/Logger.h" #include "axparser.h" /*generated by bison*/ #include - #include + #include + #include /// @note Bypasses conversion warnings in YY_CURRENT_BUFFER macro. /// This is a bit over zealous as we only need to suppress @@ -25,10 +26,10 @@ SPDX-License-Identifier: MPL-2.0 /// @note Location tracking macro for axlloc token locations. /// YY_USER_ACTION is called before any and each lexer action - /// is performed. Instead of manually tracking newlines, we /// can simply scan for them in the current text held by axtext #define YY_USER_ACTION \ + assert(axlog); \ axlloc.first_line = axlloc.last_line; \ axlloc.first_column = axlloc.last_column; \ for (int i = 0; axtext[i] != '\0'; i++) { \ @@ -140,6 +141,7 @@ COMMENT "//".* "f@" { return F_AT; } "i@" { return I_AT; } "s@" { return S_AT; } +"int16@" { return I16_AT; } "v$" { return V_DOLLAR; } "f$" { return F_DOLLAR; } @@ -158,9 +160,9 @@ COMMENT "//".* "continue" { return CONTINUE;} "bool" { return BOOL; } -"short" { return SHORT; } -"int" { return INT; } -"long" { return LONG; } +"int" { return INT32; } +"int32" { return INT32; } +"int64" { return INT64; } "float" { return FLOAT; } "double" { return DOUBLE; } "string" { return STRING; } @@ -188,21 +190,38 @@ COMMENT "//".* "4@" { return M4F_AT; } /*Deprecated Tokens*/ -"vectorint" { return VEC3I; } -"vectorfloat" { return VEC3F; } -"vectordouble" { return VEC3D; } +"vectorint" { axlog->warning("vectorint keyword is deprecated. use vec3i.", + {axlloc.first_line, axlloc.first_column}); + return VEC3I; + } +"vectorfloat" { axlog->warning("vectorfloat keyword is deprecated. use vec3f.", + {axlloc.first_line, axlloc.first_column}); + return VEC3F; + } +"vectordouble" { axlog->warning("vectordouble keyword is deprecated. use vec3d.", + {axlloc.first_line, axlloc.first_column}); + return VEC3D; + } +"short" { axlog->warning("short local variables have been removed. use int, int32 or int64.", + {axlloc.first_line, axlloc.first_column}); + return INT32; + } +"long" { axlog->warning("long keyword is deprecated. use int64.", + {axlloc.first_line, axlloc.first_column}); + return INT64; + } /* Reserved keywords */ "case"|"char"|"class"|"const"|"def"|"default" | \ -"enum"|"extern"|"friend"|"function"|"inline" | \ +"enum"|"extern"|"friend"|"function"|"inline"|"int16" | \ "private"|"protected"|"signed"|"sizeof"|"static" | \ "struct"|"switch"|"template"|"this"|"typedef" | \ "uniform"|"union"|"unsigned"|"until"|"virtual" | \ -"void" { +"void"|"byte"|"flt"|"flt16"|"flt32"|"flt64"|"int8" | \ +"half" { /* @todo: move this into parser */ std::ostringstream os; os <<"\""<< axtext << "\" is a reserved keyword."; - assert(axlog); axlog->error(os.str(), {axlloc.first_line, axlloc.first_column}); } @@ -216,31 +235,83 @@ COMMENT "//".* } {DIGIT}+s { - axlval.string = strdup(axtext); - return L_SHORT; + axlog->warning("s suffix is deprecated.", {axlloc.first_line, axlloc.first_column}); + errno = 0; + axlval.index = uint64_t(std::strtoull(axtext, /*end*/nullptr, /*base*/10)); + if (errno == ERANGE) { + errno = 0; + axlog->error("integer constant is too large to be represented:", + {axlloc.first_line, axlloc.first_column}); + } + return L_INT32; } {DIGIT}+ { - axlval.string = strdup(axtext); - return L_INT; + errno = 0; + axlval.index = uint64_t(std::strtoull(axtext, /*end*/nullptr, /*base*/10)); + if (errno == ERANGE) { + errno = 0; + axlog->error("integer constant is too large to be represented:", + {axlloc.first_line, axlloc.first_column}); + } + + return L_INT32; } {DIGIT}+l { - axlval.string = strdup(axtext); - return L_LONG; + errno = 0; + axlval.index = uint64_t(std::strtoull(axtext, /*end*/nullptr, /*base*/10)); + if (errno == ERANGE) { + errno = 0; + axlog->error("integer constant is too large to be represented:", + {axlloc.first_line, axlloc.first_column}); + } + return L_INT64; } "."{DIGIT}+f | {DIGIT}+"."{DIGIT}*f | {DIGIT}+("."{DIGIT}+)?{E}+f { - axlval.string = strdup(axtext); + errno = 0; + axlval.flt = static_cast(std::strtof(axtext, /*end*/nullptr)); + if (errno == ERANGE) { + errno = 0; + if (std::isinf(axlval.flt)) { + axlog->warning("floating point constant is too large for type float, " + "will be converted to inf.", {axlloc.first_line, axlloc.first_column}); + } + else if (axlval.flt == 0.0) { + axlog->warning("floating point constant truncated to zero.", + {axlloc.first_line, axlloc.first_column}); + } + else { + axlog->warning("floating point constant is too small for type float " + "and may underflow.", {axlloc.first_line, axlloc.first_column}); + } + } return L_FLOAT; } "."{DIGIT}+ | {DIGIT}+"."{DIGIT}* | {DIGIT}+("."{DIGIT}+)?{E}+ { - axlval.string = strdup(axtext); + errno = 0; + axlval.flt = std::strtod(axtext, /*end*/nullptr); + if (errno == ERANGE) { + errno = 0; + if (std::isinf(axlval.flt)) { + axlog->warning("floating point constant is too large for type double, " + "will be converted to inf.", {axlloc.first_line, axlloc.first_column}); + } + else if (axlval.flt == 0.0) { + axlog->warning("floating point constant truncated to zero.", + {axlloc.first_line, axlloc.first_column}); + } + else { + axlog->warning("floating point constant is too small for type double " + "and may underflow.", {axlloc.first_line, axlloc.first_column}); + } + } return L_DOUBLE; } diff --git a/openvdb_ax/openvdb_ax/grammar/axparser.y b/openvdb_ax/openvdb_ax/grammar/axparser.y index de448dfb67..62684fcfb9 100644 --- a/openvdb_ax/openvdb_ax/grammar/axparser.y +++ b/openvdb_ax/openvdb_ax/grammar/axparser.y @@ -9,16 +9,12 @@ /// %code top { - #include - #include - - #include // for OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN - #include "openvdb_ax/ast/AST.h" #include "openvdb_ax/ast/Parse.h" #include "openvdb_ax/ast/Tokens.h" - #include "openvdb_ax/compiler/Logger.h" + #include // for OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + #include /// @note Bypasses bison conversion warnings in yyparse OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN @@ -63,6 +59,7 @@ const char* string; uint64_t index; + double flt; openvdb::ax::ast::Tree* tree; openvdb::ax::ast::ValueBase* value; @@ -82,10 +79,10 @@ } -%code { - +%code +{ template - T* newNode(YYLTYPE* loc, const Args&... args) { + T* newNode(AXLTYPE* loc, const Args&... args) { T* ptr = new T(args...); assert(axlog); axlog->addNodeLocation(ptr, {loc->first_line, loc->first_column}); @@ -103,19 +100,16 @@ %token RETURN BREAK CONTINUE %token LCURLY RCURLY %token LSQUARE RSQUARE -%token STRING DOUBLE FLOAT LONG INT SHORT BOOL VOID +%token STRING DOUBLE FLOAT INT32 INT64 BOOL %token VEC2I VEC2F VEC2D VEC3I VEC3F VEC3D VEC4I VEC4F VEC4D -%token F_AT I_AT V_AT S_AT +%token F_AT I_AT V_AT S_AT I16_AT %token MAT3F MAT3D MAT4F MAT4D M3F_AT M4F_AT %token F_DOLLAR I_DOLLAR V_DOLLAR S_DOLLAR %token DOT_X DOT_Y DOT_Z -%token L_SHORT -%token L_INT -%token L_LONG -%token L_FLOAT -%token L_DOUBLE -%token L_STRING -%token IDENTIFIER +%token L_INT32 L_INT64 +%token L_FLOAT +%token L_DOUBLE +%token L_STRING IDENTIFIER /* AX nonterminal symbols and their union types */ @@ -165,6 +159,7 @@ * deallocating it. */ %destructor { } // nothing to do +%destructor { } // nothing to do %destructor { } %destructor { free(const_cast($$)); } %destructor { for (auto& ptr : *$$) delete ptr; delete $$; } @@ -187,6 +182,13 @@ %left SHIFTLEFT SHIFTRIGHT %left PLUS MINUS %left MULTIPLY DIVIDE MODULO +/* UMINUS exists for contextual precedence with negation. Note that + * this is somewhat unecessary as * / % ops evaluate to the same + * values regardless i.e. (-a)*b == -(a*b), (-a)/b == -(a/b) and + * (-a)%b == -(a%b). In general it makes sense to adhere to this + * anyway (i.e. reprint -a * b rather than -(a*b)) + */ +%left UMINUS // for contextual precedence with negation %left NOT BITNOT PLUSPLUS MINUSMINUS %left LCURLY RCURLY %left LPARENS RPARENS @@ -412,10 +414,10 @@ ternary_expression: /// @brief A unary expression which takes an expression and returns an expression unary_expression: - PLUS expression { $$ = newNode(&@1, $2, tokens::PLUS); } - | MINUS expression { $$ = newNode(&@1, $2, tokens::MINUS); } - | BITNOT expression { $$ = newNode(&@1, $2, tokens::BITNOT); } - | NOT expression { $$ = newNode(&@1, $2, tokens::NOT); } + PLUS expression { $$ = newNode(&@1, $2, tokens::PLUS); } + | MINUS expression %prec UMINUS { $$ = newNode(&@1, $2, tokens::MINUS); } + | BITNOT expression { $$ = newNode(&@1, $2, tokens::BITNOT); } + | NOT expression { $$ = newNode(&@1, $2, tokens::NOT); } ; pre_crement: @@ -461,7 +463,8 @@ variable: /// @brief Syntax for supported attribute access attribute: type AT IDENTIFIER { $$ = newNode(&@$, $3, static_cast($1)); free(const_cast($3)); } - | I_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::INT); free(const_cast($2)); } + | I16_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::INT16); free(const_cast($2)); } + | I_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::INT32); free(const_cast($2)); } | F_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::FLOAT); free(const_cast($2)); } | V_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::VEC3F); free(const_cast($2)); } | S_AT IDENTIFIER { $$ = newNode(&@$, $2, tokens::STRING); free(const_cast($2)); } @@ -473,7 +476,7 @@ attribute: /// @brief Syntax for supported external variable access external: type DOLLAR IDENTIFIER { $$ = newNode(&@$, $3, static_cast($1)); free(const_cast($3)); } - | I_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::INT); free(const_cast($2)); } + | I_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::INT32); free(const_cast($2)); } | F_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::FLOAT); free(const_cast($2)); } | V_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::VEC3F); free(const_cast($2)); } | S_DOLLAR IDENTIFIER { $$ = newNode(&@$, $2, tokens::STRING); free(const_cast($2)); } @@ -491,14 +494,13 @@ local: /// @note Anything which uses one of the below tokens must free the returned char /// array (aside from TRUE and FALSE tokens) literal: - L_SHORT { $$ = newNode>(&@1, $1); free(const_cast($1)); } - | L_INT { $$ = newNode>(&@1, $1); free(const_cast($1)); } - | L_LONG { $$ = newNode>(&@1, $1); free(const_cast($1)); } - | L_FLOAT { $$ = newNode>(&@1, $1); free(const_cast($1)); } - | L_DOUBLE { $$ = newNode>(&@1, $1); free(const_cast($1)); } - | L_STRING { $$ = newNode>(&@1, $1); free(const_cast($1)); } - | TRUE { $$ = newNode>(&@1, true); } - | FALSE { $$ = newNode>(&@1, false); } + L_INT32 { $$ = newNode>(&@1, $1); } + | L_INT64 { $$ = newNode>(&@1, $1); } + | L_FLOAT { $$ = newNode>(&@1, static_cast($1)); } + | L_DOUBLE { $$ = newNode>(&@1, $1); } + | L_STRING { $$ = newNode>(&@1, $1); free(const_cast($1)); } + | TRUE { $$ = newNode>(&@1, true); } + | FALSE { $$ = newNode>(&@1, false); } ; type: @@ -519,9 +521,8 @@ matrix_type: /// @brief Scalar types scalar_type: BOOL { $$ = tokens::BOOL; } - | SHORT { $$ = tokens::SHORT; } - | INT { $$ = tokens::INT; } - | LONG { $$ = tokens::LONG; } + | INT32 { $$ = tokens::INT32; } + | INT64 { $$ = tokens::INT64; } | FLOAT { $$ = tokens::FLOAT; } | DOUBLE { $$ = tokens::DOUBLE; } ; diff --git a/openvdb_ax/openvdb_ax/grammar/generated/axlexer.cc b/openvdb_ax/openvdb_ax/grammar/generated/axlexer.cc index 5fb1c711b7..badd7b0db1 100644 --- a/openvdb_ax/openvdb_ax/grammar/generated/axlexer.cc +++ b/openvdb_ax/openvdb_ax/grammar/generated/axlexer.cc @@ -611,8 +611,8 @@ static void yynoreturn yy_fatal_error ( const char* msg ); (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; -#define YY_NUM_RULES 119 -#define YY_END_OF_BUFFER 120 +#define YY_NUM_RULES 123 +#define YY_END_OF_BUFFER 124 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info @@ -620,42 +620,44 @@ struct yy_trans_info flex_int32_t yy_verify; flex_int32_t yy_nxt; }; -static const flex_int16_t yy_accept[309] = +static const flex_int16_t yy_accept[326] = { 0, - 0, 0, 120, 118, 104, 106, 36, 118, 3, 9, - 12, 40, 41, 7, 5, 37, 6, 118, 8, 109, - 109, 109, 39, 1, 19, 4, 18, 38, 2, 117, - 44, 45, 14, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 42, 13, - 43, 15, 104, 17, 0, 107, 0, 26, 34, 27, - 24, 32, 22, 33, 23, 114, 51, 50, 49, 46, - 47, 48, 105, 25, 115, 109, 0, 110, 108, 93, - 94, 10, 21, 16, 20, 11, 117, 28, 117, 117, - 117, 117, 117, 117, 117, 66, 117, 117, 117, 57, - - 53, 117, 117, 117, 117, 117, 58, 54, 60, 117, - 117, 117, 117, 117, 59, 55, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 56, 52, 117, 117, 117, - 117, 29, 35, 111, 105, 115, 112, 0, 116, 30, - 31, 117, 117, 117, 117, 117, 117, 98, 117, 117, - 117, 117, 117, 117, 65, 117, 117, 117, 72, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 113, 70, 117, 98, 117, 117, 117, 117, 117, - 61, 99, 117, 117, 117, 117, 117, 117, 73, 117, - - 117, 117, 117, 117, 117, 117, 117, 117, 117, 117, - 117, 117, 117, 101, 62, 117, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 103, 117, 68, 117, 117, - 117, 117, 63, 74, 117, 117, 117, 87, 86, 89, - 88, 117, 117, 117, 117, 71, 117, 117, 117, 117, - 117, 117, 117, 117, 117, 102, 117, 79, 78, 77, - 82, 81, 80, 85, 84, 83, 117, 117, 67, 117, - 117, 75, 117, 91, 117, 117, 64, 100, 76, 117, - 117, 117, 117, 90, 117, 117, 117, 92, 117, 117, - 117, 117, 117, 117, 69, 117, 117, 117, 117, 117, - - 117, 95, 117, 117, 117, 96, 97, 0 + 0, 0, 124, 122, 108, 110, 36, 122, 3, 9, + 12, 40, 41, 7, 5, 37, 6, 122, 8, 113, + 113, 113, 39, 1, 19, 4, 18, 38, 2, 121, + 44, 45, 14, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 42, + 13, 43, 15, 108, 17, 0, 111, 0, 26, 34, + 27, 24, 32, 22, 33, 23, 118, 51, 50, 49, + 46, 47, 48, 109, 25, 119, 113, 0, 114, 112, + 94, 95, 10, 21, 16, 20, 11, 121, 28, 121, + 121, 121, 121, 121, 121, 121, 121, 67, 121, 121, + + 121, 58, 53, 121, 121, 121, 121, 121, 121, 59, + 54, 61, 121, 121, 121, 121, 121, 60, 55, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 57, 52, + 121, 121, 121, 121, 29, 35, 115, 109, 119, 116, + 0, 120, 30, 31, 121, 121, 121, 121, 121, 121, + 121, 101, 121, 121, 121, 121, 121, 121, 106, 66, + 121, 121, 121, 121, 72, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 117, 71, 121, + 106, 101, 121, 121, 121, 121, 121, 62, 102, 121, + + 121, 121, 121, 121, 121, 121, 121, 107, 121, 121, + 121, 121, 100, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 104, 63, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 69, 121, 121, 121, 121, 64, 75, 121, 121, 121, + 102, 73, 74, 88, 87, 90, 89, 121, 121, 121, + 121, 99, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 105, 121, 80, 79, 78, 83, 82, 81, 86, + 85, 84, 121, 121, 68, 121, 121, 76, 121, 56, + 92, 121, 121, 65, 103, 77, 121, 121, 121, 121, + + 91, 121, 121, 121, 93, 121, 121, 121, 121, 121, + 121, 70, 121, 121, 121, 121, 121, 121, 96, 121, + 121, 121, 97, 98, 0 } ; static const YY_CHAR yy_ec[256] = @@ -664,16 +666,16 @@ static const YY_CHAR yy_ec[256] = 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 5, 1, 6, 7, 8, 1, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 17, 18, - 19, 20, 17, 17, 17, 17, 17, 21, 22, 23, - 24, 25, 26, 27, 28, 28, 28, 28, 29, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, - 30, 31, 32, 33, 34, 1, 35, 36, 37, 38, - - 39, 40, 41, 42, 43, 28, 44, 45, 46, 47, - 48, 49, 28, 50, 51, 52, 53, 54, 55, 56, - 57, 58, 59, 60, 61, 62, 1, 1, 1, 1, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 17, 22, 17, 23, 17, 24, 25, 26, + 27, 28, 29, 30, 31, 31, 31, 31, 32, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 33, 34, 35, 36, 37, 1, 38, 39, 40, 41, + + 42, 43, 44, 45, 46, 31, 47, 48, 49, 50, + 51, 52, 31, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -690,150 +692,155 @@ static const YY_CHAR yy_ec[256] = 1, 1, 1, 1, 1 } ; -static const YY_CHAR yy_meta[63] = +static const YY_CHAR yy_meta[66] = { 0, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, - 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, - 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, - 1, 1 + 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, + 4, 3, 1, 1, 1, 1, 4, 4, 4, 4, + 4, 3, 3, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 1, 1, 1, 1 } ; -static const flex_int16_t yy_base[312] = +static const flex_int16_t yy_base[331] = { 0, - 0, 0, 396, 397, 393, 397, 370, 58, 397, 369, - 56, 397, 397, 368, 53, 397, 52, 73, 51, 66, - 117, 364, 397, 397, 45, 366, 46, 397, 397, 0, - 397, 397, 365, 24, 52, 34, 51, 113, 98, 340, - 352, 336, 346, 100, 71, 337, 133, 341, 397, 55, - 397, 397, 380, 397, 119, 397, 0, 397, 397, 397, - 397, 397, 397, 397, 397, 160, 397, 397, 397, 397, - 397, 397, 0, 397, 165, 172, 190, 397, 397, 397, - 397, 357, 397, 397, 397, 356, 0, 397, 331, 339, - 326, 341, 340, 327, 333, 319, 320, 317, 317, 397, - - 397, 323, 319, 316, 322, 317, 397, 397, 0, 56, - 316, 310, 106, 309, 397, 397, 312, 58, 53, 316, - 312, 314, 303, 306, 122, 397, 397, 317, 303, 309, - 308, 397, 397, 397, 0, 195, 397, 176, 201, 397, - 397, 305, 314, 309, 297, 295, 118, 310, 308, 304, - 296, 302, 289, 304, 0, 299, 300, 293, 0, 294, - 178, 280, 281, 279, 281, 283, 290, 276, 59, 275, - 277, 274, 285, 284, 78, 279, 278, 207, 268, 281, - 273, 397, 0, 273, 0, 265, 263, 271, 260, 267, - 0, 0, 261, 271, 257, 261, 255, 259, 0, 82, - - 113, 262, 269, 264, 252, 249, 261, 251, 255, 250, - 259, 258, 249, 0, 0, 255, 244, 244, 249, 244, - 193, 199, 205, 240, 234, 0, 247, 0, 238, 239, - 244, 235, 0, 0, 243, 237, 240, 0, 0, 0, - 0, 222, 225, 239, 228, 0, 236, 233, 235, 230, - 218, 227, 233, 228, 216, 0, 217, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 211, 223, 0, 204, - 204, 0, 207, 235, 214, 200, 0, 0, 0, 199, - 210, 201, 199, 206, 187, 190, 175, 0, 177, 167, - 165, 151, 143, 139, 0, 137, 118, 119, 112, 123, - - 122, 0, 102, 89, 94, 0, 0, 397, 259, 75, - 262 + 0, 0, 417, 418, 414, 418, 388, 61, 418, 387, + 59, 418, 418, 386, 56, 418, 55, 79, 54, 126, + 382, 381, 418, 418, 45, 383, 46, 418, 418, 0, + 418, 418, 382, 24, 40, 38, 44, 104, 370, 81, + 356, 368, 352, 362, 120, 62, 353, 121, 357, 418, + 49, 418, 418, 399, 418, 74, 418, 0, 418, 418, + 418, 418, 418, 418, 418, 418, 164, 418, 418, 418, + 418, 418, 418, 0, 418, 171, 0, 183, 418, 418, + 418, 418, 373, 418, 418, 418, 372, 0, 418, 347, + 355, 341, 341, 356, 355, 342, 348, 334, 335, 332, + + 332, 418, 418, 338, 54, 332, 338, 333, 334, 418, + 418, 0, 58, 331, 325, 68, 324, 418, 418, 327, + 72, 82, 331, 327, 329, 318, 321, 75, 418, 418, + 332, 318, 324, 323, 418, 418, 418, 0, 127, 418, + 198, 166, 418, 418, 320, 329, 324, 323, 311, 309, + 82, 324, 322, 318, 310, 316, 303, 318, 204, 0, + 313, 314, 310, 306, 205, 307, 190, 293, 294, 292, + 294, 296, 303, 289, 108, 288, 290, 287, 298, 297, + 74, 292, 291, 210, 281, 294, 286, 418, 0, 286, + 0, 0, 278, 276, 284, 273, 280, 0, 0, 274, + + 284, 270, 302, 304, 301, 271, 265, 0, 269, 296, + 298, 295, 0, 130, 136, 269, 276, 271, 259, 256, + 268, 258, 262, 257, 266, 265, 256, 0, 0, 262, + 251, 251, 256, 251, 191, 192, 198, 247, 241, 254, + 0, 245, 246, 251, 242, 0, 0, 250, 244, 247, + 258, 0, 0, 0, 0, 0, 0, 228, 231, 245, + 234, 0, 242, 239, 241, 236, 224, 233, 239, 234, + 220, 0, 217, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 211, 225, 0, 206, 206, 0, 209, 418, + 239, 216, 202, 0, 0, 0, 201, 212, 205, 211, + + 199, 204, 209, 200, 0, 207, 206, 206, 195, 188, + 163, 0, 171, 143, 145, 121, 123, 123, 0, 108, + 98, 86, 0, 0, 418, 265, 267, 271, 90, 87 } ; -static const flex_int16_t yy_def[312] = +static const flex_int16_t yy_def[331] = { 0, - 308, 1, 308, 308, 308, 308, 308, 309, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 21, 308, 308, 308, 308, 308, 308, 308, 310, - 308, 308, 308, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 308, 308, - 308, 308, 308, 308, 309, 308, 309, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 311, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 310, 308, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 308, - - 308, 310, 310, 310, 310, 310, 308, 308, 310, 310, - 310, 310, 310, 310, 308, 308, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 308, 308, 310, 310, 310, - 310, 308, 308, 308, 311, 308, 308, 308, 308, 308, - 308, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 308, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - 310, 310, 310, 310, 310, 310, 310, 310, 310, 310, - - 310, 310, 310, 310, 310, 310, 310, 0, 308, 308, - 308 + 325, 1, 325, 325, 325, 325, 325, 326, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 20, 20, 325, 325, 325, 325, 325, 325, 325, 327, + 325, 325, 325, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 325, + 325, 325, 325, 325, 325, 326, 325, 326, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 328, 325, 325, 20, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 327, 325, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + + 327, 325, 325, 327, 327, 327, 327, 327, 327, 325, + 325, 327, 327, 327, 327, 327, 327, 325, 325, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 325, 325, + 327, 327, 327, 327, 325, 325, 325, 328, 329, 325, + 325, 330, 325, 325, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 325, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 325, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 327, 327, 327, 327, 327, 327, + 327, 327, 327, 327, 0, 325, 325, 325, 325, 325 } ; -static const flex_int16_t yy_nxt[460] = +static const flex_int16_t yy_nxt[484] = { 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, - 14, 15, 16, 17, 18, 19, 20, 20, 21, 22, - 23, 24, 25, 26, 27, 28, 29, 30, 30, 31, - 4, 32, 33, 30, 30, 34, 35, 36, 37, 38, - 30, 30, 39, 30, 40, 41, 30, 30, 42, 43, - 44, 45, 46, 47, 48, 30, 30, 30, 49, 50, - 51, 52, 56, 59, 62, 64, 73, 82, 83, 85, - 86, 89, 95, 90, 74, 65, 63, 87, 132, 60, - 75, 96, 76, 76, 76, 76, 91, 168, 57, 66, - 66, 66, 66, 92, 77, 97, 93, 98, 166, 94, - - 158, 210, 169, 107, 77, 115, 99, 159, 67, 121, - 78, 211, 122, 68, 133, 167, 79, 217, 100, 238, - 123, 239, 69, 56, 108, 218, 116, 124, 70, 71, - 72, 75, 307, 76, 76, 76, 76, 109, 126, 101, - 306, 117, 118, 80, 110, 77, 305, 102, 162, 57, - 240, 119, 241, 163, 120, 77, 304, 103, 303, 127, - 104, 78, 105, 302, 175, 106, 301, 79, 187, 188, - 300, 128, 176, 177, 278, 129, 66, 66, 66, 66, - 130, 136, 136, 136, 136, 299, 75, 298, 76, 76, - 76, 76, 139, 139, 139, 139, 200, 201, 297, 134, - - 77, 138, 256, 138, 137, 214, 139, 139, 139, 139, - 77, 136, 136, 136, 136, 296, 78, 139, 139, 139, - 139, 192, 79, 77, 221, 222, 223, 202, 295, 77, - 258, 256, 259, 77, 137, 260, 261, 291, 262, 77, - 182, 263, 264, 292, 265, 293, 256, 266, 294, 214, - 290, 289, 278, 288, 287, 185, 286, 285, 224, 55, - 284, 55, 135, 283, 135, 282, 281, 280, 214, 214, - 279, 278, 278, 278, 277, 276, 275, 274, 192, 273, - 192, 192, 272, 271, 270, 269, 268, 267, 256, 257, - 256, 255, 254, 253, 252, 251, 250, 249, 248, 247, - - 246, 245, 244, 243, 242, 237, 236, 235, 234, 233, - 232, 231, 230, 229, 185, 185, 228, 227, 226, 225, - 220, 219, 216, 215, 214, 213, 212, 209, 208, 207, - 206, 205, 204, 203, 199, 198, 197, 196, 195, 194, - 193, 192, 191, 190, 189, 186, 185, 185, 184, 183, - 181, 180, 179, 178, 174, 173, 172, 171, 170, 165, - 164, 161, 160, 157, 156, 155, 154, 153, 152, 151, - 150, 149, 148, 147, 146, 145, 144, 143, 142, 141, - 140, 53, 131, 125, 114, 113, 112, 111, 88, 84, - 81, 61, 58, 54, 53, 308, 3, 308, 308, 308, - - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308 + 14, 15, 16, 17, 18, 19, 20, 20, 20, 21, + 22, 20, 20, 23, 24, 25, 26, 27, 28, 29, + 30, 30, 31, 4, 32, 33, 30, 30, 34, 35, + 36, 37, 38, 30, 39, 40, 30, 41, 42, 30, + 30, 43, 44, 45, 46, 47, 48, 49, 30, 30, + 30, 50, 51, 52, 53, 57, 60, 63, 65, 74, + 83, 84, 86, 87, 90, 135, 91, 93, 57, 97, + 75, 66, 64, 92, 94, 61, 110, 95, 98, 142, + 96, 99, 139, 100, 58, 67, 67, 67, 67, 67, + + 67, 67, 101, 124, 158, 164, 125, 58, 159, 102, + 111, 136, 165, 168, 126, 172, 231, 68, 169, 174, + 181, 127, 69, 112, 232, 118, 129, 324, 182, 183, + 113, 70, 173, 103, 175, 194, 195, 71, 72, 73, + 76, 104, 77, 77, 77, 77, 77, 77, 77, 119, + 130, 105, 323, 224, 106, 322, 107, 78, 78, 108, + 321, 320, 131, 225, 120, 121, 132, 78, 78, 140, + 254, 133, 255, 79, 122, 319, 256, 123, 257, 80, + 67, 67, 67, 67, 67, 67, 67, 139, 139, 139, + 139, 139, 139, 139, 141, 318, 141, 78, 317, 142, + + 142, 142, 142, 142, 142, 142, 137, 78, 188, 214, + 215, 295, 316, 140, 142, 142, 142, 142, 142, 142, + 142, 203, 210, 204, 211, 205, 212, 191, 235, 236, + 237, 274, 277, 275, 278, 315, 276, 279, 280, 309, + 281, 310, 216, 282, 311, 314, 272, 228, 313, 199, + 312, 272, 308, 272, 228, 307, 306, 295, 305, 304, + 192, 303, 302, 301, 238, 56, 300, 56, 56, 88, + 88, 138, 299, 138, 138, 298, 297, 228, 228, 296, + 295, 295, 295, 294, 293, 292, 291, 290, 199, 289, + 199, 199, 288, 287, 286, 285, 284, 283, 272, 273, + + 272, 271, 270, 269, 268, 267, 266, 265, 264, 263, + 262, 261, 260, 259, 258, 253, 252, 251, 250, 249, + 248, 191, 191, 191, 247, 246, 245, 244, 243, 242, + 192, 192, 241, 240, 191, 239, 234, 233, 230, 229, + 228, 227, 226, 223, 222, 221, 220, 219, 218, 217, + 213, 209, 208, 207, 206, 202, 201, 200, 199, 198, + 197, 196, 193, 192, 192, 191, 190, 189, 187, 186, + 185, 184, 180, 179, 178, 177, 176, 171, 170, 167, + 166, 163, 162, 161, 160, 157, 156, 155, 154, 153, + 152, 151, 150, 149, 148, 147, 146, 145, 144, 143, + + 54, 134, 128, 117, 116, 115, 114, 109, 89, 85, + 82, 81, 62, 59, 55, 54, 325, 3, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325 } ; -static const flex_int16_t yy_chk[460] = +static const flex_int16_t yy_chk[484] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, @@ -841,50 +848,53 @@ static const flex_int16_t yy_chk[460] = 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 8, 11, 15, 17, 19, 25, 25, 27, - 27, 34, 36, 34, 19, 17, 15, 310, 50, 11, - 20, 36, 20, 20, 20, 20, 35, 119, 8, 18, - 18, 18, 18, 35, 20, 37, 35, 37, 118, 35, - - 110, 169, 119, 39, 20, 44, 37, 110, 18, 45, - 20, 169, 45, 18, 50, 118, 20, 175, 38, 200, - 45, 200, 18, 55, 39, 175, 44, 45, 18, 18, - 18, 21, 305, 21, 21, 21, 21, 39, 47, 38, - 304, 44, 44, 21, 39, 21, 303, 38, 113, 55, - 201, 44, 201, 113, 44, 21, 301, 38, 300, 47, - 38, 21, 38, 299, 125, 38, 298, 21, 147, 147, - 297, 47, 125, 125, 296, 47, 66, 66, 66, 66, - 47, 75, 75, 75, 75, 294, 76, 293, 76, 76, - 76, 76, 138, 138, 138, 138, 161, 161, 292, 66, - - 76, 77, 291, 77, 75, 290, 77, 77, 77, 77, - 76, 136, 136, 136, 136, 289, 76, 139, 139, 139, - 139, 287, 76, 136, 178, 178, 178, 161, 286, 139, - 221, 285, 221, 136, 136, 221, 222, 283, 222, 139, - 139, 222, 223, 284, 223, 284, 282, 223, 284, 281, - 280, 276, 275, 274, 273, 271, 270, 268, 178, 309, - 267, 309, 311, 257, 311, 255, 254, 253, 252, 251, - 250, 249, 248, 247, 245, 244, 243, 242, 237, 236, - 235, 232, 231, 230, 229, 227, 225, 224, 220, 219, - 218, 217, 216, 213, 212, 211, 210, 209, 208, 207, - - 206, 205, 204, 203, 202, 198, 197, 196, 195, 194, - 193, 190, 189, 188, 187, 186, 184, 181, 180, 179, - 177, 176, 174, 173, 172, 171, 170, 168, 167, 166, - 165, 164, 163, 162, 160, 158, 157, 156, 154, 153, - 152, 151, 150, 149, 148, 146, 145, 144, 143, 142, - 131, 130, 129, 128, 124, 123, 122, 121, 120, 117, - 114, 112, 111, 106, 105, 104, 103, 102, 99, 98, - 97, 96, 95, 94, 93, 92, 91, 90, 89, 86, - 82, 53, 48, 46, 43, 42, 41, 40, 33, 26, - 22, 14, 10, 7, 5, 3, 308, 308, 308, 308, - - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308, 308, - 308, 308, 308, 308, 308, 308, 308, 308, 308 + 1, 1, 1, 1, 1, 8, 11, 15, 17, 19, + 25, 25, 27, 27, 34, 51, 34, 35, 56, 36, + 19, 17, 15, 34, 35, 11, 40, 35, 36, 330, + 35, 37, 329, 37, 8, 18, 18, 18, 18, 18, + + 18, 18, 37, 46, 105, 113, 46, 56, 105, 38, + 40, 51, 113, 116, 46, 121, 181, 18, 116, 122, + 128, 46, 18, 40, 181, 45, 48, 322, 128, 128, + 40, 18, 121, 38, 122, 151, 151, 18, 18, 18, + 20, 38, 20, 20, 20, 20, 20, 20, 20, 45, + 48, 38, 321, 175, 38, 320, 38, 20, 139, 38, + 318, 317, 48, 175, 45, 45, 48, 20, 139, 139, + 214, 48, 214, 20, 45, 316, 215, 45, 215, 20, + 67, 67, 67, 67, 67, 67, 67, 76, 76, 76, + 76, 76, 76, 76, 78, 315, 78, 142, 314, 78, + + 78, 78, 78, 78, 78, 78, 67, 142, 142, 167, + 167, 313, 311, 76, 141, 141, 141, 141, 141, 141, + 141, 159, 165, 159, 165, 159, 165, 165, 184, 184, + 184, 235, 236, 235, 236, 310, 235, 236, 237, 301, + 237, 301, 167, 237, 301, 309, 308, 307, 306, 304, + 303, 302, 300, 299, 298, 297, 293, 292, 291, 289, + 287, 286, 284, 283, 184, 326, 273, 326, 326, 327, + 327, 328, 271, 328, 328, 270, 269, 268, 267, 266, + 265, 264, 263, 261, 260, 259, 258, 251, 250, 249, + 248, 245, 244, 243, 242, 240, 239, 238, 234, 233, + + 232, 231, 230, 227, 226, 225, 224, 223, 222, 221, + 220, 219, 218, 217, 216, 212, 211, 210, 209, 207, + 206, 205, 204, 203, 202, 201, 200, 197, 196, 195, + 194, 193, 190, 187, 186, 185, 183, 182, 180, 179, + 178, 177, 176, 174, 173, 172, 171, 170, 169, 168, + 166, 164, 163, 162, 161, 158, 157, 156, 155, 154, + 153, 152, 150, 149, 148, 147, 146, 145, 134, 133, + 132, 131, 127, 126, 125, 124, 123, 120, 117, 115, + 114, 109, 108, 107, 106, 104, 101, 100, 99, 98, + 97, 96, 95, 94, 93, 92, 91, 90, 87, 83, + + 54, 49, 47, 44, 43, 42, 41, 39, 33, 26, + 22, 21, 14, 10, 7, 5, 3, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325, 325, 325, 325, 325, 325, 325, 325, + 325, 325, 325 } ; static yy_state_type yy_last_accepting_state; @@ -911,11 +921,12 @@ SPDX-License-Identifier: MPL-2.0 @brief OpenVDB AX Lexer */ - #include "openvdb_ax/Exceptions.h" #include "openvdb_ax/ast/Parse.h" + #include "openvdb_ax/compiler/Logger.h" #include "axparser.h" /*generated by bison*/ #include - #include + #include + #include /// @note Bypasses conversion warnings in YY_CURRENT_BUFFER macro. /// This is a bit over zealous as we only need to suppress @@ -926,10 +937,10 @@ SPDX-License-Identifier: MPL-2.0 /// @note Location tracking macro for axlloc token locations. /// YY_USER_ACTION is called before any and each lexer action - /// is performed. Instead of manually tracking newlines, we /// can simply scan for them in the current text held by axtext #define YY_USER_ACTION \ + assert(axlog); \ axlloc.first_line = axlloc.last_line; \ axlloc.first_column = axlloc.last_column; \ for (int i = 0; axtext[i] != '\0'; i++) { \ @@ -1198,13 +1209,13 @@ YY_DECL while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 309 ) + if ( yy_current_state >= 326 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; ++yy_cp; } - while ( yy_base[yy_current_state] != 397 ); + while ( yy_base[yy_current_state] != 418 ); yy_find_action: yy_act = yy_accept[yy_current_state]; @@ -1450,257 +1461,336 @@ YY_RULE_SETUP YY_BREAK case 56: YY_RULE_SETUP -{ return V_DOLLAR; } +{ return I16_AT; } YY_BREAK case 57: YY_RULE_SETUP -{ return F_DOLLAR; } +{ return V_DOLLAR; } YY_BREAK case 58: YY_RULE_SETUP -{ return I_DOLLAR; } +{ return F_DOLLAR; } YY_BREAK case 59: YY_RULE_SETUP -{ return S_DOLLAR; } +{ return I_DOLLAR; } YY_BREAK case 60: YY_RULE_SETUP -{ return IF; } +{ return S_DOLLAR; } YY_BREAK case 61: YY_RULE_SETUP -{ return ELSE; } +{ return IF; } YY_BREAK case 62: YY_RULE_SETUP -{ return TRUE; } +{ return ELSE; } YY_BREAK case 63: YY_RULE_SETUP -{ return FALSE; } +{ return TRUE; } YY_BREAK case 64: YY_RULE_SETUP -{ return RETURN; } +{ return FALSE; } YY_BREAK case 65: YY_RULE_SETUP -{ return FOR; } +{ return RETURN; } YY_BREAK case 66: YY_RULE_SETUP -{ return DO; } +{ return FOR; } YY_BREAK case 67: YY_RULE_SETUP -{ return WHILE; } +{ return DO; } YY_BREAK case 68: YY_RULE_SETUP -{ return BREAK;} +{ return WHILE; } YY_BREAK case 69: YY_RULE_SETUP -{ return CONTINUE;} +{ return BREAK;} YY_BREAK case 70: YY_RULE_SETUP -{ return BOOL; } +{ return CONTINUE;} YY_BREAK case 71: YY_RULE_SETUP -{ return SHORT; } +{ return BOOL; } YY_BREAK case 72: YY_RULE_SETUP -{ return INT; } +{ return INT32; } YY_BREAK case 73: YY_RULE_SETUP -{ return LONG; } +{ return INT32; } YY_BREAK case 74: YY_RULE_SETUP -{ return FLOAT; } +{ return INT64; } YY_BREAK case 75: YY_RULE_SETUP -{ return DOUBLE; } +{ return FLOAT; } YY_BREAK case 76: YY_RULE_SETUP -{ return STRING; } +{ return DOUBLE; } YY_BREAK case 77: YY_RULE_SETUP -{ return VEC2I; } +{ return STRING; } YY_BREAK case 78: YY_RULE_SETUP -{ return VEC2F; } +{ return VEC2I; } YY_BREAK case 79: YY_RULE_SETUP -{ return VEC2D; } +{ return VEC2F; } YY_BREAK case 80: YY_RULE_SETUP -{ return VEC3I; } +{ return VEC2D; } YY_BREAK case 81: YY_RULE_SETUP -{ return VEC3F; } +{ return VEC3I; } YY_BREAK case 82: YY_RULE_SETUP -{ return VEC3D; } +{ return VEC3F; } YY_BREAK case 83: YY_RULE_SETUP -{ return VEC4I; } +{ return VEC3D; } YY_BREAK case 84: YY_RULE_SETUP -{ return VEC4F; } +{ return VEC4I; } YY_BREAK case 85: YY_RULE_SETUP -{ return VEC4D; } +{ return VEC4F; } YY_BREAK case 86: YY_RULE_SETUP -{ return MAT3F; } +{ return VEC4D; } YY_BREAK case 87: YY_RULE_SETUP -{ return MAT3D; } +{ return MAT3F; } YY_BREAK case 88: YY_RULE_SETUP -{ return MAT4F; } +{ return MAT3D; } YY_BREAK case 89: YY_RULE_SETUP -{ return MAT4D; } +{ return MAT4F; } YY_BREAK -/*Tokens to support VEX Syntax*/ case 90: YY_RULE_SETUP -{ return VEC3F; } /*VEX SUPPORT TOKENS*/ +{ return MAT4D; } YY_BREAK +/*Tokens to support VEX Syntax*/ case 91: YY_RULE_SETUP -{ return MAT4F; } +{ return VEC3F; } /*VEX SUPPORT TOKENS*/ YY_BREAK case 92: YY_RULE_SETUP -{ return MAT3F; } +{ return MAT4F; } YY_BREAK case 93: YY_RULE_SETUP -{ return M3F_AT; } +{ return MAT3F; } YY_BREAK case 94: YY_RULE_SETUP -{ return M4F_AT; } +{ return M3F_AT; } YY_BREAK -/*Deprecated Tokens*/ case 95: YY_RULE_SETUP -{ return VEC3I; } +{ return M4F_AT; } YY_BREAK +/*Deprecated Tokens*/ case 96: YY_RULE_SETUP -{ return VEC3F; } +{ axlog->warning("vectorint keyword is deprecated. use vec3i.", + {axlloc.first_line, axlloc.first_column}); + return VEC3I; + } YY_BREAK case 97: YY_RULE_SETUP -{ return VEC3D; } +{ axlog->warning("vectorfloat keyword is deprecated. use vec3f.", + {axlloc.first_line, axlloc.first_column}); + return VEC3F; + } YY_BREAK -/* Reserved keywords */ case 98: +YY_RULE_SETUP +{ axlog->warning("vectordouble keyword is deprecated. use vec3d.", + {axlloc.first_line, axlloc.first_column}); + return VEC3D; + } + YY_BREAK case 99: +YY_RULE_SETUP +{ axlog->warning("short local variables have been removed. use int, int32 or int64.", + {axlloc.first_line, axlloc.first_column}); + return INT32; + } + YY_BREAK case 100: +YY_RULE_SETUP +{ axlog->warning("long keyword is deprecated. use int64.", + {axlloc.first_line, axlloc.first_column}); + return INT64; + } + YY_BREAK +/* Reserved keywords */ case 101: case 102: case 103: +case 104: +case 105: +case 106: +case 107: YY_RULE_SETUP { /* @todo: move this into parser */ std::ostringstream os; os <<"\""<< axtext << "\" is a reserved keyword."; - assert(axlog); axlog->error(os.str(), {axlloc.first_line, axlloc.first_column}); } YY_BREAK -case 104: +case 108: YY_RULE_SETUP { } /* ignore whitespace */ YY_BREAK -case 105: +case 109: YY_RULE_SETUP { } /* ignore //-style one-line comments */ YY_BREAK -case 106: -/* rule 106 can match eol */ +case 110: +/* rule 110 can match eol */ YY_RULE_SETUP { } /* ignore newlines */ YY_BREAK -case 107: +case 111: YY_RULE_SETUP { axlval.string = strndup(axtext+1, axleng-2); return L_STRING; } YY_BREAK -case 108: +case 112: YY_RULE_SETUP { - axlval.string = strdup(axtext); - return L_SHORT; + axlog->warning("s suffix is deprecated.", {axlloc.first_line, axlloc.first_column}); + errno = 0; + axlval.index = uint64_t(std::strtoull(axtext, /*end*/nullptr, /*base*/10)); + if (errno == ERANGE) { + errno = 0; + axlog->error("integer constant is too large to be represented:", + {axlloc.first_line, axlloc.first_column}); + } + return L_INT32; } YY_BREAK -case 109: +case 113: YY_RULE_SETUP { - axlval.string = strdup(axtext); - return L_INT; + errno = 0; + axlval.index = uint64_t(std::strtoull(axtext, /*end*/nullptr, /*base*/10)); + if (errno == ERANGE) { + errno = 0; + axlog->error("integer constant is too large to be represented:", + {axlloc.first_line, axlloc.first_column}); + } + + return L_INT32; } YY_BREAK -case 110: +case 114: YY_RULE_SETUP { - axlval.string = strdup(axtext); - return L_LONG; + errno = 0; + axlval.index = uint64_t(std::strtoull(axtext, /*end*/nullptr, /*base*/10)); + if (errno == ERANGE) { + errno = 0; + axlog->error("integer constant is too large to be represented:", + {axlloc.first_line, axlloc.first_column}); + } + return L_INT64; } YY_BREAK -case 111: -case 112: -case 113: +case 115: +case 116: +case 117: YY_RULE_SETUP { - axlval.string = strdup(axtext); + errno = 0; + axlval.flt = static_cast(std::strtof(axtext, /*end*/nullptr)); + if (errno == ERANGE) { + errno = 0; + if (std::isinf(axlval.flt)) { + axlog->warning("floating point constant is too large for type float, " + "will be converted to inf.", {axlloc.first_line, axlloc.first_column}); + } + else if (axlval.flt == 0.0) { + axlog->warning("floating point constant truncated to zero.", + {axlloc.first_line, axlloc.first_column}); + } + else { + axlog->warning("floating point constant is too small for type float " + "and may underflow.", {axlloc.first_line, axlloc.first_column}); + } + } return L_FLOAT; } YY_BREAK -case 114: -case 115: -case 116: +case 118: +case 119: +case 120: YY_RULE_SETUP { - axlval.string = strdup(axtext); + errno = 0; + axlval.flt = std::strtod(axtext, /*end*/nullptr); + if (errno == ERANGE) { + errno = 0; + if (std::isinf(axlval.flt)) { + axlog->warning("floating point constant is too large for type double, " + "will be converted to inf.", {axlloc.first_line, axlloc.first_column}); + } + else if (axlval.flt == 0.0) { + axlog->warning("floating point constant truncated to zero.", + {axlloc.first_line, axlloc.first_column}); + } + else { + axlog->warning("floating point constant is too small for type double " + "and may underflow.", {axlloc.first_line, axlloc.first_column}); + } + } return L_DOUBLE; } YY_BREAK -case 117: +case 121: YY_RULE_SETUP { axlval.string = strdup(axtext); return IDENTIFIER; } YY_BREAK -case 118: +case 122: YY_RULE_SETUP { /* error on everything else */ @@ -1711,7 +1801,7 @@ YY_RULE_SETUP } YY_BREAK -case 119: +case 123: YY_RULE_SETUP ECHO; YY_BREAK @@ -2011,7 +2101,7 @@ static int yy_get_next_buffer (void) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 309 ) + if ( yy_current_state >= 326 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; @@ -2039,11 +2129,11 @@ static int yy_get_next_buffer (void) while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; - if ( yy_current_state >= 309 ) + if ( yy_current_state >= 326 ) yy_c = yy_meta[yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; - yy_is_jam = (yy_current_state == 308); + yy_is_jam = (yy_current_state == 325); return yy_is_jam ? 0 : yy_current_state; } diff --git a/openvdb_ax/openvdb_ax/grammar/generated/axparser.cc b/openvdb_ax/openvdb_ax/grammar/generated/axparser.cc index 4cdc35d9c1..04e3f993f1 100644 --- a/openvdb_ax/openvdb_ax/grammar/generated/axparser.cc +++ b/openvdb_ax/openvdb_ax/grammar/generated/axparser.cc @@ -61,16 +61,12 @@ /* "%code top" blocks. */ - #include - #include - - #include // for OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN - #include "openvdb_ax/ast/AST.h" #include "openvdb_ax/ast/Parse.h" #include "openvdb_ax/ast/Tokens.h" - #include "openvdb_ax/compiler/Logger.h" + #include // for OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN + #include /// @note Bypasses bison conversion warnings in yyparse OPENVDB_NO_TYPE_CONVERSION_WARNING_BEGIN @@ -165,83 +161,82 @@ extern int axdebug; STRING = 275, DOUBLE = 276, FLOAT = 277, - LONG = 278, - INT = 279, - SHORT = 280, - BOOL = 281, - VOID = 282, - VEC2I = 283, - VEC2F = 284, - VEC2D = 285, - VEC3I = 286, - VEC3F = 287, - VEC3D = 288, - VEC4I = 289, - VEC4F = 290, - VEC4D = 291, - F_AT = 292, - I_AT = 293, - V_AT = 294, - S_AT = 295, - MAT3F = 296, - MAT3D = 297, - MAT4F = 298, - MAT4D = 299, - M3F_AT = 300, - M4F_AT = 301, - F_DOLLAR = 302, - I_DOLLAR = 303, - V_DOLLAR = 304, - S_DOLLAR = 305, - DOT_X = 306, - DOT_Y = 307, - DOT_Z = 308, - L_SHORT = 309, - L_INT = 310, - L_LONG = 311, - L_FLOAT = 312, - L_DOUBLE = 313, - L_STRING = 314, - IDENTIFIER = 315, - COMMA = 316, - QUESTION = 317, - COLON = 318, - EQUALS = 319, - PLUSEQUALS = 320, - MINUSEQUALS = 321, - MULTIPLYEQUALS = 322, - DIVIDEEQUALS = 323, - MODULOEQUALS = 324, - BITANDEQUALS = 325, - BITXOREQUALS = 326, - BITOREQUALS = 327, - SHIFTLEFTEQUALS = 328, - SHIFTRIGHTEQUALS = 329, - OR = 330, - AND = 331, - BITOR = 332, - BITXOR = 333, - BITAND = 334, - EQUALSEQUALS = 335, - NOTEQUALS = 336, - MORETHAN = 337, - LESSTHAN = 338, - MORETHANOREQUAL = 339, - LESSTHANOREQUAL = 340, - SHIFTLEFT = 341, - SHIFTRIGHT = 342, - PLUS = 343, - MINUS = 344, - MULTIPLY = 345, - DIVIDE = 346, - MODULO = 347, - NOT = 348, - BITNOT = 349, - PLUSPLUS = 350, - MINUSMINUS = 351, - LPARENS = 352, - RPARENS = 353, - LOWER_THAN_ELSE = 354 + INT32 = 278, + INT64 = 279, + BOOL = 280, + VEC2I = 281, + VEC2F = 282, + VEC2D = 283, + VEC3I = 284, + VEC3F = 285, + VEC3D = 286, + VEC4I = 287, + VEC4F = 288, + VEC4D = 289, + F_AT = 290, + I_AT = 291, + V_AT = 292, + S_AT = 293, + I16_AT = 294, + MAT3F = 295, + MAT3D = 296, + MAT4F = 297, + MAT4D = 298, + M3F_AT = 299, + M4F_AT = 300, + F_DOLLAR = 301, + I_DOLLAR = 302, + V_DOLLAR = 303, + S_DOLLAR = 304, + DOT_X = 305, + DOT_Y = 306, + DOT_Z = 307, + L_INT32 = 308, + L_INT64 = 309, + L_FLOAT = 310, + L_DOUBLE = 311, + L_STRING = 312, + IDENTIFIER = 313, + COMMA = 314, + QUESTION = 315, + COLON = 316, + EQUALS = 317, + PLUSEQUALS = 318, + MINUSEQUALS = 319, + MULTIPLYEQUALS = 320, + DIVIDEEQUALS = 321, + MODULOEQUALS = 322, + BITANDEQUALS = 323, + BITXOREQUALS = 324, + BITOREQUALS = 325, + SHIFTLEFTEQUALS = 326, + SHIFTRIGHTEQUALS = 327, + OR = 328, + AND = 329, + BITOR = 330, + BITXOR = 331, + BITAND = 332, + EQUALSEQUALS = 333, + NOTEQUALS = 334, + MORETHAN = 335, + LESSTHAN = 336, + MORETHANOREQUAL = 337, + LESSTHANOREQUAL = 338, + SHIFTLEFT = 339, + SHIFTRIGHT = 340, + PLUS = 341, + MINUS = 342, + MULTIPLY = 343, + DIVIDE = 344, + MODULO = 345, + UMINUS = 346, + NOT = 347, + BITNOT = 348, + PLUSPLUS = 349, + MINUSMINUS = 350, + LPARENS = 351, + RPARENS = 352, + LOWER_THAN_ELSE = 353 }; #endif @@ -257,6 +252,7 @@ union AXSTYPE const char* string; uint64_t index; + double flt; openvdb::ax::ast::Tree* tree; openvdb::ax::ast::ValueBase* value; @@ -309,9 +305,8 @@ int axparse (openvdb::ax::ast::Tree** tree); /* Unqualified %code blocks. */ - template - T* newNode(YYLTYPE* loc, const Args&... args) { + T* newNode(AXLTYPE* loc, const Args&... args) { T* ptr = new T(args...); assert(axlog); axlog->addNodeLocation(ptr, {loc->first_line, loc->first_column}); @@ -562,21 +557,21 @@ union yyalloc /* YYFINAL -- State number of the termination state. */ #define YYFINAL 126 /* YYLAST -- Last index in YYTABLE. */ -#define YYLAST 892 +#define YYLAST 898 /* YYNTOKENS -- Number of terminals. */ -#define YYNTOKENS 100 +#define YYNTOKENS 99 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 37 /* YYNRULES -- Number of rules. */ -#define YYNRULES 156 +#define YYNRULES 155 /* YYNSTATES -- Number of states. */ #define YYNSTATES 263 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 -#define YYMAXUTOK 354 +#define YYMAXUTOK 353 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) @@ -620,29 +615,29 @@ static const yytype_uint8 yytranslate[] = 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, - 95, 96, 97, 98, 99 + 95, 96, 97, 98 }; #if AXDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { - 0, 235, 235, 238, 244, 245, 246, 249, 255, 256, - 262, 263, 264, 265, 266, 267, 268, 269, 272, 273, - 278, 279, 285, 286, 287, 288, 289, 290, 291, 292, - 293, 294, 295, 300, 302, 308, 313, 318, 324, 335, - 336, 341, 342, 348, 349, 354, 355, 359, 360, 365, - 366, 367, 372, 373, 378, 380, 381, 386, 387, 392, - 393, 394, 399, 400, 401, 402, 403, 404, 405, 406, - 407, 408, 409, 415, 416, 417, 418, 419, 420, 421, - 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, - 432, 436, 437, 442, 443, 444, 445, 449, 450, 454, - 455, 460, 461, 462, 463, 464, 465, 466, 478, 484, - 485, 490, 491, 492, 493, 494, 495, 496, 497, 502, - 503, 504, 505, 506, 507, 514, 521, 522, 523, 524, - 525, 526, 527, 528, 532, 533, 534, 535, 540, 541, - 542, 543, 548, 549, 550, 551, 552, 553, 558, 559, - 560, 561, 562, 563, 564, 565, 566 + 0, 237, 237, 240, 246, 247, 248, 251, 257, 258, + 264, 265, 266, 267, 268, 269, 270, 271, 274, 275, + 280, 281, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 302, 304, 310, 315, 320, 326, 337, + 338, 343, 344, 350, 351, 356, 357, 361, 362, 367, + 368, 369, 374, 375, 380, 382, 383, 388, 389, 394, + 395, 396, 401, 402, 403, 404, 405, 406, 407, 408, + 409, 410, 411, 417, 418, 419, 420, 421, 422, 423, + 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, + 434, 438, 439, 444, 445, 446, 447, 451, 452, 456, + 457, 462, 463, 464, 465, 466, 467, 468, 480, 486, + 487, 492, 493, 494, 495, 496, 497, 498, 499, 500, + 505, 506, 507, 508, 509, 510, 517, 524, 525, 526, + 527, 528, 529, 530, 534, 535, 536, 537, 542, 543, + 544, 545, 550, 551, 552, 553, 554, 559, 560, 561, + 562, 563, 564, 565, 566, 567 }; #endif @@ -654,18 +649,18 @@ static const char *const yytname[] = "$end", "error", "$undefined", "TRUE", "FALSE", "SEMICOLON", "AT", "DOLLAR", "IF", "ELSE", "FOR", "DO", "WHILE", "RETURN", "BREAK", "CONTINUE", "LCURLY", "RCURLY", "LSQUARE", "RSQUARE", "STRING", "DOUBLE", - "FLOAT", "LONG", "INT", "SHORT", "BOOL", "VOID", "VEC2I", "VEC2F", - "VEC2D", "VEC3I", "VEC3F", "VEC3D", "VEC4I", "VEC4F", "VEC4D", "F_AT", - "I_AT", "V_AT", "S_AT", "MAT3F", "MAT3D", "MAT4F", "MAT4D", "M3F_AT", - "M4F_AT", "F_DOLLAR", "I_DOLLAR", "V_DOLLAR", "S_DOLLAR", "DOT_X", - "DOT_Y", "DOT_Z", "L_SHORT", "L_INT", "L_LONG", "L_FLOAT", "L_DOUBLE", - "L_STRING", "IDENTIFIER", "COMMA", "QUESTION", "COLON", "EQUALS", - "PLUSEQUALS", "MINUSEQUALS", "MULTIPLYEQUALS", "DIVIDEEQUALS", - "MODULOEQUALS", "BITANDEQUALS", "BITXOREQUALS", "BITOREQUALS", - "SHIFTLEFTEQUALS", "SHIFTRIGHTEQUALS", "OR", "AND", "BITOR", "BITXOR", - "BITAND", "EQUALSEQUALS", "NOTEQUALS", "MORETHAN", "LESSTHAN", - "MORETHANOREQUAL", "LESSTHANOREQUAL", "SHIFTLEFT", "SHIFTRIGHT", "PLUS", - "MINUS", "MULTIPLY", "DIVIDE", "MODULO", "NOT", "BITNOT", "PLUSPLUS", + "FLOAT", "INT32", "INT64", "BOOL", "VEC2I", "VEC2F", "VEC2D", "VEC3I", + "VEC3F", "VEC3D", "VEC4I", "VEC4F", "VEC4D", "F_AT", "I_AT", "V_AT", + "S_AT", "I16_AT", "MAT3F", "MAT3D", "MAT4F", "MAT4D", "M3F_AT", "M4F_AT", + "F_DOLLAR", "I_DOLLAR", "V_DOLLAR", "S_DOLLAR", "DOT_X", "DOT_Y", + "DOT_Z", "L_INT32", "L_INT64", "L_FLOAT", "L_DOUBLE", "L_STRING", + "IDENTIFIER", "COMMA", "QUESTION", "COLON", "EQUALS", "PLUSEQUALS", + "MINUSEQUALS", "MULTIPLYEQUALS", "DIVIDEEQUALS", "MODULOEQUALS", + "BITANDEQUALS", "BITXOREQUALS", "BITOREQUALS", "SHIFTLEFTEQUALS", + "SHIFTRIGHTEQUALS", "OR", "AND", "BITOR", "BITXOR", "BITAND", + "EQUALSEQUALS", "NOTEQUALS", "MORETHAN", "LESSTHAN", "MORETHANOREQUAL", + "LESSTHANOREQUAL", "SHIFTLEFT", "SHIFTRIGHT", "PLUS", "MINUS", + "MULTIPLY", "DIVIDE", "MODULO", "UMINUS", "NOT", "BITNOT", "PLUSPLUS", "MINUSMINUS", "LPARENS", "RPARENS", "LOWER_THAN_ELSE", "$accept", "tree", "body", "block", "statement", "expressions", "comma_operator", "expression", "declaration", "declaration_list", "declarations", @@ -693,7 +688,7 @@ static const yytype_uint16 yytoknum[] = 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, - 345, 346, 347, 348, 349, 350, 351, 352, 353, 354 + 345, 346, 347, 348, 349, 350, 351, 352, 353 }; # endif @@ -711,33 +706,33 @@ static const yytype_uint16 yytoknum[] = STATE-NUM. */ static const yytype_int16 yypact[] = { - 528, -225, -225, -225, -57, -53, -76, -68, 528, -63, - 45, 52, 53, 338, -225, -225, -225, -225, -225, -225, - -225, -225, -225, -225, -225, -225, -225, -225, -225, -225, - 19, 20, 33, 34, -225, -225, -225, -225, 35, 40, - 65, 67, 84, 85, -225, -225, -225, -225, -225, -225, - -37, 718, 718, 718, 718, 796, 796, 718, 99, 528, - -225, -225, 141, 86, 232, 107, 108, 165, -225, -225, - -55, -225, -225, -225, -225, -225, -225, -225, 536, -225, - 31, -225, -225, -225, -225, 21, -225, 74, -225, -225, - -225, 718, 718, -225, -225, 160, 718, -225, -225, -225, - -225, 433, -15, -225, -225, -225, -225, -225, -225, -225, - -225, -225, -225, 242, 718, -59, 4, -59, -225, -225, - -225, -225, 167, -225, -225, 76, -225, -225, -225, -225, - 718, 718, 623, 718, 718, 718, 718, 718, 718, 718, - 718, 718, 718, 718, 718, 718, 718, 718, 718, 718, - 718, 115, 116, -225, 718, -225, 718, 718, 718, 718, - 718, 718, 718, 718, 718, 718, 718, -225, -225, 718, - -225, -225, -225, 117, 138, 136, 718, 103, -225, -225, - 199, 109, -225, -225, 110, -225, -225, -225, 422, -15, - 232, -225, 422, 422, 718, 327, 73, 610, 701, 778, - 763, 507, 507, -72, -72, -72, -72, -1, -1, -59, - -59, -225, -225, -225, 143, 145, 422, 422, 422, 422, - 422, 422, 422, 422, 422, 422, 422, 422, -14, -225, - -225, 718, 140, 528, 718, 718, 528, 422, 718, 718, - 718, -225, 718, 422, -225, 196, -225, 208, 139, -225, - 422, 422, 422, 105, 528, 718, -225, -225, -225, -225, - 142, 528, -225 + 525, -225, -225, -225, -54, -51, -85, -62, 525, -49, + 48, 72, 83, 337, -225, -225, -225, -225, -225, -225, + -225, -225, -225, -225, -225, -225, -225, -225, -225, 31, + 34, 35, 36, 40, -225, -225, -225, -225, 41, 64, + 65, 66, 85, 86, -225, -225, -225, -225, -225, -6, + 713, 713, 713, 713, 790, 790, 713, 145, 525, -225, + -225, 154, 87, 233, 102, 103, 158, -225, -225, -56, + -225, -225, -225, -225, -225, -225, -225, 533, -225, -8, + -225, -225, -225, -225, 20, -225, 70, -225, -225, -225, + 713, 713, -225, -225, 152, 713, -225, -225, -225, -225, + 431, -11, -225, -225, -225, -225, -225, -225, -225, -225, + -225, -225, -225, 242, 713, -57, 22, -225, -225, -225, + -225, -225, 161, -225, -225, 73, -225, -225, -225, -225, + 713, 713, 619, 713, 713, 713, 713, 713, 713, 713, + 713, 713, 713, 713, 713, 713, 713, 713, 713, 713, + 713, 111, 113, -225, 713, -225, 713, 713, 713, 713, + 713, 713, 713, 713, 713, 713, 713, -225, -225, 713, + -225, -225, -225, 114, 115, 112, 713, 78, -225, -225, + 171, 81, -225, -225, 106, -225, -225, -225, 421, -11, + 233, -225, 421, 421, 713, 327, 607, 697, 773, 787, + 808, 68, 68, -70, -70, -70, -70, -7, -7, -57, + -57, -225, -225, -225, 116, 137, 421, 421, 421, 421, + 421, 421, 421, 421, 421, 421, 421, 421, -14, -225, + -225, 713, 108, 525, 713, 713, 525, 421, 713, 713, + 713, -225, 713, 421, -225, 195, -225, 201, 110, -225, + 421, 421, 421, 141, 525, 713, -225, -225, -225, -225, + 135, 525, -225 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. @@ -746,18 +741,18 @@ static const yytype_int16 yypact[] = static const yytype_uint8 yydefact[] = { 2, 132, 133, 17, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 137, 147, 146, 145, 144, 143, - 142, 148, 149, 150, 151, 152, 153, 154, 155, 156, + 0, 0, 0, 0, 137, 146, 145, 143, 144, 142, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 0, 0, 0, 0, 0, 138, 139, 140, 141, 0, 0, - 0, 0, 0, 0, 126, 127, 128, 129, 130, 131, - 125, 0, 0, 0, 0, 0, 0, 0, 0, 3, - 7, 6, 0, 19, 18, 39, 40, 0, 12, 13, - 0, 26, 25, 22, 24, 23, 102, 29, 31, 30, - 101, 109, 28, 110, 27, 0, 136, 134, 135, 118, - 124, 0, 51, 41, 42, 0, 0, 14, 15, 16, - 9, 0, 19, 113, 112, 114, 115, 116, 117, 121, - 120, 122, 123, 0, 0, 93, 0, 94, 96, 95, - 125, 97, 0, 134, 98, 0, 1, 5, 4, 10, + 0, 0, 0, 0, 127, 128, 129, 130, 131, 126, + 0, 0, 0, 0, 0, 0, 0, 0, 3, 7, + 6, 0, 19, 18, 39, 40, 0, 12, 13, 0, + 26, 25, 22, 24, 23, 102, 29, 31, 30, 101, + 109, 28, 110, 27, 0, 136, 134, 135, 119, 125, + 0, 51, 41, 42, 0, 0, 14, 15, 16, 9, + 0, 19, 114, 113, 115, 116, 112, 117, 118, 122, + 121, 123, 124, 0, 0, 93, 0, 94, 96, 95, + 126, 97, 0, 134, 98, 0, 1, 5, 4, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 0, 60, 0, 0, 0, 0, @@ -768,7 +763,7 @@ static const yytype_uint8 yydefact[] = 80, 85, 86, 87, 88, 89, 90, 78, 79, 73, 74, 75, 76, 77, 36, 38, 58, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 0, 111, - 119, 0, 0, 0, 48, 0, 0, 92, 0, 0, + 120, 0, 0, 0, 48, 0, 0, 92, 0, 0, 0, 106, 0, 34, 61, 43, 47, 0, 0, 56, 91, 35, 37, 0, 0, 53, 55, 107, 44, 52, 0, 0, 54 @@ -777,19 +772,19 @@ static const yytype_uint8 yydefact[] = /* YYPGOTO[NTERM-NUM]. */ static const yytype_int16 yypgoto[] = { - -225, -225, 226, 38, 39, -56, 9, -28, -92, -225, - 149, -224, -225, -193, -225, -225, -225, -225, -225, -225, - -225, -225, -225, -225, -225, -225, -11, -225, -225, -225, - -225, -225, -225, 0, -225, -2, -225 + -225, -225, 199, 38, 39, -55, 12, -29, -93, -225, + 117, -224, -225, -185, -225, -225, -225, -225, -225, -225, + -225, -225, -225, -225, -225, -225, 2, -225, -225, -225, + -225, -225, -225, 0, -225, 32, -225 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { - -1, 58, 59, 93, 94, 62, 63, 64, 65, 66, - 67, 95, 68, 184, 247, 180, 260, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 84, 116, 86, 87, 88 + -1, 57, 58, 92, 93, 61, 62, 63, 64, 65, + 66, 94, 67, 184, 247, 180, 260, 68, 69, 70, + 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + 81, 82, 83, 116, 85, 86, 87 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If @@ -797,190 +792,190 @@ static const yytype_int16 yydefgoto[] = number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint16 yytable[] = { - 85, 125, 186, 89, 183, 241, 154, 90, 85, 245, - 173, 174, 249, 85, 144, 145, 146, 147, 148, 149, - 150, 91, 102, 115, 117, 118, 119, 173, 174, 92, - 258, 148, 149, 150, 96, 177, 178, 262, 60, 61, - 182, 246, 248, 155, 121, 124, 130, 242, 132, 169, - 97, 60, 61, 123, 123, 122, 122, 98, 99, 85, - 113, 133, 134, 135, 136, 137, 138, 139, 140, 141, - 142, 143, 144, 145, 146, 147, 148, 149, 150, 103, - 104, 175, 170, 171, 172, 188, 190, 146, 147, 148, - 149, 150, 85, 105, 106, 107, 85, 127, 128, 126, - 108, 85, 192, 193, 195, 196, 197, 198, 199, 200, - 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, - 211, 212, 213, 189, 257, 109, 216, 110, 217, 218, - 219, 220, 221, 222, 223, 224, 225, 226, 227, 127, - 128, 228, 183, 183, 111, 112, 129, 130, 232, 134, - 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, - 145, 146, 147, 148, 149, 150, 237, 132, 151, 152, - 153, 176, 181, 173, 191, 214, 215, 229, 182, 182, + 84, 125, 183, 154, 88, 241, 186, 89, 84, 245, + 169, 90, 249, 84, 144, 145, 146, 147, 148, 149, + 150, 115, 117, 118, 119, 101, 173, 174, 173, 174, + 258, 148, 149, 150, 91, 177, 178, 262, 59, 60, + 182, 155, 170, 171, 172, 242, 132, 95, 130, 246, + 248, 59, 60, 96, 122, 122, 121, 124, 84, 133, + 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, + 144, 145, 146, 147, 148, 149, 150, 97, 175, 146, + 147, 148, 149, 150, 188, 190, 123, 123, 98, 102, + 113, 84, 103, 104, 105, 84, 127, 128, 106, 107, + 84, 192, 193, 195, 196, 197, 198, 199, 200, 201, + 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, + 212, 213, 108, 109, 110, 216, 189, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 127, 128, + 228, 183, 183, 111, 112, 126, 130, 232, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 129, + 257, 151, 152, 153, 181, 237, 176, 173, 132, 214, + 191, 215, 229, 230, 231, 233, 234, 235, 239, 182, + 182, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 240, + 259, 132, 243, 236, 254, 244, 255, 256, 179, 250, + 251, 252, 100, 253, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 261, 84, 84, 84, 84, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 2, 0, 4, 5, + 0, 0, 0, 0, 84, 0, 0, 0, 114, 0, + 0, 84, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, + 42, 43, 131, 132, 0, 44, 45, 46, 47, 48, + 49, 0, 0, 0, 0, 0, 133, 134, 135, 136, + 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 0, 0, 0, 0, 50, 51, + 0, 0, 0, 0, 52, 53, 54, 55, 56, 187, + 1, 2, 3, 4, 5, 6, 0, 7, 8, 9, + 10, 11, 12, 13, 99, 0, 0, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 37, 38, 39, 40, 41, 42, 43, 132, 238, 0, + 44, 45, 46, 47, 48, 49, 0, 0, 0, 0, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, - 143, 144, 145, 146, 147, 148, 149, 150, 230, 259, - 231, 233, 132, 243, 234, 254, 235, 239, 236, 240, - 250, 251, 252, 255, 253, 133, 134, 135, 136, 137, - 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, - 148, 149, 150, 85, 85, 85, 85, 256, 244, 101, - 261, 179, 0, 0, 0, 1, 2, 0, 4, 5, - 0, 0, 0, 0, 85, 0, 0, 0, 114, 0, - 0, 85, 14, 15, 16, 17, 18, 19, 20, 0, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, - 41, 42, 43, 131, 132, 0, 44, 45, 46, 47, - 48, 49, 50, 0, 0, 0, 0, 133, 134, 135, - 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, - 146, 147, 148, 149, 150, 0, 0, 0, 0, 0, - 51, 52, 0, 0, 0, 53, 54, 55, 56, 57, - 187, 1, 2, 3, 4, 5, 6, 0, 7, 8, - 9, 10, 11, 12, 13, 100, 0, 0, 14, 15, - 16, 17, 18, 19, 20, 0, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 132, - 238, 0, 44, 45, 46, 47, 48, 49, 50, 0, - 0, 0, 133, 134, 135, 136, 137, 138, 139, 140, - 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 0, 0, 0, 0, 0, 0, 51, 52, 0, 0, - 0, 53, 54, 55, 56, 57, 1, 2, 3, 4, - 5, 6, 0, 7, 8, 9, 10, 11, 12, 13, - 185, 0, 0, 14, 15, 16, 17, 18, 19, 20, - 0, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 132, 0, 0, 44, 45, 46, - 47, 48, 49, 50, 0, 0, 0, 133, 134, 135, - 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, - 146, 147, 148, 149, 150, 0, 0, 0, 0, 0, - 0, 51, 52, 0, 0, 0, 53, 54, 55, 56, - 57, 1, 2, 3, 4, 5, 6, 0, 7, 8, - 9, 10, 11, 12, 13, 0, 0, 0, 14, 15, - 16, 17, 18, 19, 20, 0, 21, 22, 23, 24, + 143, 144, 145, 146, 147, 148, 149, 150, 0, 0, + 0, 0, 0, 50, 51, 0, 0, 0, 0, 52, + 53, 54, 55, 56, 1, 2, 3, 4, 5, 6, + 0, 7, 8, 9, 10, 11, 12, 13, 185, 0, + 0, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 132, 0, 0, 44, 45, 46, 47, 48, 49, + 0, 0, 0, 0, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 0, 0, 0, 0, 0, 50, 51, 0, + 0, 0, 0, 52, 53, 54, 55, 56, 1, 2, + 3, 4, 5, 6, 0, 7, 8, 9, 10, 11, + 12, 13, 0, 0, 0, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 0, 0, 0, 44, 45, + 46, 47, 48, 49, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 156, 157, 158, 159, 160, + 161, 162, 163, 164, 165, 166, 0, 0, 0, 0, + 0, 50, 51, 0, 0, 0, 0, 52, 53, 54, + 55, 56, 1, 2, 0, 4, 5, 167, 168, 0, + 0, 0, 0, 0, 0, 114, 0, 0, 0, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 0, - 0, 0, 44, 45, 46, 47, 48, 49, 50, 140, - 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, - 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, - 166, 0, 0, 0, 0, 0, 51, 52, 0, 0, - 0, 53, 54, 55, 56, 57, 1, 2, 0, 4, - 5, 167, 168, 0, 0, 0, 0, 0, 0, 114, + 0, 0, 44, 45, 46, 47, 48, 49, 0, 0, + 194, 134, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 0, 0, + 0, 0, 0, 0, 0, 50, 51, 0, 0, 0, + 0, 52, 53, 54, 55, 56, 1, 2, 0, 4, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 114, 0, 0, 0, 14, 15, 16, 17, 18, 19, 20, - 0, 21, 22, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 0, 0, 0, 44, 45, 46, - 47, 48, 49, 50, 0, 0, 194, 135, 136, 137, - 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, - 148, 149, 150, 0, 0, 0, 0, 0, 0, 0, - 0, 51, 52, 0, 0, 0, 53, 54, 55, 56, - 57, 1, 2, 0, 4, 5, 0, 0, 0, 0, - 0, 0, 0, 0, 114, 0, 0, 0, 14, 15, - 16, 17, 18, 19, 20, 0, 21, 22, 23, 24, - 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, - 35, 36, 37, 38, 39, 40, 41, 42, 43, 0, - 0, 0, 44, 45, 46, 47, 48, 49, 50, 136, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 0, 0, 0, 44, 45, 46, 47, + 48, 49, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 0, 0, + 0, 0, 0, 0, 0, 0, 4, 0, 0, 50, + 51, 0, 0, 0, 0, 52, 53, 54, 55, 56, + 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, + 34, 35, 36, 37, 38, 39, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 120, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, - 147, 148, 149, 150, 0, 0, 0, 0, 0, 0, - 0, 0, 4, 0, 0, 0, 51, 52, 0, 0, - 0, 53, 54, 55, 56, 57, 14, 15, 16, 17, - 18, 19, 20, 0, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 138, 139, 140, 141, 142, 143, 144, - 145, 146, 147, 148, 149, 150, 120, 137, 138, 139, - 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, - 150, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 55, 56 + 147, 148, 149, 150, 137, 138, 139, 140, 141, 142, + 143, 144, 145, 146, 147, 148, 149, 150, 0, 0, + 0, 0, 0, 0, 54, 55, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150 }; static const yytype_int16 yycheck[] = { - 0, 57, 17, 60, 96, 19, 61, 60, 8, 233, - 6, 7, 236, 13, 86, 87, 88, 89, 90, 91, - 92, 97, 13, 51, 52, 53, 54, 6, 7, 97, - 254, 90, 91, 92, 97, 91, 92, 261, 0, 0, - 96, 234, 235, 98, 55, 56, 61, 61, 62, 18, - 5, 13, 13, 55, 56, 55, 56, 5, 5, 59, - 97, 75, 76, 77, 78, 79, 80, 81, 82, 83, - 84, 85, 86, 87, 88, 89, 90, 91, 92, 60, - 60, 60, 51, 52, 53, 113, 114, 88, 89, 90, - 91, 92, 92, 60, 60, 60, 96, 59, 59, 0, - 60, 101, 130, 131, 132, 133, 134, 135, 136, 137, - 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, - 148, 149, 150, 114, 19, 60, 154, 60, 156, 157, - 158, 159, 160, 161, 162, 163, 164, 165, 166, 101, - 101, 169, 234, 235, 60, 60, 5, 61, 176, 76, - 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, - 87, 88, 89, 90, 91, 92, 194, 62, 61, 61, - 5, 97, 12, 6, 98, 60, 60, 60, 234, 235, - 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, - 85, 86, 87, 88, 89, 90, 91, 92, 60, 255, - 64, 98, 62, 231, 5, 9, 97, 64, 98, 64, - 238, 239, 240, 5, 242, 75, 76, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, 233, 234, 235, 236, 98, 98, 13, - 98, 92, -1, -1, -1, 3, 4, -1, 6, 7, + 0, 56, 95, 59, 58, 19, 17, 58, 8, 233, + 18, 96, 236, 13, 84, 85, 86, 87, 88, 89, + 90, 50, 51, 52, 53, 13, 6, 7, 6, 7, + 254, 88, 89, 90, 96, 90, 91, 261, 0, 0, + 95, 97, 50, 51, 52, 59, 60, 96, 59, 234, + 235, 13, 13, 5, 54, 55, 54, 55, 58, 73, + 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 5, 58, 86, + 87, 88, 89, 90, 113, 114, 54, 55, 5, 58, + 96, 91, 58, 58, 58, 95, 58, 58, 58, 58, + 100, 130, 131, 132, 133, 134, 135, 136, 137, 138, + 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, + 149, 150, 58, 58, 58, 154, 114, 156, 157, 158, + 159, 160, 161, 162, 163, 164, 165, 166, 100, 100, + 169, 234, 235, 58, 58, 0, 59, 176, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 5, + 19, 59, 59, 5, 12, 194, 96, 6, 60, 58, + 97, 58, 58, 58, 62, 97, 5, 96, 62, 234, + 235, 73, 74, 75, 76, 77, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90, 62, + 255, 60, 231, 97, 9, 97, 5, 97, 91, 238, + 239, 240, 13, 242, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, 97, 233, 234, 235, 236, -1, -1, -1, + -1, -1, -1, -1, -1, 3, 4, -1, 6, 7, -1, -1, -1, -1, 254, -1, -1, -1, 16, -1, - -1, 261, 20, 21, 22, 23, 24, 25, 26, -1, + -1, 261, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 61, 62, -1, 54, 55, 56, 57, - 58, 59, 60, -1, -1, -1, -1, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, -1, -1, -1, -1, -1, - 88, 89, -1, -1, -1, 93, 94, 95, 96, 97, - 98, 3, 4, 5, 6, 7, 8, -1, 10, 11, - 12, 13, 14, 15, 16, 17, -1, -1, 20, 21, - 22, 23, 24, 25, 26, -1, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, 62, - 63, -1, 54, 55, 56, 57, 58, 59, 60, -1, - -1, -1, 75, 76, 77, 78, 79, 80, 81, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - -1, -1, -1, -1, -1, -1, 88, 89, -1, -1, - -1, 93, 94, 95, 96, 97, 3, 4, 5, 6, - 7, 8, -1, 10, 11, 12, 13, 14, 15, 16, - 17, -1, -1, 20, 21, 22, 23, 24, 25, 26, - -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 49, 50, 62, -1, -1, 54, 55, 56, - 57, 58, 59, 60, -1, -1, -1, 75, 76, 77, - 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, - 88, 89, 90, 91, 92, -1, -1, -1, -1, -1, - -1, 88, 89, -1, -1, -1, 93, 94, 95, 96, - 97, 3, 4, 5, 6, 7, 8, -1, 10, 11, - 12, 13, 14, 15, 16, -1, -1, -1, 20, 21, - 22, 23, 24, 25, 26, -1, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, - -1, -1, 54, 55, 56, 57, 58, 59, 60, 82, - 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, - 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, -1, -1, -1, -1, -1, 88, 89, -1, -1, - -1, 93, 94, 95, 96, 97, 3, 4, -1, 6, - 7, 95, 96, -1, -1, -1, -1, -1, -1, 16, + 48, 49, 59, 60, -1, 53, 54, 55, 56, 57, + 58, -1, -1, -1, -1, -1, 73, 74, 75, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, -1, -1, -1, -1, 86, 87, + -1, -1, -1, -1, 92, 93, 94, 95, 96, 97, + 3, 4, 5, 6, 7, 8, -1, 10, 11, 12, + 13, 14, 15, 16, 17, -1, -1, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 60, 61, -1, + 53, 54, 55, 56, 57, 58, -1, -1, -1, -1, + 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, 86, 87, -1, -1, -1, -1, 92, + 93, 94, 95, 96, 3, 4, 5, 6, 7, 8, + -1, 10, 11, 12, 13, 14, 15, 16, 17, -1, + -1, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, + 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 60, -1, -1, 53, 54, 55, 56, 57, 58, + -1, -1, -1, -1, 73, 74, 75, 76, 77, 78, + 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, + 89, 90, -1, -1, -1, -1, -1, 86, 87, -1, + -1, -1, -1, 92, 93, 94, 95, 96, 3, 4, + 5, 6, 7, 8, -1, 10, 11, 12, 13, 14, + 15, 16, -1, -1, -1, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, -1, -1, -1, 53, 54, + 55, 56, 57, 58, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 62, 63, 64, 65, 66, + 67, 68, 69, 70, 71, 72, -1, -1, -1, -1, + -1, 86, 87, -1, -1, -1, -1, 92, 93, 94, + 95, 96, 3, 4, -1, 6, 7, 94, 95, -1, + -1, -1, -1, -1, -1, 16, -1, -1, -1, 20, + 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, -1, + -1, -1, 53, 54, 55, 56, 57, 58, -1, -1, + 61, 74, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, -1, -1, 86, 87, -1, -1, -1, + -1, 92, 93, 94, 95, 96, 3, 4, -1, 6, + 7, -1, -1, -1, -1, -1, -1, -1, -1, 16, -1, -1, -1, 20, 21, 22, 23, 24, 25, 26, - -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, + 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 49, 50, -1, -1, -1, 54, 55, 56, - 57, 58, 59, 60, -1, -1, 63, 77, 78, 79, - 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, - 90, 91, 92, -1, -1, -1, -1, -1, -1, -1, - -1, 88, 89, -1, -1, -1, 93, 94, 95, 96, - 97, 3, 4, -1, 6, 7, -1, -1, -1, -1, - -1, -1, -1, -1, 16, -1, -1, -1, 20, 21, - 22, 23, 24, 25, 26, -1, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 42, 43, 44, 45, 46, 47, 48, 49, 50, -1, - -1, -1, 54, 55, 56, 57, 58, 59, 60, 78, - 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, - 89, 90, 91, 92, -1, -1, -1, -1, -1, -1, - -1, -1, 6, -1, -1, -1, 88, 89, -1, -1, - -1, 93, 94, 95, 96, 97, 20, 21, 22, 23, - 24, 25, 26, -1, 28, 29, 30, 31, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, - 44, 45, 46, 80, 81, 82, 83, 84, 85, 86, - 87, 88, 89, 90, 91, 92, 60, 79, 80, 81, - 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, - 92, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 95, 96 + 47, 48, 49, -1, -1, -1, 53, 54, 55, 56, + 57, 58, 75, 76, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, -1, -1, -1, 6, -1, -1, 86, + 87, -1, -1, -1, -1, 92, 93, 94, 95, 96, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, + 40, 41, 42, 43, 44, 45, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 58, 76, + 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 77, 78, 79, 80, 81, 82, + 83, 84, 85, 86, 87, 88, 89, 90, -1, -1, + -1, -1, -1, -1, 94, 95, 78, 79, 80, 81, + 82, 83, 84, 85, 86, 87, 88, 89, 90 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing @@ -989,52 +984,52 @@ static const yytype_uint8 yystos[] = { 0, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15, 16, 20, 21, 22, 23, 24, 25, - 26, 28, 29, 30, 31, 32, 33, 34, 35, 36, - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, - 47, 48, 49, 50, 54, 55, 56, 57, 58, 59, - 60, 88, 89, 93, 94, 95, 96, 97, 101, 102, - 103, 104, 105, 106, 107, 108, 109, 110, 112, 117, + 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, + 46, 47, 48, 49, 53, 54, 55, 56, 57, 58, + 86, 87, 92, 93, 94, 95, 96, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 111, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, - 128, 129, 130, 131, 132, 133, 134, 135, 136, 60, - 60, 97, 97, 103, 104, 111, 97, 5, 5, 5, - 17, 102, 106, 60, 60, 60, 60, 60, 60, 60, - 60, 60, 60, 97, 16, 107, 133, 107, 107, 107, - 60, 126, 133, 135, 126, 105, 0, 103, 104, 5, - 61, 61, 62, 75, 76, 77, 78, 79, 80, 81, - 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, - 92, 61, 61, 5, 61, 98, 64, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 95, 96, 18, - 51, 52, 53, 6, 7, 60, 97, 105, 105, 110, - 115, 12, 105, 108, 113, 17, 17, 98, 107, 106, - 107, 98, 107, 107, 63, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 107, 60, 60, 107, 107, 107, 107, - 107, 107, 107, 107, 107, 107, 107, 107, 107, 60, - 60, 64, 107, 98, 5, 97, 98, 107, 63, 64, - 64, 19, 61, 107, 98, 111, 113, 114, 113, 111, - 107, 107, 107, 107, 9, 5, 98, 19, 111, 105, - 116, 98, 111 + 128, 129, 130, 131, 132, 133, 134, 135, 58, 58, + 96, 96, 102, 103, 110, 96, 5, 5, 5, 17, + 101, 105, 58, 58, 58, 58, 58, 58, 58, 58, + 58, 58, 58, 96, 16, 106, 132, 106, 106, 106, + 58, 125, 132, 134, 125, 104, 0, 102, 103, 5, + 59, 59, 60, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 90, 59, 59, 5, 59, 97, 62, 63, 64, 65, + 66, 67, 68, 69, 70, 71, 72, 94, 95, 18, + 50, 51, 52, 6, 7, 58, 96, 104, 104, 109, + 114, 12, 104, 107, 112, 17, 17, 97, 106, 105, + 106, 97, 106, 106, 61, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 106, 58, 58, 106, 106, 106, 106, + 106, 106, 106, 106, 106, 106, 106, 106, 106, 58, + 58, 62, 106, 97, 5, 96, 97, 106, 61, 62, + 62, 19, 59, 106, 97, 110, 112, 113, 112, 110, + 106, 106, 106, 106, 9, 5, 97, 19, 110, 104, + 115, 97, 110 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { - 0, 100, 101, 101, 102, 102, 102, 102, 103, 103, - 104, 104, 104, 104, 104, 104, 104, 104, 105, 105, - 106, 106, 107, 107, 107, 107, 107, 107, 107, 107, - 107, 107, 107, 108, 108, 109, 109, 109, 109, 110, - 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, - 115, 115, 116, 116, 117, 117, 117, 118, 118, 119, - 119, 119, 120, 120, 120, 120, 120, 120, 120, 120, - 120, 120, 120, 121, 121, 121, 121, 121, 121, 121, - 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, - 121, 122, 122, 123, 123, 123, 123, 124, 124, 125, - 125, 126, 126, 126, 126, 126, 126, 126, 127, 128, - 128, 129, 129, 129, 129, 129, 129, 129, 129, 130, - 130, 130, 130, 130, 130, 131, 132, 132, 132, 132, - 132, 132, 132, 132, 133, 133, 133, 133, 134, 134, - 134, 134, 135, 135, 135, 135, 135, 135, 136, 136, - 136, 136, 136, 136, 136, 136, 136 + 0, 99, 100, 100, 101, 101, 101, 101, 102, 102, + 103, 103, 103, 103, 103, 103, 103, 103, 104, 104, + 105, 105, 106, 106, 106, 106, 106, 106, 106, 106, + 106, 106, 106, 107, 107, 108, 108, 108, 108, 109, + 109, 110, 110, 111, 111, 112, 112, 113, 113, 114, + 114, 114, 115, 115, 116, 116, 116, 117, 117, 118, + 118, 118, 119, 119, 119, 119, 119, 119, 119, 119, + 119, 119, 119, 120, 120, 120, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, 120, 120, + 120, 121, 121, 122, 122, 122, 122, 123, 123, 124, + 124, 125, 125, 125, 125, 125, 125, 125, 126, 127, + 127, 128, 128, 128, 128, 128, 128, 128, 128, 128, + 129, 129, 129, 129, 129, 129, 130, 131, 131, 131, + 131, 131, 131, 131, 132, 132, 132, 132, 133, 133, + 133, 133, 134, 134, 134, 134, 134, 135, 135, 135, + 135, 135, 135, 135, 135, 135 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ @@ -1051,11 +1046,11 @@ static const yytype_uint8 yyr2[] = 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 5, 4, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 4, 6, 3, 1, - 1, 3, 2, 2, 2, 2, 2, 2, 2, 3, - 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, + 1, 3, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1 + 1, 1, 1, 1, 1, 1 }; @@ -1550,259 +1545,253 @@ yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep, YYLTYPE *yylocatio YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN switch (yytype) { - case 54: /* L_SHORT */ + case 53: /* L_INT32 */ - { free(const_cast(((*yyvaluep).string))); } - - break; - - case 55: /* L_INT */ - - { free(const_cast(((*yyvaluep).string))); } + { } break; - case 56: /* L_LONG */ + case 54: /* L_INT64 */ - { free(const_cast(((*yyvaluep).string))); } + { } break; - case 57: /* L_FLOAT */ + case 55: /* L_FLOAT */ - { free(const_cast(((*yyvaluep).string))); } + { } break; - case 58: /* L_DOUBLE */ + case 56: /* L_DOUBLE */ - { free(const_cast(((*yyvaluep).string))); } + { } break; - case 59: /* L_STRING */ + case 57: /* L_STRING */ { free(const_cast(((*yyvaluep).string))); } break; - case 60: /* IDENTIFIER */ + case 58: /* IDENTIFIER */ { free(const_cast(((*yyvaluep).string))); } break; - case 101: /* tree */ + case 100: /* tree */ { } break; - case 102: /* body */ + case 101: /* body */ { delete ((*yyvaluep).block); } break; - case 103: /* block */ + case 102: /* block */ { delete ((*yyvaluep).block); } break; - case 104: /* statement */ + case 103: /* statement */ { delete ((*yyvaluep).statement); } break; - case 105: /* expressions */ + case 104: /* expressions */ { delete ((*yyvaluep).expression); } break; - case 106: /* comma_operator */ + case 105: /* comma_operator */ { for (auto& ptr : *((*yyvaluep).explist)) delete ptr; delete ((*yyvaluep).explist); } break; - case 107: /* expression */ + case 106: /* expression */ { delete ((*yyvaluep).expression); } break; - case 108: /* declaration */ + case 107: /* declaration */ { delete ((*yyvaluep).declare_local); } break; - case 109: /* declaration_list */ + case 108: /* declaration_list */ { delete ((*yyvaluep).statementlist); } break; - case 110: /* declarations */ + case 109: /* declarations */ { delete ((*yyvaluep).statement); } break; - case 111: /* block_or_statement */ + case 110: /* block_or_statement */ { delete ((*yyvaluep).block); } break; - case 112: /* conditional_statement */ + case 111: /* conditional_statement */ { delete ((*yyvaluep).statement); } break; - case 113: /* loop_condition */ + case 112: /* loop_condition */ { delete ((*yyvaluep).statement); } break; - case 114: /* loop_condition_optional */ + case 113: /* loop_condition_optional */ { delete ((*yyvaluep).statement); } break; - case 115: /* loop_init */ + case 114: /* loop_init */ { delete ((*yyvaluep).statement); } break; - case 116: /* loop_iter */ + case 115: /* loop_iter */ { delete ((*yyvaluep).expression); } break; - case 117: /* loop */ + case 116: /* loop */ { delete ((*yyvaluep).statement); } break; - case 118: /* function_start_expression */ + case 117: /* function_start_expression */ { delete ((*yyvaluep).function); } break; - case 119: /* function_call_expression */ + case 118: /* function_call_expression */ { delete ((*yyvaluep).expression); } break; - case 120: /* assign_expression */ + case 119: /* assign_expression */ { delete ((*yyvaluep).expression); } break; - case 121: /* binary_expression */ + case 120: /* binary_expression */ { delete ((*yyvaluep).expression); } break; - case 122: /* ternary_expression */ + case 121: /* ternary_expression */ { delete ((*yyvaluep).expression); } break; - case 123: /* unary_expression */ + case 122: /* unary_expression */ { delete ((*yyvaluep).expression); } break; - case 124: /* pre_crement */ + case 123: /* pre_crement */ { delete ((*yyvaluep).expression); } break; - case 125: /* post_crement */ + case 124: /* post_crement */ { delete ((*yyvaluep).expression); } break; - case 126: /* variable_reference */ + case 125: /* variable_reference */ { delete ((*yyvaluep).expression); } break; - case 127: /* array */ + case 126: /* array */ { delete ((*yyvaluep).expression); } break; - case 128: /* variable */ + case 127: /* variable */ { delete ((*yyvaluep).variable); } break; - case 129: /* attribute */ + case 128: /* attribute */ { delete ((*yyvaluep).attribute); } break; - case 130: /* external */ + case 129: /* external */ { delete ((*yyvaluep).external); } break; - case 131: /* local */ + case 130: /* local */ { delete ((*yyvaluep).local); } break; - case 132: /* literal */ + case 131: /* literal */ { delete ((*yyvaluep).value); } break; - case 133: /* type */ + case 132: /* type */ { } break; - case 134: /* matrix_type */ + case 133: /* matrix_type */ { } break; - case 135: /* scalar_type */ + case 134: /* scalar_type */ { } break; - case 136: /* vector_type */ + case 135: /* vector_type */ { } @@ -2782,115 +2771,115 @@ yyparse (openvdb::ax::ast::Tree** tree) case 112: - { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::INT); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::INT16); free(const_cast((yyvsp[0].string))); } break; case 113: - { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::INT32); free(const_cast((yyvsp[0].string))); } break; case 114: - { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::VEC3F); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } break; case 115: - { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::STRING); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::VEC3F); free(const_cast((yyvsp[0].string))); } break; case 116: - { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::MAT3F); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::STRING); free(const_cast((yyvsp[0].string))); } break; case 117: - { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::MAT4F); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::MAT3F); free(const_cast((yyvsp[0].string))); } break; case 118: - { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT, true); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::MAT4F); free(const_cast((yyvsp[0].string))); } break; case 119: - { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), static_cast((yyvsp[-2].index))); free(const_cast((yyvsp[0].string))); } + { (yyval.attribute) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT, true); free(const_cast((yyvsp[0].string))); } break; case 120: - { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::INT); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), static_cast((yyvsp[-2].index))); free(const_cast((yyvsp[0].string))); } break; case 121: - { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::INT32); free(const_cast((yyvsp[0].string))); } break; case 122: - { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::VEC3F); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } break; case 123: - { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::STRING); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::VEC3F); free(const_cast((yyvsp[0].string))); } break; case 124: - { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::STRING); free(const_cast((yyvsp[0].string))); } break; case 125: - { (yyval.local) = newNode(&(yyloc), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.external) = newNode(&(yyloc), (yyvsp[0].string), tokens::FLOAT); free(const_cast((yyvsp[0].string))); } break; case 126: - { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.local) = newNode(&(yyloc), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } break; case 127: - { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].index)); } break; case 128: - { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].index)); } break; case 129: - { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), static_cast((yyvsp[0].flt))); } break; case 130: - { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].string)); free(const_cast((yyvsp[0].string))); } + { (yyval.value) = newNode>(&(yylsp[0]), (yyvsp[0].flt)); } break; @@ -2968,83 +2957,77 @@ yyparse (openvdb::ax::ast::Tree** tree) case 143: - { (yyval.index) = tokens::SHORT; } + { (yyval.index) = tokens::INT32; } break; case 144: - { (yyval.index) = tokens::INT; } + { (yyval.index) = tokens::INT64; } break; case 145: - { (yyval.index) = tokens::LONG; } - - break; - - case 146: - { (yyval.index) = tokens::FLOAT; } break; - case 147: + case 146: { (yyval.index) = tokens::DOUBLE; } break; - case 148: + case 147: { (yyval.index) = tokens::VEC2I; } break; - case 149: + case 148: { (yyval.index) = tokens::VEC2F; } break; - case 150: + case 149: { (yyval.index) = tokens::VEC2D; } break; - case 151: + case 150: { (yyval.index) = tokens::VEC3I; } break; - case 152: + case 151: { (yyval.index) = tokens::VEC3F; } break; - case 153: + case 152: { (yyval.index) = tokens::VEC3D; } break; - case 154: + case 153: { (yyval.index) = tokens::VEC4I; } break; - case 155: + case 154: { (yyval.index) = tokens::VEC4F; } break; - case 156: + case 155: { (yyval.index) = tokens::VEC4D; } diff --git a/openvdb_ax/openvdb_ax/grammar/generated/axparser.h b/openvdb_ax/openvdb_ax/grammar/generated/axparser.h index 0e93737113..b751b09a6f 100644 --- a/openvdb_ax/openvdb_ax/grammar/generated/axparser.h +++ b/openvdb_ax/openvdb_ax/grammar/generated/axparser.h @@ -73,83 +73,82 @@ extern int axdebug; STRING = 275, DOUBLE = 276, FLOAT = 277, - LONG = 278, - INT = 279, - SHORT = 280, - BOOL = 281, - VOID = 282, - VEC2I = 283, - VEC2F = 284, - VEC2D = 285, - VEC3I = 286, - VEC3F = 287, - VEC3D = 288, - VEC4I = 289, - VEC4F = 290, - VEC4D = 291, - F_AT = 292, - I_AT = 293, - V_AT = 294, - S_AT = 295, - MAT3F = 296, - MAT3D = 297, - MAT4F = 298, - MAT4D = 299, - M3F_AT = 300, - M4F_AT = 301, - F_DOLLAR = 302, - I_DOLLAR = 303, - V_DOLLAR = 304, - S_DOLLAR = 305, - DOT_X = 306, - DOT_Y = 307, - DOT_Z = 308, - L_SHORT = 309, - L_INT = 310, - L_LONG = 311, - L_FLOAT = 312, - L_DOUBLE = 313, - L_STRING = 314, - IDENTIFIER = 315, - COMMA = 316, - QUESTION = 317, - COLON = 318, - EQUALS = 319, - PLUSEQUALS = 320, - MINUSEQUALS = 321, - MULTIPLYEQUALS = 322, - DIVIDEEQUALS = 323, - MODULOEQUALS = 324, - BITANDEQUALS = 325, - BITXOREQUALS = 326, - BITOREQUALS = 327, - SHIFTLEFTEQUALS = 328, - SHIFTRIGHTEQUALS = 329, - OR = 330, - AND = 331, - BITOR = 332, - BITXOR = 333, - BITAND = 334, - EQUALSEQUALS = 335, - NOTEQUALS = 336, - MORETHAN = 337, - LESSTHAN = 338, - MORETHANOREQUAL = 339, - LESSTHANOREQUAL = 340, - SHIFTLEFT = 341, - SHIFTRIGHT = 342, - PLUS = 343, - MINUS = 344, - MULTIPLY = 345, - DIVIDE = 346, - MODULO = 347, - NOT = 348, - BITNOT = 349, - PLUSPLUS = 350, - MINUSMINUS = 351, - LPARENS = 352, - RPARENS = 353, - LOWER_THAN_ELSE = 354 + INT32 = 278, + INT64 = 279, + BOOL = 280, + VEC2I = 281, + VEC2F = 282, + VEC2D = 283, + VEC3I = 284, + VEC3F = 285, + VEC3D = 286, + VEC4I = 287, + VEC4F = 288, + VEC4D = 289, + F_AT = 290, + I_AT = 291, + V_AT = 292, + S_AT = 293, + I16_AT = 294, + MAT3F = 295, + MAT3D = 296, + MAT4F = 297, + MAT4D = 298, + M3F_AT = 299, + M4F_AT = 300, + F_DOLLAR = 301, + I_DOLLAR = 302, + V_DOLLAR = 303, + S_DOLLAR = 304, + DOT_X = 305, + DOT_Y = 306, + DOT_Z = 307, + L_INT32 = 308, + L_INT64 = 309, + L_FLOAT = 310, + L_DOUBLE = 311, + L_STRING = 312, + IDENTIFIER = 313, + COMMA = 314, + QUESTION = 315, + COLON = 316, + EQUALS = 317, + PLUSEQUALS = 318, + MINUSEQUALS = 319, + MULTIPLYEQUALS = 320, + DIVIDEEQUALS = 321, + MODULOEQUALS = 322, + BITANDEQUALS = 323, + BITXOREQUALS = 324, + BITOREQUALS = 325, + SHIFTLEFTEQUALS = 326, + SHIFTRIGHTEQUALS = 327, + OR = 328, + AND = 329, + BITOR = 330, + BITXOR = 331, + BITAND = 332, + EQUALSEQUALS = 333, + NOTEQUALS = 334, + MORETHAN = 335, + LESSTHAN = 336, + MORETHANOREQUAL = 337, + LESSTHANOREQUAL = 338, + SHIFTLEFT = 339, + SHIFTRIGHT = 340, + PLUS = 341, + MINUS = 342, + MULTIPLY = 343, + DIVIDE = 344, + MODULO = 345, + UMINUS = 346, + NOT = 347, + BITNOT = 348, + PLUSPLUS = 349, + MINUSMINUS = 350, + LPARENS = 351, + RPARENS = 352, + LOWER_THAN_ELSE = 353 }; #endif @@ -165,6 +164,7 @@ union AXSTYPE const char* string; uint64_t index; + double flt; openvdb::ax::ast::Tree* tree; openvdb::ax::ast::ValueBase* value; diff --git a/openvdb_ax/openvdb_ax/test/CMakeLists.txt b/openvdb_ax/openvdb_ax/test/CMakeLists.txt index ed6002cfc9..e3ac195c96 100644 --- a/openvdb_ax/openvdb_ax/test/CMakeLists.txt +++ b/openvdb_ax/openvdb_ax/test/CMakeLists.txt @@ -38,6 +38,7 @@ set(TEST_SOURCE_FILES backend/TestLogger.cc backend/TestSymbolTable.cc backend/TestTypes.cc + compiler/TestAXRun.cc compiler/TestPointExecutable.cc compiler/TestVolumeExecutable.cc frontend/TestArrayPack.cc diff --git a/openvdb_ax/openvdb_ax/test/ast/TestPrinters.cc b/openvdb_ax/openvdb_ax/test/ast/TestPrinters.cc index 94d8eb6198..9c00e40cba 100644 --- a/openvdb_ax/openvdb_ax/test/ast/TestPrinters.cc +++ b/openvdb_ax/openvdb_ax/test/ast/TestPrinters.cc @@ -125,8 +125,8 @@ void TestPrinters::testReprint() // Test declarations os.str(""); - in = "bool a; short b,c; int d=0, e; long f; float g; double h, i=0;"; - expected = "bool a;\nshort b, c;\nint d = 0, e;\nlong f;\nfloat g;\ndouble h, i = 0;\n"; + in = "bool a; int b,c; int32 d=0, e; int64 f; float g; double h, i=0;"; + expected = "bool a;\nint32 b, c;\nint32 d = 0, e;\nint64 f;\nfloat g;\ndouble h, i = 0;\n"; tree = parse(in.c_str()); CPPUNIT_ASSERT(tree.get()); reprint(*tree, os, ""); @@ -153,7 +153,7 @@ void TestPrinters::testReprint() // Test attributes/externals os.str(""); in = "@a; $a; v@b; v$b; f@a; f$a; i@c; i$c; s@d; s$d;"; - expected = "float@a;\nfloat$a;\nvec3f@b;\nvec3f$b;\nfloat@a;\nfloat$a;\nint@c;\nint$c;\nstring@d;\nstring$d;\n"; + expected = "float@a;\nfloat$a;\nvec3f@b;\nvec3f$b;\nfloat@a;\nfloat$a;\nint32@c;\nint32$c;\nstring@d;\nstring$d;\n"; tree = parse(in.c_str()); CPPUNIT_ASSERT(tree.get()); reprint(*tree, os, ""); @@ -170,8 +170,8 @@ void TestPrinters::testReprint() // Test loops os.str(""); - in = "while (a) for (int b, c;;) do { d; } while (e)"; - expected = "while (a)\n{\nfor (int b, c; true; )\n{\ndo\n{\nd;\n}\nwhile (e)\n}\n}\n"; + in = "while (a) for (int32 b, c;;) do { d; } while (e)"; + expected = "while (a)\n{\nfor (int32 b, c; true; )\n{\ndo\n{\nd;\n}\nwhile (e)\n}\n}\n"; tree = parse(in.c_str()); CPPUNIT_ASSERT(tree.get()); reprint(*tree, os, ""); @@ -179,8 +179,8 @@ void TestPrinters::testReprint() // Test loops with indents os.str(""); - in = "while (a) for (int b, c;;) do { d; } while (e)"; - expected = " while (a)\n {\n for (int b, c; true; )\n {\n do\n {\n d;\n }\n while (e)\n }\n }\n"; + in = "while (a) for (int32 b, c;;) do { d; } while (e)"; + expected = " while (a)\n {\n for (int32 b, c; true; )\n {\n do\n {\n d;\n }\n while (e)\n }\n }\n"; tree = parse(in.c_str()); CPPUNIT_ASSERT(tree.get()); reprint(*tree, os, " "); diff --git a/openvdb_ax/openvdb_ax/test/ast/TestScanners.cc b/openvdb_ax/openvdb_ax/test/ast/TestScanners.cc index 074f2c19fe..261956ef4d 100644 --- a/openvdb_ax/openvdb_ax/test/ast/TestScanners.cc +++ b/openvdb_ax/openvdb_ax/test/ast/TestScanners.cc @@ -123,8 +123,8 @@ void TestScanners::testVisitNodeType() ++count; return true; }; - // "long@a;" - Node::Ptr node(new Attribute("a", CoreType::LONG)); + // "int64@a;" + Node::Ptr node(new Attribute("a", CoreType::INT64)); visitNodeType(*node, counter); CPPUNIT_ASSERT_EQUAL(size_t(1), count); diff --git a/openvdb_ax/openvdb_ax/test/backend/TestComputeGeneratorFailures.cc b/openvdb_ax/openvdb_ax/test/backend/TestComputeGeneratorFailures.cc index 5eb22402c1..e24272b972 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestComputeGeneratorFailures.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestComputeGeneratorFailures.cc @@ -1,14 +1,11 @@ // Copyright Contributors to the OpenVDB Project // SPDX-License-Identifier: MPL-2.0 -#include - #include "util.h" #include "../util.h" #include #include -#include #include #include #include @@ -43,75 +40,98 @@ static const std::vector tests { "double a; a | 1;", "double a; a ^ 1;", "double a; a & 1;", - "mat3f a,b; a % b;", "mat3d a,b; a % b;", - "mat3f a,b; a % b;", - "mat4d a,b; a % b;", - "vec2i a,b; a & b;", - "vec2f a,b; a & b;", - "vec2d a,b; a & b;", - "vec3i a,b; a & b;", - "vec3d a,b; a & b;", - "vec3f a,b; a & b;", - "vec4i a,b; a & b;", - "vec4f a,b; a & b;", - "vec4d a,b; a & b;", - "mat3f a,b; a & b;", "mat3d a,b; a & b;", - "mat3f a,b; a & b;", - "mat4d a,b; a & b;", - "vec2i a,b; a | b;", - "vec2f a,b; a | b;", - "vec2d a,b; a | b;", - "vec3i a,b; a | b;", - "vec3d a,b; a | b;", - "vec3f a,b; a | b;", - "vec4i a,b; a | b;", - "vec4f a,b; a | b;", - "vec4d a,b; a ^ b;", - "mat3f a,b; a ^ b;", + "mat3d a,b; a && b;", + "mat3d a,b; a << b;", + "mat3d a,b; a >> b;", "mat3d a,b; a ^ b;", + "mat3d a,b; a || b;", + "mat3f a,b; a % b;", + "mat3f a,b; a & b;", + "mat3f a,b; a && b;", + "mat3f a,b; a << b;", + "mat3f a,b; a >> b;", "mat3f a,b; a ^ b;", + "mat3f a,b; a || b;", + "mat4d a,b; a % b;", + "mat4d a,b; a & b;", + "mat4d a,b; a && b;", + "mat4d a,b; a << b;", + "mat4d a,b; a >> b;", "mat4d a,b; a ^ b;", - "vec2i a,b; a ^ b;", - "vec2f a,b; a ^ b;", + "mat4d a,b; a || b;", + "string a,b; a & b;", + "string a,b; a && b;", + "string a,b; a - b;", + "string a,b; a << b;", + "string a,b; a >> b;", + "string a,b; a ^ b;", + "string a,b; a | b;", + "string a,b; a || b;", + "vec2d a,b; a & b;", + "vec2d a,b; a && b;", + "vec2d a,b; a << b;", + "vec2d a,b; a >> b;", "vec2d a,b; a ^ b;", - "vec3i a,b; a ^ b;", - "vec3d a,b; a ^ b;", - "vec3f a,b; a ^ b;", - "vec4i a,b; a ^ b;", - "vec4f a,b; a ^ b;", - "vec4d a,b; a ^ b;", - "mat3f a,b; a ^ b;", - "mat3d a,b; a ^ b;", - "mat3f a,b; a ^ b;", - "mat4d a,b; a ^ b;", - "vec2i a,b; a << b;", + "vec2d a,b; a | b;", + "vec2d a,b; a || b;", + "vec2f a,b; a & b;", + "vec2f a,b; a && b;", "vec2f a,b; a << b;", - "vec2d a,b; a << b;", - "vec3i a,b; a << b;", - "vec3d a,b; a << b;", - "vec3f a,b; a << b;", - "vec4i a,b; a << b;", - "vec4f a,b; a << b;", - "vec4d a,b; a << b;", - "mat3f a,b; a << b;", - "mat3d a,b; a << b;", - "mat3f a,b; a << b;", - "mat4d a,b; a << b;", - "vec2i a,b; a >> b;", "vec2f a,b; a >> b;", - "vec2d a,b; a >> b;", - "vec3i a,b; a >> b;", + "vec2f a,b; a ^ b;", + "vec2f a,b; a | b;", + "vec2f a,b; a || b;", + "vec2i a,b; a & b;", + "vec2i a,b; a && b;", + "vec2i a,b; a << b;", + "vec2i a,b; a >> b;", + "vec2i a,b; a ^ b;", + "vec2i a,b; a | b;", + "vec2i a,b; a || b;", + "vec3d a,b; a & b;", + "vec3d a,b; a && b;", + "vec3d a,b; a << b;", "vec3d a,b; a >> b;", + "vec3d a,b; a ^ b;", + "vec3d a,b; a | b;", + "vec3d a,b; a || b;", + "vec3f a,b; a & b;", + "vec3f a,b; a && b;", + "vec3f a,b; a << b;", "vec3f a,b; a >> b;", - "vec4i a,b; a >> b;", - "vec4f a,b; a >> b;", + "vec3f a,b; a ^ b;", + "vec3f a,b; a | b;", + "vec3f a,b; a || b;", + "vec3i a,b; a & b;", + "vec3i a,b; a && b;", + "vec3i a,b; a << b;", + "vec3i a,b; a >> b;", + "vec3i a,b; a ^ b;", + "vec3i a,b; a | b;", + "vec3i a,b; a || b;", + "vec4d a,b; a & b;", + "vec4d a,b; a && b;", + "vec4d a,b; a << b;", "vec4d a,b; a >> b;", - "mat3f a,b; a >> b;", - "mat3d a,b; a >> b;", - "mat3f a,b; a >> b;", - "mat4d a,b; a >> b;", + "vec4d a,b; a ^ b;", + "vec4d a,b; a ^ b;", + "vec4d a,b; a || b;", + "vec4f a,b; a & b;", + "vec4f a,b; a && b;", + "vec4f a,b; a << b;", + "vec4f a,b; a >> b;", + "vec4f a,b; a ^ b;", + "vec4f a,b; a | b;", + "vec4f a,b; a || b;", + "vec4i a,b; a & b;", + "vec4i a,b; a && b;", + "vec4i a,b; a << b;", + "vec4i a,b; a >> b;", + "vec4i a,b; a ^ b;", + "vec4i a,b; a | b;", + "vec4i a,b; a || b;", /// invalid unary ops "vec2f a; !a;", "vec2d a; !a;", @@ -193,12 +213,74 @@ static const std::vector tests { // ternary "int a = true ? print(1) : print(2);", "true ? print(1) : 1;", + "mat4d a; a ? 0 : 1;", + "mat4f a; a ? 0 : 1;", + "string a; a ? 0 : 1;", + "vec2d a; a ? 0 : 1;", + "vec2f a; a ? 0 : 1;", + "vec2i a; a ? 0 : 1;", + "vec3d a; a ? 0 : 1;", + "vec3f a; a ? 0 : 1;", + "vec3i a; a ? 0 : 1;", + "vec4d a; a ? 0 : 1;", + "vec4f a; a ? 0 : 1;", + "vec4i a; a ? 0 : 1;", // "int a, b; (a ? b : 2) = 1;", "true ? {1,2} : {1,2,3};", "true ? \"foo\" : 1;", "true ? 1.0f : \"foo\";", - "string a; true ? a : 1;" - "string a; true ? 1.0f : a;" + "string a; true ? a : 1;", + "string a; true ? 1.0f : a;", + // conditional + "mat4d a; if (a) 1;", + "mat4f a; if (a) 1;", + "string a; if (a) 1;", + "vec2d a; if (a) 1;", + "vec2f a; if (a) 1;", + "vec2i a; if (a) 1;", + "vec3d a; if (a) 1;", + "vec3f a; if (a) 1;", + "vec3i a; if (a) 1;", + "vec4d a; if (a) 1;", + "vec4f a; if (a) 1;", + "vec4i a; if (a) 1;", + // loops + "mat4d a; for (;a;) 1;", + "mat4f a; for (;a;) 1;", + "string a; for (;a;) 1;", + "vec2d a; for (;a;) 1;", + "vec2f a; for (;a;) 1;", + "vec2i a; for (;a;) 1;", + "vec3d a; for (;a;) 1;", + "vec3f a; for (;a;) 1;", + "vec3i a; for (;a;) 1;", + "vec4d a; for (;a;) 1;", + "vec4f a; for (;a;) 1;", + "vec4i a; for (;a;) 1;", + "mat4d a; while (a) 1;", + "mat4f a; while (a) 1;", + "string a; while (a) 1;", + "vec2d a; while (a) 1;", + "vec2f a; while (a) 1;", + "vec2i a; while (a) 1;", + "vec3d a; while (a) 1;", + "vec3f a; while (a) 1;", + "vec3i a; while (a) 1;", + "vec4d a; while (a) 1;", + "vec4f a; while (a) 1;", + "vec4i a; while (a) 1;", + "mat4d a; do { 1; } while(a);", + "mat4f a; do { 1; } while(a);", + "string a; do { 1; } while(a);", + "vec2d a; do { 1; } while(a);", + "vec2f a; do { 1; } while(a);", + "vec2i a; do { 1; } while(a);", + "vec3d a; do { 1; } while(a);", + "vec3f a; do { 1; } while(a);", + "vec3i a; do { 1; } while(a);", + "vec4d a; do { 1; } while(a);", + "vec4f a; do { 1; } while(a);", + "vec4i a; do { 1; } while(a);" }; class TestComputeGeneratorFailures : public CppUnit::TestCase @@ -228,10 +310,10 @@ TestComputeGeneratorFailures::testFailures() for (const auto& code : tests) { const openvdb::ax::ast::Tree::ConstPtr ast = openvdb::ax::ast::parse(code.c_str(), logger); - CPPUNIT_ASSERT(ast.get()); + CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Unable to parse", code), ast.get()); unittest_util::LLVMState state; - openvdb::ax::codegen::ComputeGenerator gen(state.module(), opts, reg, logger); + openvdb::ax::codegen::codegen_internal::ComputeGenerator gen(state.module(), opts, reg, logger); gen.generate(*ast); CPPUNIT_ASSERT_MESSAGE(ERROR_MSG("Expected Compiler Error", code), logger.hasError()); diff --git a/openvdb_ax/openvdb_ax/test/backend/TestFunctionTypes.cc b/openvdb_ax/openvdb_ax/test/backend/TestFunctionTypes.cc index c65b69bfc8..03a9338452 100644 --- a/openvdb_ax/openvdb_ax/test/backend/TestFunctionTypes.cc +++ b/openvdb_ax/openvdb_ax/test/backend/TestFunctionTypes.cc @@ -63,9 +63,9 @@ struct TestIRFunction : public openvdb::ax::codegen::IRFunctionBase struct CBindings { static void voidfunc() {} - static int16_t scalarfunc(bool,int16_t,int32_t,int64_t,float,double) { return short(); } - static int32_t scalatptsfunc(bool*,int16_t*,int32_t*,int64_t*,float*,double*) { return int(); } - static int64_t arrayfunc(bool(*)[1],int16_t(*)[2],int32_t(*)[3],int64_t(*)[4],float(*)[5],double(*)[6]) { return long(); } + static int16_t scalarfunc(bool,int16_t,int32_t,int64_t,float,double) { return int16_t(); } + static int32_t scalatptsfunc(bool*,int16_t*,int32_t*,int64_t*,float*,double*) { return int32_t(); } + static int64_t arrayfunc(bool(*)[1],int16_t(*)[2],int32_t(*)[3],int64_t(*)[4],float(*)[5],double(*)[6]) { return int64_t(); } static void multiptrfunc(void*, void**, void***, float*, float**, float***) { } template static inline Type tmplfunc() { return Type(); } }; @@ -249,15 +249,15 @@ TestFunctionTypes::testPrintSignature() os.str(""); printSignature(os, types, vt, "", {"one"}, true); - CPPUNIT_ASSERT_EQUAL(std::string("void(int one; long)"), os.str()); + CPPUNIT_ASSERT_EQUAL(std::string("void(int32 one; int64)"), os.str()); os.str(""); printSignature(os, types, vt, "", {"one", "two"}, true); - CPPUNIT_ASSERT_EQUAL(std::string("void(int one; long two)"), os.str()); + CPPUNIT_ASSERT_EQUAL(std::string("void(int32 one; int64 two)"), os.str()); os.str(""); printSignature(os, types, vt, "1", {"one", "two", "three"}, true); - CPPUNIT_ASSERT_EQUAL(std::string("void 1(int one; long two)"), os.str()); + CPPUNIT_ASSERT_EQUAL(std::string("void 1(int32 one; int64 two)"), os.str()); os.str(""); printSignature(os, types, vt, "1", {"", "two"}, false); @@ -272,7 +272,7 @@ TestFunctionTypes::testPrintSignature() types.emplace_back(llvm::ArrayType::get(llvm::Type::getInt32Ty(C), 3)); printSignature(os, types, llvm::Type::getInt64Ty(C), "test", {"", "two"}, true); - CPPUNIT_ASSERT_EQUAL(std::string("long test(int; long two; i8*; vec3i)"), os.str()); + CPPUNIT_ASSERT_EQUAL(std::string("int64 test(int32; int64 two; i8*; vec3i)"), os.str()); os.str(""); types.clear(); @@ -347,7 +347,7 @@ TestFunctionTypes::testFunctionCreate() // test print os.str(""); test->print(C, os, "name", /*axtypes=*/true); - CPPUNIT_ASSERT_EQUAL(std::string("void name(int)"), os.str()); + CPPUNIT_ASSERT_EQUAL(std::string("void name(int32)"), os.str()); // // Test empty signature @@ -389,7 +389,7 @@ TestFunctionTypes::testFunctionCreate() // test print os.str(""); test->print(C, os, "name", /*axtypes=*/true); - CPPUNIT_ASSERT_EQUAL(std::string("int name()"), os.str()); + CPPUNIT_ASSERT_EQUAL(std::string("int32 name()"), os.str()); // // Test scalar types @@ -448,7 +448,7 @@ TestFunctionTypes::testFunctionCreate() // test print os.str(""); test->print(C, os, "name", /*axtypes=*/true); - CPPUNIT_ASSERT_EQUAL(std::string("short name(bool; short; int; long; float; double)"), os.str()); + CPPUNIT_ASSERT_EQUAL(std::string("int16 name(bool; int16; int32; int64; float; double)"), os.str()); // // Test scalar ptrs types @@ -584,7 +584,7 @@ TestFunctionTypes::testFunctionCreate() // test print - note mat/i types are not ax types os.str(""); test->print(C, os, "name", /*axtypes=*/true); - CPPUNIT_ASSERT_EQUAL(std::string("long name(vec2i; vec2f; vec2d; vec3i; vec3f; vec3d;" + CPPUNIT_ASSERT_EQUAL(std::string("int64 name(vec2i; vec2f; vec2d; vec3i; vec3f; vec3d;" " vec4i; vec4f; vec4d; [9 x i32]*; mat3f; mat3d; [16 x i32]*; mat4f; mat4d)"), os.str()); @@ -765,7 +765,7 @@ TestFunctionTypes::testFunctionCall() llvm::ArrayType::get(llvm::Type::getDoubleTy(C), 2)->getPointerTo(), // vec2d llvm::ArrayType::get(llvm::Type::getDoubleTy(C), 9)->getPointerTo(), // mat3d llvm::Type::getInt32Ty(C), // int - llvm::Type::getInt64Ty(C), // long + llvm::Type::getInt64Ty(C), // int64 llvm::Type::getFloatTy(C) // float }, llvm::Type::getVoidTy(C), @@ -779,7 +779,7 @@ TestFunctionTypes::testFunctionCall() llvm::Value* f32c0 = LLVMType::get(C, 0.0f); // float llvm::Value* d64c0 = LLVMType::get(C, 0.0); // double llvm::Value* i32c1 = B.getInt32(1); // int - llvm::Value* i64c1 = B.getInt64(1); // long + llvm::Value* i64c1 = B.getInt64(1); // int64 llvm::Value* vec3i = openvdb::ax::codegen::arrayPack({i32c1,i32c1,i32c1}, B); // vec3i llvm::Value* vec2d = openvdb::ax::codegen::arrayPack({d64c0,d64c0},B); // vec2d llvm::Value* mat3d = openvdb::ax::codegen::arrayPack({ d64c0,d64c0,d64c0, @@ -909,9 +909,9 @@ TestFunctionTypes::testFunctionCall() argsToCast.emplace_back(vec3i); // vec3i - no cast required argsToCast.emplace_back(vec2d); // vec2d - no cast required argsToCast.emplace_back(mat3d); // mat3d - no cast required - argsToCast.emplace_back(i64c1); // long - argsToCast.emplace_back(i64c1); // long - no cast required - argsToCast.emplace_back(i64c1); // long + argsToCast.emplace_back(i64c1); // int64 + argsToCast.emplace_back(i64c1); // int64 - no cast required + argsToCast.emplace_back(i64c1); // int64 result = test->call(argsToCast, B, /*cast*/true); CPPUNIT_ASSERT(result); @@ -1201,9 +1201,9 @@ TestFunctionTypes::testFunctionMatch() const std::vector scalars { llvm::Type::getInt1Ty(C), // bool - llvm::Type::getInt16Ty(C), // short + llvm::Type::getInt16Ty(C), // int16 llvm::Type::getInt32Ty(C), // int - llvm::Type::getInt64Ty(C), // long + llvm::Type::getInt64Ty(C), // int64 llvm::Type::getFloatTy(C), // float llvm::Type::getDoubleTy(C) // double }; @@ -1265,9 +1265,9 @@ TestFunctionTypes::testFunctionMatch() test.reset(new TestFunction({ llvm::Type::getInt1Ty(C), // bool - llvm::Type::getInt16Ty(C), // short - llvm::Type::getInt32Ty(C), // int - llvm::Type::getInt64Ty(C), // long + llvm::Type::getInt16Ty(C), // int16 + llvm::Type::getInt32Ty(C), // int32 + llvm::Type::getInt64Ty(C), // int64 llvm::Type::getFloatTy(C), // float llvm::Type::getDoubleTy(C), // double llvm::ArrayType::get(llvm::Type::getInt32Ty(C), 2)->getPointerTo(), // vec2i diff --git a/openvdb_ax/openvdb_ax/test/compiler/TestAXRun.cc b/openvdb_ax/openvdb_ax/test/compiler/TestAXRun.cc new file mode 100644 index 0000000000..5d0106628a --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/compiler/TestAXRun.cc @@ -0,0 +1,128 @@ +// Copyright Contributors to the OpenVDB Project +// SPDX-License-Identifier: MPL-2.0 + +#include +#include + +#include +#include + +#include + +class TestAXRun : public CppUnit::TestCase +{ +public: + + CPPUNIT_TEST_SUITE(TestAXRun); + CPPUNIT_TEST(singleRun); + CPPUNIT_TEST(multiRun); + CPPUNIT_TEST_SUITE_END(); + + void singleRun(); + void multiRun(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestAXRun); + +void +TestAXRun::singleRun() +{ + openvdb::FloatGrid f; + f.setName("a"); + f.tree().setValueOn({0,0,0}, 0.0f); + openvdb::ax::run("@a = 1.0f;", f); + CPPUNIT_ASSERT_EQUAL(1.0f, f.tree().getValue({0,0,0})); + + openvdb::math::Transform::Ptr defaultTransform = + openvdb::math::Transform::createLinearTransform(); + const std::vector singlePointZero = {openvdb::Vec3d::zero()}; + openvdb::points::PointDataGrid::Ptr + points = openvdb::points::createPointDataGrid + (singlePointZero, *defaultTransform); + + openvdb::ax::run("@a = 1.0f;", *points); + const auto leafIter = points->tree().cbeginLeaf(); + const auto& descriptor = leafIter->attributeSet().descriptor(); + + CPPUNIT_ASSERT_EQUAL(size_t(2), descriptor.size()); + const size_t idx = descriptor.find("a"); + CPPUNIT_ASSERT(idx != openvdb::points::AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(descriptor.valueType(idx) == openvdb::typeNameAsString()); + openvdb::points::AttributeHandle handle(leafIter->constAttributeArray(idx)); + CPPUNIT_ASSERT_EQUAL(1.0f, handle.get(0)); +} + +void +TestAXRun::multiRun() +{ + { + // test error on points and volumes + openvdb::FloatGrid::Ptr f(new openvdb::FloatGrid); + openvdb::points::PointDataGrid::Ptr p(new openvdb::points::PointDataGrid); + std::vector v1 { f, p }; + CPPUNIT_ASSERT_THROW(openvdb::ax::run("@a = 1.0f;", v1), openvdb::AXCompilerError); + + std::vector v2 { p, f }; + CPPUNIT_ASSERT_THROW(openvdb::ax::run("@a = 1.0f;", v2), openvdb::AXCompilerError); + } + + { + // multi volumes + openvdb::FloatGrid::Ptr f1(new openvdb::FloatGrid); + openvdb::FloatGrid::Ptr f2(new openvdb::FloatGrid); + f1->setName("a"); + f2->setName("b"); + f1->tree().setValueOn({0,0,0}, 0.0f); + f2->tree().setValueOn({0,0,0}, 0.0f); + std::vector v { f1, f2 }; + openvdb::ax::run("@a = @b = 1;", v); + CPPUNIT_ASSERT_EQUAL(1.0f, f1->tree().getValue({0,0,0})); + CPPUNIT_ASSERT_EQUAL(1.0f, f2->tree().getValue({0,0,0})); + } + + { + // multi points + openvdb::math::Transform::Ptr defaultTransform = + openvdb::math::Transform::createLinearTransform(); + const std::vector singlePointZero = {openvdb::Vec3d::zero()}; + openvdb::points::PointDataGrid::Ptr + p1 = openvdb::points::createPointDataGrid + (singlePointZero, *defaultTransform); + openvdb::points::PointDataGrid::Ptr + p2 = openvdb::points::createPointDataGrid + (singlePointZero, *defaultTransform); + + std::vector v { p1, p2 }; + openvdb::ax::run("@a = @b = 1;", v); + + const auto leafIter1 = p1->tree().cbeginLeaf(); + const auto leafIter2 = p2->tree().cbeginLeaf(); + const auto& descriptor1 = leafIter1->attributeSet().descriptor(); + const auto& descriptor2 = leafIter1->attributeSet().descriptor(); + + CPPUNIT_ASSERT_EQUAL(size_t(3), descriptor1.size()); + CPPUNIT_ASSERT_EQUAL(size_t(3), descriptor2.size()); + const size_t idx1 = descriptor1.find("a"); + CPPUNIT_ASSERT_EQUAL(idx1, descriptor2.find("a")); + const size_t idx2 = descriptor1.find("b"); + CPPUNIT_ASSERT_EQUAL(idx2, descriptor2.find("b")); + CPPUNIT_ASSERT(idx1 != openvdb::points::AttributeSet::INVALID_POS); + CPPUNIT_ASSERT(idx2 != openvdb::points::AttributeSet::INVALID_POS); + + CPPUNIT_ASSERT(descriptor1.valueType(idx1) == openvdb::typeNameAsString()); + CPPUNIT_ASSERT(descriptor1.valueType(idx2) == openvdb::typeNameAsString()); + CPPUNIT_ASSERT(descriptor2.valueType(idx1) == openvdb::typeNameAsString()); + CPPUNIT_ASSERT(descriptor2.valueType(idx2) == openvdb::typeNameAsString()); + + openvdb::points::AttributeHandle handle(leafIter1->constAttributeArray(idx1)); + CPPUNIT_ASSERT_EQUAL(1.0f, handle.get(0)); + handle = openvdb::points::AttributeHandle(leafIter1->constAttributeArray(idx2)); + CPPUNIT_ASSERT_EQUAL(1.0f, handle.get(0)); + + handle = openvdb::points::AttributeHandle(leafIter2->constAttributeArray(idx1)); + CPPUNIT_ASSERT_EQUAL(1.0f, handle.get(0)); + handle = openvdb::points::AttributeHandle(leafIter2->constAttributeArray(idx2)); + CPPUNIT_ASSERT_EQUAL(1.0f, handle.get(0)); + } +} + diff --git a/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc b/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc index b2d5e436d2..e86a7b5f8b 100644 --- a/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc +++ b/openvdb_ax/openvdb_ax/test/compiler/TestPointExecutable.cc @@ -223,7 +223,7 @@ TestPointExecutable::testCompilerCases() logger.clear(); { openvdb::ax::PointExecutable::Ptr executable = - compiler->compile("int i = 1e200000000;", logger); // warning + compiler->compile("int i = 18446744073709551615;", logger); // warning CPPUNIT_ASSERT(executable); CPPUNIT_ASSERT(logger.hasWarning()); } @@ -257,7 +257,7 @@ TestPointExecutable::testCompilerCases() } logger.clear(); { - openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 1e200000000;", logger); + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 18446744073709551615;", logger); CPPUNIT_ASSERT(tree); openvdb::ax::PointExecutable::Ptr executable = compiler->compile(*tree, logger); // warning @@ -287,7 +287,7 @@ TestPointExecutable::testCompilerCases() } logger.clear(); { - openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 1e200000000;", logger); + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 18446744073709551615;", logger); openvdb::ax::PointExecutable::Ptr executable = compiler->compile(*(tree->copy()), logger); // warning CPPUNIT_ASSERT(executable); diff --git a/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc b/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc index 11c3d82074..371f5ef66b 100644 --- a/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc +++ b/openvdb_ax/openvdb_ax/test/compiler/TestVolumeExecutable.cc @@ -221,7 +221,7 @@ TestVolumeExecutable::testCompilerCases() logger.clear(); { openvdb::ax::VolumeExecutable::Ptr executable = - compiler->compile("int i = 1e200000000;", logger); // warning + compiler->compile("int i = 18446744073709551615;", logger); // warning CPPUNIT_ASSERT(executable); CPPUNIT_ASSERT(logger.hasWarning()); } @@ -255,7 +255,7 @@ TestVolumeExecutable::testCompilerCases() } logger.clear(); { - openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 1e200000000;", logger); + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 18446744073709551615;", logger); CPPUNIT_ASSERT(tree); openvdb::ax::VolumeExecutable::Ptr executable = compiler->compile(*tree, logger); // warning @@ -286,7 +286,7 @@ TestVolumeExecutable::testCompilerCases() } logger.clear(); { - openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 1e200000000;", logger); + openvdb::ax::ast::Tree::ConstPtr tree = openvdb::ax::ast::parse("int i = 18446744073709551615;", logger); openvdb::ax::VolumeExecutable::Ptr executable = compiler->compile(*(tree->copy()), logger); // warning CPPUNIT_ASSERT(executable); diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestArrayPack.cc b/openvdb_ax/openvdb_ax/test/frontend/TestArrayPack.cc index cf6b98c77c..cad7f6d53c 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestArrayPack.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestArrayPack.cc @@ -18,8 +18,8 @@ namespace { static const unittest_util::CodeTests tests = { - { "{1s, 2, {1,2,3}};", Node::Ptr(new ArrayPack({ - new Value(1), + { "{1, 2, {1,2,3}};", Node::Ptr(new ArrayPack({ + new Value(1), new Value(2), new ArrayPack({ new Value(1), diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestArrayUnpackNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestArrayUnpackNode.cc index 59496bcb62..db7f276a11 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestArrayUnpackNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestArrayUnpackNode.cc @@ -32,7 +32,6 @@ static const unittest_util::CodeTests tests = { "@a.r;", Node::Ptr(new ArrayUnpack(new Attribute("a", CoreType::FLOAT, true), new Value(0))) }, { "@a.g;", Node::Ptr(new ArrayUnpack(new Attribute("a", CoreType::FLOAT, true), new Value(1))) }, { "@a.b;", Node::Ptr(new ArrayUnpack(new Attribute("a", CoreType::FLOAT, true), new Value(2))) }, - { "@a[0s];", Node::Ptr(new ArrayUnpack(new Attribute("a", CoreType::FLOAT, true), new Value(0))) }, { "@a[0l];", Node::Ptr(new ArrayUnpack(new Attribute("a", CoreType::FLOAT, true), new Value(0))) }, { "@a[0];", Node::Ptr(new ArrayUnpack(new Attribute("a", CoreType::FLOAT, true), new Value(0))) }, { "@a[1];", Node::Ptr(new ArrayUnpack(new Attribute("a", CoreType::FLOAT, true), new Value(1))) }, diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestAssignExpressionNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestAssignExpressionNode.cc index 1c1f5ec295..5c681920b5 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestAssignExpressionNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestAssignExpressionNode.cc @@ -32,12 +32,12 @@ unittest_util::CodeTests tests = { "@a = test();", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::FLOAT, true), new FunctionCall("test"))) }, { "@a = 1 + i@b;", Node::Ptr(new AssignExpression( new Attribute("a", CoreType::FLOAT, true), - new BinaryOperator(new Value(1), new Attribute("b", CoreType::INT), OperatorToken::PLUS) + new BinaryOperator(new Value(1), new Attribute("b", CoreType::INT32), OperatorToken::PLUS) )) }, { "@a = -int@b;", Node::Ptr(new AssignExpression( new Attribute("a", CoreType::FLOAT, true), - new UnaryOperator(new Attribute("b", CoreType::INT), OperatorToken::MINUS) + new UnaryOperator(new Attribute("b", CoreType::INT32), OperatorToken::MINUS) )) }, { "@a = ++float@b;", Node::Ptr(new AssignExpression( @@ -64,16 +64,16 @@ unittest_util::CodeTests tests = new ArrayUnpack(new Attribute("b", CoreType::VEC3F), new Value(0)) )) }, - { "@a = 1s;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::FLOAT, true), new Value(1))) }, { "@a = \"b\";", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::FLOAT, true), new Value("b"))) }, { "@a = b;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::FLOAT, true), new Local("b"))) }, // test all attribute { "bool@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::BOOL), new Value(true))) }, - { "short@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::SHORT), new Value(true))) }, - { "i@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::INT), new Value(true))) }, - { "int@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::INT), new Value(true))) }, - { "long@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::LONG), new Value(true))) }, + { "int16@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::INT16), new Value(true))) }, + { "i@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::INT32), new Value(true))) }, + { "int@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::INT32), new Value(true))) }, + { "int32@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::INT32), new Value(true))) }, + { "int64@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::INT64), new Value(true))) }, { "f@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::FLOAT), new Value(true))) }, { "float@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::FLOAT), new Value(true))) }, { "double@a = true;", Node::Ptr(new AssignExpression(new Attribute("a", CoreType::DOUBLE), new Value(true))) }, diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestAttributeNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestAttributeNode.cc index 880c595407..ef54cc7ed2 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestAttributeNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestAttributeNode.cc @@ -19,10 +19,11 @@ namespace { static const unittest_util::CodeTests tests = { { "bool@_a;", Node::Ptr(new Attribute("_a", CoreType::BOOL)) }, - { "short@a_;", Node::Ptr(new Attribute("a_", CoreType::SHORT)) }, - { "i@a1;", Node::Ptr(new Attribute("a1", CoreType::INT)) }, - { "int@abc;", Node::Ptr(new Attribute("abc", CoreType::INT)) }, - { "long@a;", Node::Ptr(new Attribute("a", CoreType::LONG)) }, + { "int16@a_;", Node::Ptr(new Attribute("a_", CoreType::INT16)) }, + { "i@a1;", Node::Ptr(new Attribute("a1", CoreType::INT32)) }, + { "int@abc;", Node::Ptr(new Attribute("abc", CoreType::INT32)) }, + { "int32@abc;", Node::Ptr(new Attribute("abc", CoreType::INT32)) }, + { "int64@a;", Node::Ptr(new Attribute("a", CoreType::INT64)) }, { "@a;", Node::Ptr(new Attribute("a", CoreType::FLOAT, true)) }, { "f@a;", Node::Ptr(new Attribute("a", CoreType::FLOAT)) }, { "float@a;", Node::Ptr(new Attribute("a", CoreType::FLOAT)) }, diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestBinaryOperatorNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestBinaryOperatorNode.cc index 2f975c1678..624419f088 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestBinaryOperatorNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestBinaryOperatorNode.cc @@ -228,7 +228,7 @@ static const unittest_util::CodeTests tests = }, { "int(a) + float(b);", Node::Ptr( new BinaryOperator( - new Cast(new Local("a"), CoreType::INT), + new Cast(new Local("a"), CoreType::INT32), new Cast(new Local("b"), CoreType::FLOAT), OperatorToken::PLUS ) @@ -258,10 +258,10 @@ static const unittest_util::CodeTests tests = ) ) }, - { "0 + 1s;", Node::Ptr( + { "0 + 1;", Node::Ptr( new BinaryOperator( new Value(0), - new Value(1), + new Value(1), OperatorToken::PLUS ) ) diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestCastNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestCastNode.cc index 504e6349f6..42354f9bbc 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestCastNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestCastNode.cc @@ -19,32 +19,30 @@ namespace { static const unittest_util::CodeTests tests = { { "bool(a);", Node::Ptr(new Cast(new Local("a"), CoreType::BOOL)) }, - { "short(a);", Node::Ptr(new Cast(new Local("a"), CoreType::SHORT)) }, - { "int(a);", Node::Ptr(new Cast(new Local("a"), CoreType::INT)) }, - { "long(a);", Node::Ptr(new Cast(new Local("a"), CoreType::LONG)) }, + { "int(a);", Node::Ptr(new Cast(new Local("a"), CoreType::INT32)) }, + { "int32(a);", Node::Ptr(new Cast(new Local("a"), CoreType::INT32)) }, + { "int64(a);", Node::Ptr(new Cast(new Local("a"), CoreType::INT64)) }, { "float(a);", Node::Ptr(new Cast(new Local("a"), CoreType::FLOAT)) }, { "double(a);", Node::Ptr(new Cast(new Local("a"), CoreType::DOUBLE)) }, - { "int((a));", Node::Ptr(new Cast(new Local("a"), CoreType::INT)) }, - { "int(1l);", Node::Ptr(new Cast(new Value(1), CoreType::INT)) }, - { "int(1);", Node::Ptr(new Cast(new Value(1), CoreType::INT)) }, - { "int(0);", Node::Ptr(new Cast(new Value(0), CoreType::INT)) }, - { "int(@a);", Node::Ptr(new Cast(new Attribute("a", CoreType::FLOAT, true), CoreType::INT)) }, + { "int32((a));", Node::Ptr(new Cast(new Local("a"), CoreType::INT32)) }, + { "int32(1l);", Node::Ptr(new Cast(new Value(1), CoreType::INT32)) }, + { "int32(1);", Node::Ptr(new Cast(new Value(1), CoreType::INT32)) }, + { "int32(0);", Node::Ptr(new Cast(new Value(0), CoreType::INT32)) }, + { "int32(@a);", Node::Ptr(new Cast(new Attribute("a", CoreType::FLOAT, true), CoreType::INT32)) }, { "double(true);", Node::Ptr(new Cast(new Value(true), CoreType::DOUBLE)) }, { "double(false);", Node::Ptr(new Cast(new Value(false), CoreType::DOUBLE)) }, - { "int(1.0f);", Node::Ptr(new Cast(new Value(1.0f), CoreType::INT)) }, - { "long(1.0);", Node::Ptr(new Cast(new Value(1.0), CoreType::LONG)) }, + { "int32(1.0f);", Node::Ptr(new Cast(new Value(1.0f), CoreType::INT32)) }, + { "int64(1.0);", Node::Ptr(new Cast(new Value(1.0), CoreType::INT64)) }, { "float(true);", Node::Ptr(new Cast(new Value(true), CoreType::FLOAT)) }, - { "double(1s);", Node::Ptr(new Cast(new Value(1), CoreType::DOUBLE)) }, - { "int(func());", Node::Ptr(new Cast(new FunctionCall("func"), CoreType::INT)) }, + { "int32(func());", Node::Ptr(new Cast(new FunctionCall("func"), CoreType::INT32)) }, { "bool(a+b);", Node::Ptr(new Cast(new BinaryOperator(new Local("a"), new Local("b"), OperatorToken::PLUS), CoreType::BOOL)) }, - { "short(a+b);", Node::Ptr(new Cast(new BinaryOperator(new Local("a"), new Local("b"), OperatorToken::PLUS), CoreType::SHORT)) }, - { "int(~a);", Node::Ptr(new Cast(new UnaryOperator(new Local("a"), OperatorToken::BITNOT), CoreType::INT)) }, - { "long(~a);", Node::Ptr(new Cast(new UnaryOperator(new Local("a"), OperatorToken::BITNOT), CoreType::LONG)) }, + { "int32(~a);", Node::Ptr(new Cast(new UnaryOperator(new Local("a"), OperatorToken::BITNOT), CoreType::INT32)) }, + { "int64(~a);", Node::Ptr(new Cast(new UnaryOperator(new Local("a"), OperatorToken::BITNOT), CoreType::INT64)) }, { "float(a = b);", Node::Ptr(new Cast(new AssignExpression(new Local("a"), new Local("b")), CoreType::FLOAT)) }, { "double(a.x);", Node::Ptr(new Cast(new ArrayUnpack(new Local("a"), new Value(0)), CoreType::DOUBLE)) }, - { "int(a++);", Node::Ptr(new Cast(new Crement(new Local("a"), Crement::Operation::Increment, true), CoreType::INT)) }, - { "int({a,b,c});", Node::Ptr(new Cast(new ArrayPack({new Local("a"), new Local("b"), new Local("c")}), CoreType::INT)) }, - { "int((a,b,c));", Node::Ptr(new Cast(new CommaOperator({new Local("a"), new Local("b"), new Local("c")}), CoreType::INT)) }, + { "int32(a++);", Node::Ptr(new Cast(new Crement(new Local("a"), Crement::Operation::Increment, true), CoreType::INT32)) }, + { "int32({a,b,c});", Node::Ptr(new Cast(new ArrayPack({new Local("a"), new Local("b"), new Local("c")}), CoreType::INT32)) }, + { "int32((a,b,c));", Node::Ptr(new Cast(new CommaOperator({new Local("a"), new Local("b"), new Local("c")}), CoreType::INT32)) }, { "float(double(0));", Node::Ptr(new Cast(new Cast(new Value(0), CoreType::DOUBLE), CoreType::FLOAT)) }, }; diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestCommaOperator.cc b/openvdb_ax/openvdb_ax/test/frontend/TestCommaOperator.cc index c113340ae4..dbf9c430f4 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestCommaOperator.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestCommaOperator.cc @@ -18,9 +18,9 @@ namespace { static const unittest_util::CodeTests tests = { - { "(1s, 2, (1,2,3));", Node::Ptr( + { "(1, 2, (1,2,3));", Node::Ptr( new CommaOperator({ - new Value(1), + new Value(1), new Value(2), new CommaOperator({ new Value(1), diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestDeclareLocalNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestDeclareLocalNode.cc index cfeee53592..94357a2a78 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestDeclareLocalNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestDeclareLocalNode.cc @@ -19,9 +19,8 @@ namespace { static const unittest_util::CodeTests tests = { { "bool a_;", Node::Ptr(new DeclareLocal(CoreType::BOOL, new Local("a_"))) }, - { "short _a;", Node::Ptr(new DeclareLocal(CoreType::SHORT, new Local("_a"))) }, - { "int _;", Node::Ptr(new DeclareLocal(CoreType::INT, new Local("_"))) }, - { "long aa;", Node::Ptr(new DeclareLocal(CoreType::LONG, new Local("aa"))) }, + { "int32 _;", Node::Ptr(new DeclareLocal(CoreType::INT32, new Local("_"))) }, + { "int64 aa;", Node::Ptr(new DeclareLocal(CoreType::INT64, new Local("aa"))) }, { "float A;", Node::Ptr(new DeclareLocal(CoreType::FLOAT, new Local("A"))) }, { "double _A;", Node::Ptr(new DeclareLocal(CoreType::DOUBLE, new Local("_A"))) }, { "vec2i a1;", Node::Ptr(new DeclareLocal(CoreType::VEC2I, new Local("a1"))) }, diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestExternalVariableNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestExternalVariableNode.cc index 808376397a..e1d9456d4a 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestExternalVariableNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestExternalVariableNode.cc @@ -20,10 +20,10 @@ static const unittest_util::CodeTests tests = { { "$a;", Node::Ptr(new ExternalVariable("a", CoreType::FLOAT)) }, { "bool$_a;", Node::Ptr(new ExternalVariable("_a", CoreType::BOOL)) }, - { "short$a_;", Node::Ptr(new ExternalVariable("a_", CoreType::SHORT)) }, - { "i$a1;", Node::Ptr(new ExternalVariable("a1", CoreType::INT)) }, - { "int$abc;", Node::Ptr(new ExternalVariable("abc", CoreType::INT)) }, - { "long$a;", Node::Ptr(new ExternalVariable("a", CoreType::LONG)) }, + { "i$a1;", Node::Ptr(new ExternalVariable("a1", CoreType::INT32)) }, + { "int$abc;", Node::Ptr(new ExternalVariable("abc", CoreType::INT32)) }, + { "int32$abc;", Node::Ptr(new ExternalVariable("abc", CoreType::INT32)) }, + { "int64$a;", Node::Ptr(new ExternalVariable("a", CoreType::INT64)) }, { "f$a;", Node::Ptr(new ExternalVariable("a", CoreType::FLOAT)) }, { "float$a;", Node::Ptr(new ExternalVariable("a", CoreType::FLOAT)) }, { "double$a;", Node::Ptr(new ExternalVariable("a", CoreType::DOUBLE)) }, diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestLoopNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestLoopNode.cc index c3e3a8dcde..a9e3803e6c 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestLoopNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestLoopNode.cc @@ -18,22 +18,22 @@ namespace { static const unittest_util::CodeTests tests = { - { "for (int i = 0; i < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, + { "for (int32 i = 0; i < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, new BinaryOperator(new Local("i"), new Value(10), OperatorToken::LESSTHAN), new Block(), - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, - { "for(int i = 0; i < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, + { "for(int32 i = 0; i < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, new BinaryOperator(new Local("i"), new Value(10), OperatorToken::LESSTHAN), new Block(), - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, - { "for (int i = 0;i < 10;++i) ;", Node::Ptr(new Loop(tokens::LoopToken::FOR, + { "for (int32 i = 0;i < 10;++i) ;", Node::Ptr(new Loop(tokens::LoopToken::FOR, new BinaryOperator(new Local("i"), new Value(10), OperatorToken::LESSTHAN), new Block(), - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, { "for (i; i < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, @@ -135,10 +135,10 @@ static const unittest_util::CodeTests tests = nullptr, new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, - { "for (int i = 0; i < 10; ++i, ++j) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, + { "for (int32 i = 0; i < 10; ++i, ++j) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, new BinaryOperator(new Local("i"), new Value(10), OperatorToken::LESSTHAN), new Block(), - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new CommaOperator({ new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false), new Crement(new Local("j"), Crement::Operation::Increment, /*post*/false) @@ -153,22 +153,22 @@ static const unittest_util::CodeTests tests = new Crement(new Local("j"), Crement::Operation::Increment, /*post*/false) }))) }, - { "for (int i = 0; i; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, + { "for (int32 i = 0; i; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, new Local("i"), new Block(), - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, - { "for (int i = 0; func(i); ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, + { "for (int32 i = 0; func(i); ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, new FunctionCall("func", new Local("i")), new Block(), - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, - { "for (int i = 0; int j = func(i); ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, - new DeclareLocal(CoreType::INT, new Local("j"),new FunctionCall("func", new Local("i"))), + { "for (int32 i = 0; int32 j = func(i); ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, + new DeclareLocal(CoreType::INT32, new Local("j"),new FunctionCall("func", new Local("i"))), new Block(), - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, { "for (; i < 10;) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, @@ -195,12 +195,12 @@ static const unittest_util::CodeTests tests = new Value(3) })))) }, - { "for (int i = 0, j = 0, k; i < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, + { "for (int32 i = 0, j = 0, k; i < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, new BinaryOperator(new Local("i"), new Value(10), OperatorToken::LESSTHAN), new Block(), - new StatementList({new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), - new DeclareLocal(CoreType::INT, new Local("j"), new Value(0)), - new DeclareLocal( CoreType::INT, new Local("k"))}), + new StatementList({new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("j"), new Value(0)), + new DeclareLocal( CoreType::INT32, new Local("k"))}), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, { "for (i = 0, j = 0; i < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, @@ -212,17 +212,17 @@ static const unittest_util::CodeTests tests = }), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, - { "for (int i = 0; i < 10, j < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, + { "for (int32 i = 0; i < 10, j < 10; ++i) {}", Node::Ptr(new Loop(tokens::LoopToken::FOR, new CommaOperator({ new BinaryOperator(new Local("i"), new Value(10), OperatorToken::LESSTHAN), new BinaryOperator(new Local("j"), new Value(10), OperatorToken::LESSTHAN) }), new Block(), - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Crement(new Local("i"), Crement::Operation::Increment, /*post*/false))) }, - { "while (int i = 0) {}", Node::Ptr(new Loop(tokens::LoopToken::WHILE, - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + { "while (int32 i = 0) {}", Node::Ptr(new Loop(tokens::LoopToken::WHILE, + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Block())) }, { "while (i = 0) {}", Node::Ptr(new Loop(tokens::LoopToken::WHILE, @@ -267,8 +267,8 @@ static const unittest_util::CodeTests tests = }), new Block())) }, - { "do ; while (int i = 0)", Node::Ptr(new Loop(tokens::LoopToken::DO, - new DeclareLocal(CoreType::INT, new Local("i"), new Value(0)), + { "do ; while (int32 i = 0)", Node::Ptr(new Loop(tokens::LoopToken::DO, + new DeclareLocal(CoreType::INT32, new Local("i"), new Value(0)), new Block())) }, { "do ; while ((a,b,c))", Node::Ptr(new Loop(tokens::LoopToken::DO, diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestStatementListNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestStatementListNode.cc index 2e36393083..55790356d1 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestStatementListNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestStatementListNode.cc @@ -18,47 +18,47 @@ namespace { static const unittest_util::CodeTests tests = { - { "int a = (1,2,3), b=1, c=(b=1);", Node::Ptr(new StatementList({ - new DeclareLocal(CoreType::INT, new Local("a"), + { "int32 a = (1,2,3), b=1, c=(b=1);", Node::Ptr(new StatementList({ + new DeclareLocal(CoreType::INT32, new Local("a"), new CommaOperator({ new Value(1), new Value(2), new Value(3), })), - new DeclareLocal(CoreType::INT, new Local("b"), new Value(1)), - new DeclareLocal(CoreType::INT, new Local("c"), + new DeclareLocal(CoreType::INT32, new Local("b"), new Value(1)), + new DeclareLocal(CoreType::INT32, new Local("c"), new AssignExpression( new Local("b"), new Value(1))), })) }, - { "int a, b;", Node::Ptr(new StatementList({ - new DeclareLocal(CoreType::INT, new Local("a")), - new DeclareLocal(CoreType::INT, new Local("b")) + { "int32 a, b;", Node::Ptr(new StatementList({ + new DeclareLocal(CoreType::INT32, new Local("a")), + new DeclareLocal(CoreType::INT32, new Local("b")) })) }, - { "int a, b = 1;", Node::Ptr(new StatementList({ - new DeclareLocal(CoreType::INT, new Local("a")), - new DeclareLocal(CoreType::INT, new Local("b"), new Value(1)) + { "int32 a, b = 1;", Node::Ptr(new StatementList({ + new DeclareLocal(CoreType::INT32, new Local("a")), + new DeclareLocal(CoreType::INT32, new Local("b"), new Value(1)) })) }, - { "int a, b = 1, c = 1;", Node::Ptr(new StatementList({ - new DeclareLocal(CoreType::INT, new Local("a")), - new DeclareLocal(CoreType::INT, new Local("b"), new Value(1)), - new DeclareLocal(CoreType::INT, new Local("c"), new Value(1)) + { "int32 a, b = 1, c = 1;", Node::Ptr(new StatementList({ + new DeclareLocal(CoreType::INT32, new Local("a")), + new DeclareLocal(CoreType::INT32, new Local("b"), new Value(1)), + new DeclareLocal(CoreType::INT32, new Local("c"), new Value(1)) })) }, - { "int a, b = 1, c;", Node::Ptr(new StatementList({ - new DeclareLocal(CoreType::INT, new Local("a")), - new DeclareLocal(CoreType::INT, new Local("b"), new Value(1)), - new DeclareLocal(CoreType::INT, new Local("c")) + { "int32 a, b = 1, c;", Node::Ptr(new StatementList({ + new DeclareLocal(CoreType::INT32, new Local("a")), + new DeclareLocal(CoreType::INT32, new Local("b"), new Value(1)), + new DeclareLocal(CoreType::INT32, new Local("c")) })) }, - { "int a, b = 1, c, d = 1;", Node::Ptr(new StatementList({ - new DeclareLocal(CoreType::INT, new Local("a")), - new DeclareLocal(CoreType::INT, new Local("b"), new Value(1)), - new DeclareLocal(CoreType::INT, new Local("c")), - new DeclareLocal(CoreType::INT, new Local("d"), new Value(1)) + { "int32 a, b = 1, c, d = 1;", Node::Ptr(new StatementList({ + new DeclareLocal(CoreType::INT32, new Local("a")), + new DeclareLocal(CoreType::INT32, new Local("b"), new Value(1)), + new DeclareLocal(CoreType::INT32, new Local("c")), + new DeclareLocal(CoreType::INT32, new Local("d"), new Value(1)) })) } }; diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestUnaryOperatorNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestUnaryOperatorNode.cc index 8a115001df..a2eb0daeb5 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestUnaryOperatorNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestUnaryOperatorNode.cc @@ -53,7 +53,7 @@ static const unittest_util::CodeTests tests = { "-@a;", Node::Ptr(new UnaryOperator(new Attribute("a", CoreType::FLOAT, true), OperatorToken::MINUS)) }, { "!v@a;", Node::Ptr(new UnaryOperator(new Attribute("a", CoreType::VEC3F), OperatorToken::NOT)) }, { "~v@a;", Node::Ptr(new UnaryOperator(new Attribute("a", CoreType::VEC3F), OperatorToken::BITNOT)) }, - { "+int(a);", Node::Ptr(new UnaryOperator(new Cast(new Local("a"), CoreType::INT), OperatorToken::PLUS)) }, + { "+int(a);", Node::Ptr(new UnaryOperator(new Cast(new Local("a"), CoreType::INT32), OperatorToken::PLUS)) }, { "-(float(a));", Node::Ptr(new UnaryOperator(new Cast(new Local("a"), CoreType::FLOAT), OperatorToken::MINUS)) }, { "!a.x;", Node::Ptr(new UnaryOperator(new ArrayUnpack(new Local("a"), new Value(0)), OperatorToken::NOT)) }, { "-a[0];", Node::Ptr(new UnaryOperator(new ArrayUnpack(new Local("a"), new Value(0)), OperatorToken::MINUS)) }, diff --git a/openvdb_ax/openvdb_ax/test/frontend/TestValueNode.cc b/openvdb_ax/openvdb_ax/test/frontend/TestValueNode.cc index 4bb115103c..f0727816e7 100644 --- a/openvdb_ax/openvdb_ax/test/frontend/TestValueNode.cc +++ b/openvdb_ax/openvdb_ax/test/frontend/TestValueNode.cc @@ -10,6 +10,7 @@ #include #include +#include using namespace openvdb::ax::ast; using namespace openvdb::ax::ast::tokens; @@ -18,6 +19,19 @@ namespace { using CodeTestMap = std::map; +auto converti(const char* c) -> uint64_t { return std::strtoull(c, /*end*/nullptr, /*base*/10); } +auto convertf(const char* c) -> float { return std::strtof(c, /*end*/nullptr); } +auto convertd(const char* c) -> double { return std::strtod(c, /*end*/nullptr); } + +template +std::string fullDecimalValue(const T t) { + // 767 is max number of digits necessary to accurately represent base 2 doubles + std::ostringstream os; + os << std::setprecision(767) << t; + return os.str(); +} + + static const CodeTestMap value_tests = { // No limits::lowest, negative values are a unary operator @@ -30,46 +44,25 @@ static const CodeTestMap value_tests = } }, - { - Node::NodeType::ValueInt16Node, - { - { "1234567890s;", Node::Ptr(new Value("1234567890")) }, // signed int wrap - { "00s;", Node::Ptr(new Value("0")) }, - { "0s;", Node::Ptr(new Value("0")) }, - // signed int wrap - { std::to_string(std::numeric_limits::max()) + "0s;", - Node::Ptr(new Value(std::to_string(std::numeric_limits::max()) + "0")) - }, - // signed int wrap - { std::to_string(std::numeric_limits::max()) + "s;", - Node::Ptr(new Value(std::to_string(std::numeric_limits::max()))) - } - } - }, - { Node::NodeType::ValueInt32Node, { - { "00;", Node::Ptr(new Value("0")) }, - { "1000000000000000;", Node::Ptr(new Value("1000000000000000")) }, // signed int wrap - { "0;", Node::Ptr(new Value("0")) }, - { "1234567890;", Node::Ptr(new Value("1234567890")) }, - { "1;", Node::Ptr(new Value("1")) }, + { "00;", Node::Ptr(new Value(converti("0"))) }, + { "1000000000000000;", Node::Ptr(new Value(converti("1000000000000000"))) }, // signed int wrap + { "0;", Node::Ptr(new Value(converti("0"))) }, + { "1234567890;", Node::Ptr(new Value(converti("1234567890"))) }, + { "1;", Node::Ptr(new Value(converti("1"))) }, // signed int wrap { std::to_string(std::numeric_limits::max()) + ";", - Node::Ptr(new Value(std::to_string(std::numeric_limits::max()))) + Node::Ptr(new Value(std::numeric_limits::max())) }, // signed int wrap { std::to_string(std::numeric_limits::max()) + ";", - Node::Ptr(new Value(std::to_string(std::numeric_limits::max()))) + Node::Ptr(new Value(std::numeric_limits::max())) }, // signed int wrap { std::to_string(std::numeric_limits::max()) + "0;", - Node::Ptr(new Value(std::to_string(std::numeric_limits::max()) + "0")) - }, - // overflow - { std::to_string(std::numeric_limits::max()) + "0;", - Node::Ptr(new Value(std::to_string(std::numeric_limits::max()) + "0")) + Node::Ptr(new Value(uint64_t(std::numeric_limits::max()) * 10ul)) } } }, @@ -77,16 +70,12 @@ static const CodeTestMap value_tests = { Node::NodeType::ValueInt64Node, { - { "01l;", Node::Ptr(new Value("1")) }, - { "0l;", Node::Ptr(new Value("0")) }, - { "1234567890l;", Node::Ptr(new Value("1234567890l")) }, - // overflow - { std::to_string(std::numeric_limits::max()) + "0l;", - Node::Ptr(new Value(std::to_string(std::numeric_limits::max()) + "0")) - }, + { "01l;", Node::Ptr(new Value(converti("1"))) }, + { "0l;", Node::Ptr(new Value(converti("0"))) }, + { "1234567890l;", Node::Ptr(new Value(converti("1234567890l"))) }, // signed int wrap { std::to_string(uint64_t(std::numeric_limits::max()) + 1) + "l;", - Node::Ptr(new Value(std::to_string(uint64_t(std::numeric_limits::max()) + 1ul))) + Node::Ptr(new Value(uint64_t(std::numeric_limits::max()) + 1ul)) } } }, @@ -94,45 +83,46 @@ static const CodeTestMap value_tests = { Node::NodeType::ValueFloatNode, { - { ".123456789f;", Node::Ptr(new Value(".123456789f")) }, - { "0.0f;", Node::Ptr(new Value("0.0f")) }, - { "00.f;", Node::Ptr(new Value("0.0f")) }, - { "0e+0f;", Node::Ptr(new Value("0.0f")) }, - { "0e-0f;", Node::Ptr(new Value("0.0f")) }, - { "0e0f;", Node::Ptr(new Value("0.0f")) }, - { "1234567890.0987654321f;", Node::Ptr(new Value("1234567890.0987654321f")) }, - { "1e+6f;", Node::Ptr(new Value("1e+6f")) }, - { "1E+6f;", Node::Ptr(new Value("1E+6f")) }, - { "1e-6f;", Node::Ptr(new Value("1e-6f")) }, - { "1E-6f;", Node::Ptr(new Value("1E-6f")) }, - { "1e123456789f;", Node::Ptr(new Value("1e123456789f")) }, // overflow - { "1e6f;", Node::Ptr(new Value("1e6f")) }, - { "1E6f;", Node::Ptr(new Value("1E6f")) } + { ".123456789f;", Node::Ptr(new Value(convertf(".123456789f"))) }, + { "0.0f;", Node::Ptr(new Value(convertf("0.0f"))) }, + { "00.f;", Node::Ptr(new Value(convertf("0.0f"))) }, + { "0e+0f;", Node::Ptr(new Value(convertf("0.0f"))) }, + { "0e-0f;", Node::Ptr(new Value(convertf("0.0f"))) }, + { "0e0f;", Node::Ptr(new Value(convertf("0.0f"))) }, + { "1234567890.0987654321f;", Node::Ptr(new Value(convertf("1234567890.0987654321f"))) }, + { "1e+6f;", Node::Ptr(new Value(convertf("1e+6f"))) }, + { "1E+6f;", Node::Ptr(new Value(convertf("1E+6f"))) }, + { "1e-6f;", Node::Ptr(new Value(convertf("1e-6f"))) }, + { "1E-6f;", Node::Ptr(new Value(convertf("1E-6f"))) }, + { "1e6f;", Node::Ptr(new Value(convertf("1e6f"))) }, + { "1E6f;", Node::Ptr(new Value(convertf("1E6f"))) } } }, { Node::NodeType::ValueDoubleNode, { - { ".123456789;", Node::Ptr(new Value(".123456789")) }, - { "0.0;", Node::Ptr(new Value("0.0")) }, - { "0e0;", Node::Ptr(new Value("0.0f")) }, - { "1.0;", Node::Ptr(new Value("1.0")) }, - { "1234567890.00000000;", Node::Ptr(new Value("1234567890.0")) }, - { "1234567890.0987654321;", Node::Ptr(new Value("1234567890.0987654321")) }, - { "1234567890.10000000;", Node::Ptr(new Value("1234567890.1")) }, - { "1234567890e-0;", Node::Ptr(new Value("1234567890e-0")) }, - { "1e+6;", Node::Ptr(new Value("1e+6")) }, - { "1e-6;", Node::Ptr(new Value("1e-6")) }, - { "1e01;", Node::Ptr(new Value("1e01")) }, - { "1e123456789;", Node::Ptr(new Value("1e123456789")) }, // overflow - { "1e6;", Node::Ptr(new Value("1e6")) }, - { "1E6;", Node::Ptr(new Value("1E6")) }, + { ".123456789;", Node::Ptr(new Value(convertd(".123456789"))) }, + { "0.0;", Node::Ptr(new Value(convertd("0.0"))) }, + { "0e0;", Node::Ptr(new Value(convertd("0.0f"))) }, + { "1.0;", Node::Ptr(new Value(convertd("1.0"))) }, + { "1234567890.00000000;", Node::Ptr(new Value(convertd("1234567890.0"))) }, + { "1234567890.0987654321;", Node::Ptr(new Value(convertd("1234567890.0987654321"))) }, + { "1234567890.10000000;", Node::Ptr(new Value(convertd("1234567890.1"))) }, + { "1234567890e-0;", Node::Ptr(new Value(convertd("1234567890e-0"))) }, + { "1e+6;", Node::Ptr(new Value(convertd("1e+6"))) }, + { "1e-6;", Node::Ptr(new Value(convertd("1e-6"))) }, + { "1e01;", Node::Ptr(new Value(convertd("1e01"))) }, + { "1e6;", Node::Ptr(new Value(convertd("1e6"))) }, + { "1E6;", Node::Ptr(new Value(convertd("1E6"))) }, { std::to_string(std::numeric_limits::max()) + ";", - Node::Ptr(new Value(std::to_string(std::numeric_limits::max()))) + Node::Ptr(new Value(std::numeric_limits::max())) + }, + { fullDecimalValue(std::numeric_limits::max()) + ".0;", + Node::Ptr(new Value(std::numeric_limits::max())) }, - { std::to_string(std::numeric_limits::min()) + ";", - Node::Ptr(new Value(std::to_string(std::numeric_limits::min()))) + { fullDecimalValue(std::numeric_limits::min()) + ";", + Node::Ptr(new Value(std::numeric_limits::min())) } } }, diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign.cc b/openvdb_ax/openvdb_ax/test/integration/TestAssign.cc index 1c201f43ff..017a7ed6bd 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign.cc @@ -19,9 +19,8 @@ using ConfigMap = std::unordered_map(names, - { 2, 2, 2, 2, 3, 3, 2, 2 }); - }, - }, - { "int", + { "int32", [&](){ mHarness.addAttributes(names, { 2, 2, 2, 2, 3, 3, 2, 2 }); }, }, - { "long", + { "int64", [&](){ mHarness.addAttributes(names, { 2, 2, 2, 2, 3, 3, 2, 2 }); }, @@ -413,17 +407,7 @@ _T1_@test14 += local2; }, } }, - { "short", { - [&](){ mHarness.addAttributes(names, - { 2, -2, 0, 0, 0, 0, 0, 0, 2, 2, 5, 0, 0, 3 }); - }, - [&](){ mHarness.addAttributes(names, - { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 12, 13, 14 }, // in - { 3, 0, 6, 2, 1, 24, 1, 0, 11, 10, 5, 25, 13, 17 }); // expected - }, - } - }, - { "int", { + { "int32", { [&](){ mHarness.addAttributes(names, { 2, -2, 0, 0, 0, 0, 0, 0, 2, 2, 5, 0, 0, 3 }); }, @@ -433,7 +417,7 @@ _T1_@test14 += local2; }, } }, - { "long", { + { "int64", { [&](){ mHarness.addAttributes(names, { 2, -2, 0, 0, 0, 0, 0, 0, 2, 2, 5, 0, 0, 3 }); }, @@ -1159,50 +1143,37 @@ TestAssign::implicitScalarAssignment() // source -> dest const std::map> expected = { { "bool", [&](){ - mHarness.addAttribute("testshort", 1); - mHarness.addAttribute("testint", 1); - mHarness.addAttribute("testlong", 1); + mHarness.addAttribute("testint32", 1); + mHarness.addAttribute("testint64", 1); mHarness.addAttribute("testfloat", 1.0f); mHarness.addAttribute("testdouble", 1.0); } }, - { "short", [&](){ - mHarness.addAttribute("testbool", true); - mHarness.addAttribute("testint", 2); - mHarness.addAttribute("testlong", 2); - mHarness.addAttribute("testfloat", 2.0f); - mHarness.addAttribute("testdouble", 2.0); - } - }, - { "int", [&](){ + { "int32", [&](){ mHarness.addAttribute("testbool", true); - mHarness.addAttribute("testshort", 2); - mHarness.addAttribute("testlong", 2); + mHarness.addAttribute("testint64", 2); mHarness.addAttribute("testfloat", 2.0f); mHarness.addAttribute("testdouble", 2.0); } }, - { "long", [&](){ + { "int64", [&](){ mHarness.addAttribute("testbool", true); - mHarness.addAttribute("testshort", 2); - mHarness.addAttribute("testint", 2); + mHarness.addAttribute("testint32", 2); mHarness.addAttribute("testfloat", 2.0f); mHarness.addAttribute("testdouble", 2.0); } }, { "float", [&](){ mHarness.addAttribute("testbool", true); - mHarness.addAttribute("testshort", 1); - mHarness.addAttribute("testint", 1); - mHarness.addAttribute("testlong", 1); + mHarness.addAttribute("testint32", 1); + mHarness.addAttribute("testint64", 1); mHarness.addAttribute("testdouble", double(1.1f)); } }, { "double", [&](){ mHarness.addAttribute("testbool", true); - mHarness.addAttribute("testshort", 1); - mHarness.addAttribute("testint", 1); - mHarness.addAttribute("testlong", 1); + mHarness.addAttribute("testint32", 1); + mHarness.addAttribute("testint64", 1); mHarness.addAttribute("testfloat", float(1.1)); } } @@ -1397,23 +1368,7 @@ TestAssign::implicitContainerScalarAssignment() mHarness.addAttribute>("testmat4d", symmetric4(1.0)); } }, - { "short", [&]() { - mHarness.addAttribute>("testvec2i", openvdb::math::Vec2(2,2)); - mHarness.addAttribute>("testvec2d", openvdb::math::Vec2(2.0,2.0)); - mHarness.addAttribute>("testvec2f", openvdb::math::Vec2(2.0f,2.0f)); - mHarness.addAttribute>("testvec3i", openvdb::math::Vec3(2,2,2)); - mHarness.addAttribute>("testvec3d", openvdb::math::Vec3(2.0,2.0,2.0)); - mHarness.addAttribute>("testvec3f", openvdb::math::Vec3(2.0f,2.0f,2.0f)); - mHarness.addAttribute>("testvec4i", openvdb::math::Vec4(2,2,2,2)); - mHarness.addAttribute>("testvec4d", openvdb::math::Vec4(2.0,2.0,2.0,2.0)); - mHarness.addAttribute>("testvec4f", openvdb::math::Vec4(2.0f,2.0f,2.0f,2.0f)); - mHarness.addAttribute>("testmat3f", symmetric3(2.0f)); - mHarness.addAttribute>("testmat3d", symmetric3(2.0)); - mHarness.addAttribute>("testmat4f", symmetric4(2.0f)); - mHarness.addAttribute>("testmat4d", symmetric4(2.0)); - } - }, - { "int", [&](){ + { "int32", [&](){ mHarness.addAttribute>("testvec2i", openvdb::math::Vec2(2,2)); mHarness.addAttribute>("testvec2d", openvdb::math::Vec2(2.0,2.0)); mHarness.addAttribute>("testvec2f", openvdb::math::Vec2(2.0f,2.0f)); @@ -1429,7 +1384,7 @@ TestAssign::implicitContainerScalarAssignment() mHarness.addAttribute>("testmat4d", symmetric4(2.0)); } }, - { "long", [&](){ + { "int64", [&](){ mHarness.addAttribute>("testvec2i", openvdb::math::Vec2(2,2)); mHarness.addAttribute>("testvec2d", openvdb::math::Vec2(2.0,2.0)); mHarness.addAttribute>("testvec2f", openvdb::math::Vec2(2.0f,2.0f)); diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int.ax deleted file mode 100644 index 1dfe428089..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int.ax +++ /dev/null @@ -1,17 +0,0 @@ - -int@test1 = 2; -int local1 = 2; -int@test2 = local1; -int@test3 = - int@test4 = - int@test2; -int local3, - local2 = 3; -int@test5 = - local3 = - local2; -int@test6 = 3, - int@test7 = 2; -int@test8 = 3; -int@test8 = 2; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int32.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int32.ax new file mode 100644 index 0000000000..c2820925f1 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int32.ax @@ -0,0 +1,17 @@ + +int32@test1 = 2; +int32 local1 = 2; +int32@test2 = local1; +int32@test3 = + int32@test4 = + int32@test2; +int32 local3, + local2 = 3; +int32@test5 = + local3 = + local2; +int32@test6 = 3, + int32@test7 = 2; +int32@test8 = 3; +int32@test8 = 2; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int64.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int64.ax new file mode 100644 index 0000000000..22680e2d7b --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.int64.ax @@ -0,0 +1,17 @@ + +int64@test1 = 2l; +int64 local1 = 2l; +int64@test2 = local1; +int64@test3 = + int64@test4 = + int64@test2; +int64 local3, + local2 = 3l; +int64@test5 = + local3 = + local2; +int64@test6 = 3l, + int64@test7 = 2l; +int64@test8 = 3l; +int64@test8 = 2l; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.long.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.long.ax deleted file mode 100644 index 32eb4b0631..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.long.ax +++ /dev/null @@ -1,17 +0,0 @@ - -long@test1 = 2l; -long local1 = 2l; -long@test2 = local1; -long@test3 = - long@test4 = - long@test2; -long local3, - local2 = 3l; -long@test5 = - local3 = - local2; -long@test6 = 3l, - long@test7 = 2l; -long@test8 = 3l; -long@test8 = 2l; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.short.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.short.ax deleted file mode 100644 index 5e110a98a8..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign.short.ax +++ /dev/null @@ -1,17 +0,0 @@ - -short@test1 = 2s; -short local1 = 2s; -short@test2 = local1; -short@test3 = - short@test4 = - short@test2; -short local3, - local2 = 3s; -short@test5 = - local3 = - local2; -short@test6 = 3s, - short@test7 = 2s; -short@test8 = 3s; -short@test8 = 2s; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int.ax deleted file mode 100644 index ec63363ca9..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int.ax +++ /dev/null @@ -1,20 +0,0 @@ - -int@test1 += 2; -int@test2 -= 2; -int@test3 *= 2; -int@test4 /= 2; -int@test5 %= 2; -int@test6 <<= 2; -int@test7 >>= 2; -int@test8 &= 2; -int@test9 ^= 2; -int@test10 |= 2; - -int local1 = 2, - local2 = 3; - -local1 += local2; -int@test11 = local1; -int@test12 += int@test13; -int@test14 += local2; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int32.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int32.ax new file mode 100644 index 0000000000..04122ae7e7 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int32.ax @@ -0,0 +1,20 @@ + +int32@test1 += 2; +int32@test2 -= 2; +int32@test3 *= 2; +int32@test4 /= 2; +int32@test5 %= 2; +int32@test6 <<= 2; +int32@test7 >>= 2; +int32@test8 &= 2; +int32@test9 ^= 2; +int32@test10 |= 2; + +int32 local1 = 2, + local2 = 3; + +local1 += local2; +int32@test11 = local1; +int32@test12 += int32@test13; +int32@test14 += local2; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int64.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int64.ax new file mode 100644 index 0000000000..de923c4328 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.int64.ax @@ -0,0 +1,20 @@ + +int64@test1 += 2l; +int64@test2 -= 2l; +int64@test3 *= 2l; +int64@test4 /= 2l; +int64@test5 %= 2l; +int64@test6 <<= 2l; +int64@test7 >>= 2l; +int64@test8 &= 2l; +int64@test9 ^= 2l; +int64@test10 |= 2l; + +int64 local1 = 2l, + local2 = 3l; + +local1 += local2; +int64@test11 = local1; +int64@test12 += int64@test13; +int64@test14 += local2; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.long.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.long.ax deleted file mode 100644 index c0fe2dcb13..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.long.ax +++ /dev/null @@ -1,20 +0,0 @@ - -long@test1 += 2l; -long@test2 -= 2l; -long@test3 *= 2l; -long@test4 /= 2l; -long@test5 %= 2l; -long@test6 <<= 2l; -long@test7 >>= 2l; -long@test8 &= 2l; -long@test9 ^= 2l; -long@test10 |= 2l; - -long local1 = 2l, - local2 = 3l; - -local1 += local2; -long@test11 = local1; -long@test12 += long@test13; -long@test14 += local2; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.short.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.short.ax deleted file mode 100644 index 91b71f0a57..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_compound.short.ax +++ /dev/null @@ -1,20 +0,0 @@ - -short@test1 += 2s; -short@test2 -= 2s; -short@test3 *= 2s; -short@test4 /= 2s; -short@test5 %= 2s; -short@test6 <<= 2s; -short@test7 >>= 2s; -short@test8 &= 2s; -short@test9 ^= 2s; -short@test10 |= 2s; - -short local1 = 2s, - local2 = 3s; - -local1 += local2; -short@test11 = local1; -short@test12 += short@test13; -short@test14 += local2; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int32.ax similarity index 95% rename from openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int.ax rename to openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int32.ax index bb4f53afe1..f16eca1ffd 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int32.ax @@ -1,4 +1,4 @@ -int local = 2; +int32 local = 2; vec2d@testvec2d = local; vec2i@testvec2i = local; vec2f@testvec2f = local; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.long.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int64.ax similarity index 94% rename from openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.long.ax rename to openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int64.ax index 65d2b32740..a4dd07c364 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.long.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.int64.ax @@ -1,4 +1,4 @@ -long local = 2l; +int64 local = 2l; vec2d@testvec2d = local; vec2i@testvec2i = local; vec2f@testvec2f = local; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.short.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.short.ax deleted file mode 100644 index 338e111102..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_container_scalar.short.ax +++ /dev/null @@ -1,15 +0,0 @@ -short local = 2s; -vec2d@testvec2d = local; -vec2i@testvec2i = local; -vec2f@testvec2f = local; -vec3d@testvec3d = local; -vec3i@testvec3i = local; -vec3f@testvec3f = local; -vec4d@testvec4d = local; -vec4i@testvec4i = local; -vec4f@testvec4f = local; -mat3d@testmat3d = local; -mat3f@testmat3f = local; -mat4d@testmat4d = local; -mat4f@testmat4f = local; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.bool.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.bool.ax index 31fd4573e0..cd6f6d7ade 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.bool.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.bool.ax @@ -1,7 +1,6 @@ bool local = true; -long@testlong = local; -int@testint = local; -short@testshort = local; +int64@testint64 = local; +int32@testint32 = local; double@testdouble = local; float@testfloat = local; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.double.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.double.ax index 4083ff3184..bdded7be3e 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.double.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.double.ax @@ -1,7 +1,6 @@ double local = 1.1; -long@testlong = local; -int@testint = local; -short@testshort = local; +int64@testint64 = local; bool@testbool = local; +int32@testint32 = local; float@testfloat = local; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.float.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.float.ax index ea9a423150..6eab43b318 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.float.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.float.ax @@ -1,7 +1,6 @@ float local = 1.1f; -long@testlong = local; -int@testint = local; -short@testshort = local; +int64@testint64 = local; bool@testbool = local; +int32@testint32 = local; double@testdouble = local; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.short.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int32.ax similarity index 55% rename from openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.short.ax rename to openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int32.ax index fe7c01497c..34a8d7e4a2 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.short.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int32.ax @@ -1,6 +1,5 @@ -short local = 2s; -long@testlong = local; -int@testint = local; +int32 local = 2; +int64@testint64 = local; bool@testbool = local; double@testdouble = local; float@testfloat = local; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int64.ax similarity index 54% rename from openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int.ax rename to openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int64.ax index fb29da6ed2..b411690c35 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.int64.ax @@ -1,7 +1,6 @@ -int local = 2; -long@testlong = local; -short@testshort = local; +int64 local = 2l; bool@testbool = local; +int32@testint32 = local; double@testdouble = local; float@testfloat = local; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.long.ax b/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.long.ax deleted file mode 100644 index c5fc30d043..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestAssign/assign_implicit_scalar.long.ax +++ /dev/null @@ -1,7 +0,0 @@ -long local = 2l; -int@testint = local; -short@testshort = local; -bool@testbool = local; -double@testdouble = local; -float@testfloat = local; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary.cc b/openvdb_ax/openvdb_ax/test/integration/TestBinary.cc index 0d60d524ac..c69dc5abd2 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary.cc @@ -15,9 +15,9 @@ using ConfigMap = std::unordered_map> expected = { { "bool", [&](){ mHarness.addAttribute("testbool", true); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 5); } }, - { "int", [&](){ mHarness.addAttribute("testint", 5); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 5); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 5); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 5); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 5); } }, { "float", [&](){ mHarness.addAttribute("testfloat", 1.1f + 2.3f); } }, { "double", [&](){ mHarness.addAttribute("testdouble", 1.1 + 2.3); } }, { "vec2i", [&](){ mHarness.addAttribute>("testvec2i", openvdb::math::Vec2(4,6)); } }, @@ -217,9 +217,9 @@ _T1_@_A1_ = _L1_ - _L2_;)"; const std::map> expected = { { "bool", [&](){ mHarness.addAttribute("testbool", true); } }, - { "short", [&](){ mHarness.addAttribute("testshort", -1); } }, - { "int", [&](){ mHarness.addAttribute("testint", -1); } }, - { "long", [&](){ mHarness.addAttribute("testlong", -1); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", -1); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", -1); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", -1); } }, { "float", [&](){ mHarness.addAttribute("testfloat", 1.1f - 2.3f); } }, { "double", [&](){ mHarness.addAttribute("testdouble", 1.1 - 2.3); } }, { "vec2i", [&](){ mHarness.addAttribute>("testvec2i", openvdb::math::Vec2(-2,-2)); } }, @@ -285,9 +285,9 @@ _T1_@_A1_ = _L1_ * _L2_;)"; const std::map> expected = { { "bool", [&](){ mHarness.addAttribute("testbool", false); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 6); } }, - { "int", [&](){ mHarness.addAttribute("testint", 6); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 6); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 6); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 6); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 6); } }, { "float", [&](){ mHarness.addAttribute("testfloat", 1.1f * 2.3f); } }, { "double", [&](){ mHarness.addAttribute("testdouble", 1.1 * 2.3); } }, { "vec2i", [&](){ mHarness.addAttribute>("testvec2i", openvdb::math::Vec2(3,8)); } }, @@ -353,9 +353,9 @@ _T1_@_A1_ = _L2_ / _L1_;)"; const std::map> expected = { { "bool", [&](){ mHarness.addAttribute("testbool", false); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 1); } }, - { "int", [&](){ mHarness.addAttribute("testint", 1); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 1); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 1); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 1); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 1); } }, { "float", [&](){ mHarness.addAttribute("testfloat", 2.3f/1.1f); } }, { "double", [&](){ mHarness.addAttribute("testdouble", 2.3/1.1); } }, { "vec2i", [&](){ mHarness.addAttribute>("testvec2i", openvdb::math::Vec2(3,2)); } }, @@ -405,9 +405,9 @@ _T1_@_A1_ = _L2_ % _L1_;)"; const std::map> expected = { { "bool", [&](){ mHarness.addAttribute("testbool", false); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 1); } }, - { "int", [&](){ mHarness.addAttribute("testint", 1); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 1); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 1); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 1); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 1); } }, { "float", [&](){ mHarness.addAttribute("testfloat", std::fmod(2.3f,1.1f)); } }, { "double", [&](){ mHarness.addAttribute("testdouble", std::fmod(2.3,1.1)); } }, { "vec2i", [&](){ mHarness.addAttribute>("testvec2i", openvdb::math::Vec2(0,0)); } }, @@ -452,9 +452,9 @@ _T1_@_A1_ = _L1_ & _L2_;)"; const std::map> expected = { { "bool", [&](){ mHarness.addAttribute("testbool", false); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 2); } }, - { "int", [&](){ mHarness.addAttribute("testint", 2); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 2); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 2); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 2); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 2); } }, }; for (const auto& expc : expected) { @@ -487,10 +487,10 @@ _T1_@_A1_ = _L1_ | _L2_;)"; this->registerTest(repl, "binary_bitor.ax"); const std::map> expected = { - { "bool", [&](){ mHarness.addAttribute("testbool", true); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 3); } }, - { "int", [&](){ mHarness.addAttribute("testint", 3); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 3); } }, + { "bool", [&](){ mHarness.addAttribute("testbool", true); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 3); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 3); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 3); } }, }; for (const auto& expc : expected) { @@ -523,10 +523,10 @@ _T1_@_A1_ = _L1_ ^ _L2_;)"; this->registerTest(repl, "binary_bitxor.ax"); const std::map> expected = { - { "bool", [&](){ mHarness.addAttribute("testbool", true); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 1); } }, - { "int", [&](){ mHarness.addAttribute("testint", 1); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 1); } }, + { "bool", [&](){ mHarness.addAttribute("testbool", true); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 1); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 1); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 1); } }, }; for (const auto& expc : expected) { @@ -561,9 +561,9 @@ _T1_@_A1_ = _L1_ && _L2_;)"; const std::map> expected = { { "bool", [&](){ mHarness.addAttribute("testbool", false); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 1); } }, - { "int", [&](){ mHarness.addAttribute("testint", 1); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 1); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 1); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 1); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 1); } }, { "float", [&](){ mHarness.addAttribute("testfloat", 1.0f); } }, { "double", [&](){ mHarness.addAttribute("testdouble", 1.0); } }, }; @@ -572,6 +572,24 @@ _T1_@_A1_ = _L1_ && _L2_;)"; expc.second.operator()(); } this->execute("binary_logicaland.ax"); + + // Also test short circuiting logical op for && + + mHarness.reset(); + this->registerTest(R"( +int@scircuit1 = 0; +int@scircuit2 = 1; +int@scircuit3 = 2; +int@scircuit1++ && ++int@scircuit2; +++int@scircuit1 && ++int@scircuit3; +int@scircuit4 = 1; +int@scircuit5 = 1; +true && int@scircuit4 = 2; +false && int@scircuit5 = 2;)", + "binary_logicaland_scircuit.ax"); + + mHarness.addAttributes(unittest_util::nameSequence("scircuit", 5), { 2, 1, 3, 2, 1 }); + this->execute("binary_logicaland_scircuit.ax"); } @@ -600,9 +618,9 @@ _T1_@_A1_ = _L1_ || _L2_;)"; const std::map> expected = { { "bool", [&](){ mHarness.addAttribute("testbool", true); } }, - { "short", [&](){ mHarness.addAttribute("testshort", 1); } }, - { "int", [&](){ mHarness.addAttribute("testint", 1); } }, - { "long", [&](){ mHarness.addAttribute("testlong", 1); } }, + { "int16", [&](){ mHarness.addAttribute("testint16", 1); } }, + { "int32", [&](){ mHarness.addAttribute("testint32", 1); } }, + { "int64", [&](){ mHarness.addAttribute("testint64", 1); } }, { "float", [&](){ mHarness.addAttribute("testfloat", 1.0f); } }, { "double", [&](){ mHarness.addAttribute("testdouble", 1.0); } }, }; @@ -611,6 +629,24 @@ _T1_@_A1_ = _L1_ || _L2_;)"; expc.second.operator()(); } this->execute("binary_logicalor.ax"); + + // Also test short circuiting logical op for || + + mHarness.reset(); + this->registerTest(R"( +int@scircuit1 = 0; +int@scircuit2 = 1; +int@scircuit3 = 2; +int@scircuit1++ || ++int@scircuit2; +++int@scircuit1 || ++int@scircuit3; +int@scircuit4 = 1; +int@scircuit5 = 1; +true || int@scircuit4 = 2; +false || int@scircuit5 = 2;)", + "binary_logicalor_scircuit.ax"); + + mHarness.addAttributes(unittest_util::nameSequence("scircuit", 5), { 2, 2, 2, 1, 2 }); + this->execute("binary_logicalor_scircuit.ax"); } @@ -963,19 +999,19 @@ _T1_@_A2_ = _L2_ << _L1_;)"; mHarness.addAttribute("testbool2", false); } }, - { "short", [&](){ - mHarness.addAttribute("testshort1", 16); - mHarness.addAttribute("testshort2", 12); + { "int16", [&](){ + mHarness.addAttribute("testint161", 16); + mHarness.addAttribute("testint162", 12); } }, - { "int", [&](){ - mHarness.addAttribute("testint1", 16); - mHarness.addAttribute("testint2", 12); + { "int32", [&](){ + mHarness.addAttribute("testint321", 16); + mHarness.addAttribute("testint322", 12); } }, - { "long", [&](){ - mHarness.addAttribute("testlong1", 16); - mHarness.addAttribute("testlong2", 12); + { "int64", [&](){ + mHarness.addAttribute("testint641", 16); + mHarness.addAttribute("testint642", 12); } }, }; @@ -1017,19 +1053,19 @@ _T1_@_A2_ = _L2_ >> _L1_;)"; mHarness.addAttribute("testbool2", false); } }, - { "short", [&](){ - mHarness.addAttribute("testshort1", 0); - mHarness.addAttribute("testshort2", 0); + { "int16", [&](){ + mHarness.addAttribute("testint161", 0); + mHarness.addAttribute("testint162", 0); } }, { "int", [&](){ - mHarness.addAttribute("testint1", 0); - mHarness.addAttribute("testint2", 0); + mHarness.addAttribute("testint321", 0); + mHarness.addAttribute("testint322", 0); } }, - { "long", [&](){ - mHarness.addAttribute("testlong1", 0); - mHarness.addAttribute("testlong2", 0); + { "int64", [&](){ + mHarness.addAttribute("testint641", 0); + mHarness.addAttribute("testint642", 0); } }, }; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitand.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitand.ax index 3fc42ed475..6ccb54550c 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitand.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitand.ax @@ -1,5 +1,5 @@ -long@testlong = 2l & 3l; -int@testint = 2 & 3; -short@testshort = 2s & 3s; +int64@testint64 = 2l & 3l; +int32@testint32 = 2 & 3; +int16@testint16 = 2 & 3; bool@testbool = true & false; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitor.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitor.ax index 792a298240..ad442278a1 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitor.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitor.ax @@ -1,5 +1,5 @@ -long@testlong = 2l | 3l; -int@testint = 2 | 3; -short@testshort = 2s | 3s; +int64@testint64 = 2l | 3l; +int32@testint32 = 2 | 3; +int16@testint16 = 2 | 3; bool@testbool = true | false; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitxor.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitxor.ax index e3671e1f7f..ed3e946624 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitxor.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_bitxor.ax @@ -1,5 +1,5 @@ -long@testlong = 2l ^ 3l; -int@testint = 2 ^ 3; -short@testshort = 2s ^ 3s; +int64@testint64 = 2l ^ 3l; +int32@testint32 = 2 ^ 3; +int16@testint16 = 2 ^ 3; bool@testbool = true ^ false; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_div.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_div.ax index cdc8b9b9b5..d12810cbf4 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_div.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_div.ax @@ -1,7 +1,7 @@ -long@testlong = 3l / 2l; -int@testint = 3 / 2; -short@testshort = 3s / 2s; +int64@testint64 = 3l / 2l; +int32@testint32 = 3 / 2; +int16@testint16 = 3 / 2; bool@testbool = false / true; double@testdouble = 2.3 / 1.1; float@testfloat = 2.3f / 1.1f; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicaland.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicaland.ax index c5ca1f3c2b..c46b50fed6 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicaland.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicaland.ax @@ -1,7 +1,7 @@ -long@testlong = 2l && 3l; -int@testint = 2 && 3; -short@testshort = 2s && 3s; +int64@testint64 = 2l && 3l; +int32@testint32 = 2 && 3; +int16@testint16 = 2 && 3; bool@testbool = true && false; double@testdouble = 1.1 && 2.3; float@testfloat = 1.1f && 2.3f; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicaland_scircuit.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicaland_scircuit.ax new file mode 100644 index 0000000000..2d7a002fe5 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicaland_scircuit.ax @@ -0,0 +1,10 @@ + +int@scircuit1 = 0; +int@scircuit2 = 1; +int@scircuit3 = 2; +int@scircuit1++ && ++int@scircuit2; +++int@scircuit1 && ++int@scircuit3; +int@scircuit4 = 1; +int@scircuit5 = 1; +true && int@scircuit4 = 2; +false && int@scircuit5 = 2; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicalor.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicalor.ax index 1bb75b72d9..ee9646ca28 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicalor.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicalor.ax @@ -1,7 +1,7 @@ -long@testlong = 2l || 3l; -int@testint = 2 || 3; -short@testshort = 2s || 3s; +int64@testint64 = 2l || 3l; +int32@testint32 = 2 || 3; +int16@testint16 = 2 || 3; bool@testbool = true || false; double@testdouble = 1.1 || 2.3; float@testfloat = 1.1f || 2.3f; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicalor_scircuit.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicalor_scircuit.ax new file mode 100644 index 0000000000..74bfb3292f --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_logicalor_scircuit.ax @@ -0,0 +1,10 @@ + +int@scircuit1 = 0; +int@scircuit2 = 1; +int@scircuit3 = 2; +int@scircuit1++ || ++int@scircuit2; +++int@scircuit1 || ++int@scircuit3; +int@scircuit4 = 1; +int@scircuit5 = 1; +true || int@scircuit4 = 2; +false || int@scircuit5 = 2; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_minus.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_minus.ax index 9a3fc3fa5d..d7984cc108 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_minus.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_minus.ax @@ -1,7 +1,7 @@ -long@testlong = 2l - 3l; -int@testint = 2 - 3; -short@testshort = 2s - 3s; +int64@testint64 = 2l - 3l; +int32@testint32 = 2 - 3; +int16@testint16 = 2 - 3; bool@testbool = true - false; double@testdouble = 1.1 - 2.3; float@testfloat = 1.1f - 2.3f; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_mod.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_mod.ax index f237bc7d2e..806cf0d3c3 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_mod.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_mod.ax @@ -1,7 +1,7 @@ -long@testlong = 3l % 2l; -int@testint = 3 % 2; -short@testshort = 3s % 2s; +int64@testint64 = 3l % 2l; +int32@testint32 = 3 % 2; +int16@testint16 = 3 % 2; bool@testbool = false % true; double@testdouble = 2.3 % 1.1; float@testfloat = 2.3f % 1.1f; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_mult.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_mult.ax index 42a59f4ac3..2adcbcb8bc 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_mult.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_mult.ax @@ -1,7 +1,7 @@ -long@testlong = 2l * 3l; -int@testint = 2 * 3; -short@testshort = 2s * 3s; +int64@testint64 = 2l * 3l; +int32@testint32 = 2 * 3; +int16@testint16 = 2 * 3; bool@testbool = true * false; double@testdouble = 1.1 * 2.3; float@testfloat = 1.1f * 2.3f; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_plus.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_plus.ax index 891d810151..8578465634 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_plus.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_plus.ax @@ -1,7 +1,7 @@ -long@testlong = 2l + 3l; -int@testint = 2 + 3; -short@testshort = 2s + 3s; +int64@testint64 = 2l + 3l; +int32@testint32 = 2 + 3; +int16@testint16 = 2 + 3; bool@testbool = true + false; double@testdouble = 1.1 + 2.3; float@testfloat = 1.1f + 2.3f; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_equalsequals.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_equalsequals.ax index 9921274ad4..6a0de35e0b 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_equalsequals.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_equalsequals.ax @@ -3,8 +3,8 @@ bool@test1 = 2l == 3l; bool@test2 = 3l == 3l; bool@test3 = 2 == 3; bool@test4 = 3 == 3; -bool@test5 = 2s == 3s; -bool@test6 = 3s == 3s; +bool@test5 = 2 == 3; +bool@test6 = 3 == 3; bool@test7 = true == false; bool@test8 = false == false; bool@test9 = 1.1 == 2.3; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_greaterthan.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_greaterthan.ax index 5135cb9e12..bcc4bd8155 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_greaterthan.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_greaterthan.ax @@ -5,9 +5,9 @@ bool@test3 = 3l > 3l; bool@test4 = 2 > 3; bool@test5 = 3 > 2; bool@test6 = 3 > 3; -bool@test7 = 2s > 3s; -bool@test8 = 3s > 2s; -bool@test9 = 3s > 3s; +bool@test7 = 2 > 3; +bool@test8 = 3 > 2; +bool@test9 = 3 > 3; bool@test10 = true > false; bool@test11 = false > true; bool@test12 = false > false; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_greaterthanequals.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_greaterthanequals.ax index d5e84f1fae..0b499e63b7 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_greaterthanequals.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_greaterthanequals.ax @@ -5,9 +5,9 @@ bool@test3 = 3l >= 3l; bool@test4 = 2 >= 3; bool@test5 = 3 >= 2; bool@test6 = 3 >= 3; -bool@test7 = 2s >= 3s; -bool@test8 = 3s >= 2s; -bool@test9 = 3s >= 3s; +bool@test7 = 2 >= 3; +bool@test8 = 3 >= 2; +bool@test9 = 3 >= 3; bool@test10 = true >= false; bool@test11 = false >= true; bool@test12 = false >= false; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_lessthan.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_lessthan.ax index ac108072a4..9ba13f453e 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_lessthan.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_lessthan.ax @@ -5,9 +5,9 @@ bool@test3 = 3l < 3l; bool@test4 = 2 < 3; bool@test5 = 3 < 2; bool@test6 = 3 < 3; -bool@test7 = 2s < 3s; -bool@test8 = 3s < 2s; -bool@test9 = 3s < 3s; +bool@test7 = 2 < 3; +bool@test8 = 3 < 2; +bool@test9 = 3 < 3; bool@test10 = true < false; bool@test11 = false < true; bool@test12 = false < false; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_lessthanequals.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_lessthanequals.ax index 92708864bb..648f7c23e3 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_lessthanequals.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_lessthanequals.ax @@ -5,9 +5,9 @@ bool@test3 = 3l <= 3l; bool@test4 = 2 <= 3; bool@test5 = 3 <= 2; bool@test6 = 3 <= 3; -bool@test7 = 2s <= 3s; -bool@test8 = 3s <= 2s; -bool@test9 = 3s <= 3s; +bool@test7 = 2 <= 3; +bool@test8 = 3 <= 2; +bool@test9 = 3 <= 3; bool@test10 = true <= false; bool@test11 = false <= true; bool@test12 = false <= false; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_notequals.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_notequals.ax index 5e4693487b..ba9ed72005 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_notequals.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_relational_notequals.ax @@ -3,8 +3,8 @@ bool@test1 = 2l != 3l; bool@test2 = 3l != 3l; bool@test3 = 2 != 3; bool@test4 = 3 != 3; -bool@test5 = 2s != 3s; -bool@test6 = 3s != 3s; +bool@test5 = 2 != 3; +bool@test6 = 3 != 3; bool@test7 = true != false; bool@test8 = false != false; bool@test9 = 1.1 != 2.3; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_shiftleft.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_shiftleft.ax index 2543295587..22a77bcec6 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_shiftleft.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_shiftleft.ax @@ -1,9 +1,9 @@ -long@testlong1 = 2l << 3l; -long@testlong2 = 3l << 2l; -int@testint1 = 2 << 3; -int@testint2 = 3 << 2; -short@testshort1 = 2s << 3s; -short@testshort2 = 3s << 2s; +int64@testint641 = 2l << 3l; +int64@testint642 = 3l << 2l; +int32@testint321 = 2 << 3; +int32@testint322 = 3 << 2; +int16@testint161 = 2 << 3; +int16@testint162 = 3 << 2; bool@testbool1 = true << false; bool@testbool2 = false << true; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_shiftright.ax b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_shiftright.ax index b081cad14c..4996888fdc 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_shiftright.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestBinary/binary_shiftright.ax @@ -1,9 +1,9 @@ -long@testlong1 = 2l >> 3l; -long@testlong2 = 3l >> 2l; -int@testint1 = 2 >> 3; -int@testint2 = 3 >> 2; -short@testshort1 = 2s >> 3s; -short@testshort2 = 3s >> 2s; +int64@testint641 = 2l >> 3l; +int64@testint642 = 3l >> 2l; +int32@testint321 = 2 >> 3; +int32@testint322 = 3 >> 2; +int16@testint161 = 2 >> 3; +int16@testint162 = 3 >> 2; bool@testbool1 = true >> false; bool@testbool2 = false >> true; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast.cc b/openvdb_ax/openvdb_ax/test/integration/TestCast.cc index 863a832162..f974c58371 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestCast.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestCast.cc @@ -46,62 +46,48 @@ TestCast::explicitScalar() } }; - generate(std::vector{ "bool", "short", "int", "long", "float", "double" }); + generate(std::vector{ "bool", "int32", "int64", "float", "double" }); - const auto names = unittest_util::nameSequence("test", 5); + const auto names = unittest_util::nameSequence("test", 4); const std::map> expected = { { "bool", [&](){ - mHarness.addAttribute("testshort", 1, 1); - mHarness.addAttribute("testint", 1, 1); - mHarness.addAttribute("testlong", 0, 0); + mHarness.addAttribute("testint32", 1, 1); + mHarness.addAttribute("testint64", 0, 0); mHarness.addAttribute("testfloat", 2.3f, 2.3f); mHarness.addAttribute("testdouble", 0.1, 0.1); - mHarness.addAttributes(names, {true, true, false, true, true}); + mHarness.addAttributes(names, {true, false, true, true}); } }, - { "short", [&](){ + { "int32", [&](){ mHarness.addAttribute("testbool", true, true); - mHarness.addAttribute("testint", 2, 2); - mHarness.addAttribute("testlong", 2, 2); + mHarness.addAttribute("testint64", 2, 2); mHarness.addAttribute("testfloat", 2.3f, 2.3f); mHarness.addAttribute("testdouble", 2.1, 2.1); - mHarness.addAttributes(names, {1, 2, 2, 2, 2}); + mHarness.addAttributes(names, {1, 2, 2, 2}); } }, - { "int", [&](){ + { "int64", [&]() { mHarness.addAttribute("testbool", true, true); - mHarness.addAttribute("testshort", 2, 2); - mHarness.addAttribute("testlong", 2, 2); + mHarness.addAttribute("testint32", 2, 2); mHarness.addAttribute("testfloat", 2.3f, 2.3f); mHarness.addAttribute("testdouble", 2.1, 2.1); - mHarness.addAttributes(names, {1, 2, 2, 2, 2}); - } - }, - { "long", [&]() { - mHarness.addAttribute("testbool", true, true); - mHarness.addAttribute("testshort", 2, 2); - mHarness.addAttribute("testint", 2, 2); - mHarness.addAttribute("testfloat", 2.3f, 2.3f); - mHarness.addAttribute("testdouble", 2.1, 2.1); - mHarness.addAttributes(names, {1, 2, 2, 2, 2}); + mHarness.addAttributes(names, {1, 2, 2, 2}); } }, { "float", [&]() { mHarness.addAttribute("testbool", true, true); - mHarness.addAttribute("testshort", 1, 1); - mHarness.addAttribute("testint", 1, 1); - mHarness.addAttribute("testlong", 1, 1); + mHarness.addAttribute("testint32", 1, 1); + mHarness.addAttribute("testint64", 1, 1); mHarness.addAttribute("testdouble", 1.1, 1.1); - mHarness.addAttributes(names, {1.0f, 1.0f, 1.0f, 1.0f, float(1.1)}); + mHarness.addAttributes(names, {1.0f, 1.0f, 1.0f, float(1.1)}); } }, { "double", [&]() { mHarness.addAttribute("testbool", true, true); - mHarness.addAttribute("testshort", 1, 1); - mHarness.addAttribute("testint", 1, 1); - mHarness.addAttribute("testlong", 1, 1); + mHarness.addAttribute("testint32", 1, 1); + mHarness.addAttribute("testint64", 1, 1); mHarness.addAttribute("testfloat", 1.1f, 1.1f); - mHarness.addAttributes(names, {1.0, 1.0, 1.0, 1.0, double(1.1f)}); + mHarness.addAttributes(names, {1.0, 1.0, 1.0, double(1.1f)}); } } }; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.bool.ax b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.bool.ax index d69e9e10c5..7b0153252d 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.bool.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.bool.ax @@ -1,6 +1,5 @@ -bool@test1 = bool(short@testshort); -bool@test2 = bool(int@testint); -bool@test3 = bool(long@testlong); -bool@test4 = bool(float@testfloat); -bool@test5 = bool(double@testdouble); +bool@test1 = bool(int32@testint32); +bool@test2 = bool(int64@testint64); +bool@test3 = bool(float@testfloat); +bool@test4 = bool(double@testdouble); diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.double.ax b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.double.ax index 81356027df..90033c279d 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.double.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.double.ax @@ -1,6 +1,5 @@ double@test1 = double(bool@testbool); -double@test2 = double(short@testshort); -double@test3 = double(int@testint); -double@test4 = double(long@testlong); -double@test5 = double(float@testfloat); +double@test2 = double(int32@testint32); +double@test3 = double(int64@testint64); +double@test4 = double(float@testfloat); diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.float.ax b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.float.ax index cc5fb892cd..d8df1bb353 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.float.ax +++ b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.float.ax @@ -1,6 +1,5 @@ float@test1 = float(bool@testbool); -float@test2 = float(short@testshort); -float@test3 = float(int@testint); -float@test4 = float(long@testlong); -float@test5 = float(double@testdouble); +float@test2 = float(int32@testint32); +float@test3 = float(int64@testint64); +float@test4 = float(double@testdouble); diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int.ax b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int.ax deleted file mode 100644 index efc537e48d..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int.ax +++ /dev/null @@ -1,6 +0,0 @@ -int@test1 = int(bool@testbool); -int@test2 = int(short@testshort); -int@test3 = int(long@testlong); -int@test4 = int(float@testfloat); -int@test5 = int(double@testdouble); - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int32.ax b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int32.ax new file mode 100644 index 0000000000..b9a1b65a5d --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int32.ax @@ -0,0 +1,5 @@ +int32@test1 = int32(bool@testbool); +int32@test2 = int32(int64@testint64); +int32@test3 = int32(float@testfloat); +int32@test4 = int32(double@testdouble); + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int64.ax b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int64.ax new file mode 100644 index 0000000000..cf9ced8409 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.int64.ax @@ -0,0 +1,5 @@ +int64@test1 = int64(bool@testbool); +int64@test2 = int64(int32@testint32); +int64@test3 = int64(float@testfloat); +int64@test4 = int64(double@testdouble); + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.long.ax b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.long.ax deleted file mode 100644 index 9551e33ef6..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.long.ax +++ /dev/null @@ -1,6 +0,0 @@ -long@test1 = long(bool@testbool); -long@test2 = long(short@testshort); -long@test3 = long(int@testint); -long@test4 = long(float@testfloat); -long@test5 = long(double@testdouble); - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.short.ax b/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.short.ax deleted file mode 100644 index 6833c0eb77..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCast/cast_explicit.short.ax +++ /dev/null @@ -1,6 +0,0 @@ -short@test1 = short(bool@testbool); -short@test2 = short(int@testint); -short@test3 = short(long@testlong); -short@test4 = short(float@testfloat); -short@test5 = short(double@testdouble); - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestConditional.cc b/openvdb_ax/openvdb_ax/test/integration/TestConditional.cc index cc96a03847..c3f261ec7e 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestConditional.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestConditional.cc @@ -15,12 +15,14 @@ class TestConditional : public unittest_util::AXTestCase CPPUNIT_TEST(testConditionalScopingStatement); CPPUNIT_TEST(testConditionalSimpleStatement); CPPUNIT_TEST(testConditionalSimpleElseIf); + CPPUNIT_TEST(testConditionalErrors); CPPUNIT_TEST_SUITE_END(); void testConditionalIfWithinElse(); void testConditionalSimpleStatement(); void testConditionalScopingStatement(); void testConditionalSimpleElseIf(); + void testConditionalErrors(); }; CPPUNIT_TEST_SUITE_REGISTRATION(TestConditional); @@ -48,7 +50,7 @@ TestConditional::testConditionalSimpleStatement() void TestConditional::testConditionalScopingStatement() { - mHarness.addAttribute("int_test", 1); + mHarness.addAttribute("int_test", 1); mHarness.executeCode("test/snippets/conditional/conditionalScopingStatement"); AXTESTS_STANDARD_ASSERT(); @@ -65,3 +67,11 @@ TestConditional::testConditionalSimpleElseIf() AXTESTS_STANDARD_ASSERT(); } +void +TestConditional::testConditionalErrors() +{ + const bool success = mHarness.executeCode("test/snippets/conditional/conditionalErrors"); + CPPUNIT_ASSERT(!success); +} + + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement.cc b/openvdb_ax/openvdb_ax/test/integration/TestCrement.cc index 705246976d..0dcff34bcf 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestCrement.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestCrement.cc @@ -52,12 +52,12 @@ _T1_@test8 = (++_T1_@test6, ++_T1_@test7, _T1_@test6++); }; generate(std::vector{ - "short", "int", "long", "float", "double", + "int16", "int32", "int64", "float", "double", }); const auto names = unittest_util::nameSequence("test", 9); const std::map> expected = { - { "short", + { "int16", [&](bool inc){ if (inc) mHarness.addAttributes(names, @@ -69,7 +69,7 @@ _T1_@test8 = (++_T1_@test6, ++_T1_@test7, _T1_@test6++); { 0, 0, 2, 1, 2, 0, -5, 1, -5 }); }, }, - { "int", + { "int32", [&](bool inc){ if (inc) mHarness.addAttributes(names, @@ -81,7 +81,7 @@ _T1_@test8 = (++_T1_@test6, ++_T1_@test7, _T1_@test6++); { 0, 0, 2, 1, 2, 0, -5, 1, -5 }); }, }, - { "long", + { "int64", [&](bool inc){ if (inc) mHarness.addAttributes(names, diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int.ax deleted file mode 100644 index a1f6ddf209..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int.ax +++ /dev/null @@ -1,7 +0,0 @@ - -int@test1 = --int@test2; -int@test3 = int@test4--; -int@test5 = (int@test6--, int@test7--, --int@test6); -int@test8 = (--int@test6, --int@test7, int@test6--); ---int@test9 = int@test9; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int16.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int16.ax new file mode 100644 index 0000000000..939b5c4584 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int16.ax @@ -0,0 +1,7 @@ + +int16@test1 = --int16@test2; +int16@test3 = int16@test4--; +int16@test5 = (int16@test6--, int16@test7--, --int16@test6); +int16@test8 = (--int16@test6, --int16@test7, int16@test6--); +--int16@test9 = int16@test9; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int32.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int32.ax new file mode 100644 index 0000000000..fbe7228a1b --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int32.ax @@ -0,0 +1,7 @@ + +int32@test1 = --int32@test2; +int32@test3 = int32@test4--; +int32@test5 = (int32@test6--, int32@test7--, --int32@test6); +int32@test8 = (--int32@test6, --int32@test7, int32@test6--); +--int32@test9 = int32@test9; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int64.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int64.ax new file mode 100644 index 0000000000..420abff774 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.int64.ax @@ -0,0 +1,7 @@ + +int64@test1 = --int64@test2; +int64@test3 = int64@test4--; +int64@test5 = (int64@test6--, int64@test7--, --int64@test6); +int64@test8 = (--int64@test6, --int64@test7, int64@test6--); +--int64@test9 = int64@test9; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.long.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.long.ax deleted file mode 100644 index aa8491bed9..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.long.ax +++ /dev/null @@ -1,7 +0,0 @@ - -long@test1 = --long@test2; -long@test3 = long@test4--; -long@test5 = (long@test6--, long@test7--, --long@test6); -long@test8 = (--long@test6, --long@test7, long@test6--); ---long@test9 = long@test9; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.short.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.short.ax deleted file mode 100644 index 3b4a28070f..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_dec.short.ax +++ /dev/null @@ -1,7 +0,0 @@ - -short@test1 = --short@test2; -short@test3 = short@test4--; -short@test5 = (short@test6--, short@test7--, --short@test6); -short@test8 = (--short@test6, --short@test7, short@test6--); ---short@test9 = short@test9; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int.ax deleted file mode 100644 index 674e34ee5e..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int.ax +++ /dev/null @@ -1,7 +0,0 @@ - -int@test1 = ++int@test2; -int@test3 = int@test4++; -int@test5 = (int@test6++, int@test7++, ++int@test6); -int@test8 = (++int@test6, ++int@test7, int@test6++); -++int@test9 = int@test9; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int16.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int16.ax new file mode 100644 index 0000000000..94c48ff09f --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int16.ax @@ -0,0 +1,7 @@ + +int16@test1 = ++int16@test2; +int16@test3 = int16@test4++; +int16@test5 = (int16@test6++, int16@test7++, ++int16@test6); +int16@test8 = (++int16@test6, ++int16@test7, int16@test6++); +++int16@test9 = int16@test9; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int32.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int32.ax new file mode 100644 index 0000000000..cd60508366 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int32.ax @@ -0,0 +1,7 @@ + +int32@test1 = ++int32@test2; +int32@test3 = int32@test4++; +int32@test5 = (int32@test6++, int32@test7++, ++int32@test6); +int32@test8 = (++int32@test6, ++int32@test7, int32@test6++); +++int32@test9 = int32@test9; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int64.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int64.ax new file mode 100644 index 0000000000..0a6e19ddf7 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.int64.ax @@ -0,0 +1,7 @@ + +int64@test1 = ++int64@test2; +int64@test3 = int64@test4++; +int64@test5 = (int64@test6++, int64@test7++, ++int64@test6); +int64@test8 = (++int64@test6, ++int64@test7, int64@test6++); +++int64@test9 = int64@test9; + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.long.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.long.ax deleted file mode 100644 index 83255f9584..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.long.ax +++ /dev/null @@ -1,7 +0,0 @@ - -long@test1 = ++long@test2; -long@test3 = long@test4++; -long@test5 = (long@test6++, long@test7++, ++long@test6); -long@test8 = (++long@test6, ++long@test7, long@test6++); -++long@test9 = long@test9; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.short.ax b/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.short.ax deleted file mode 100644 index 4039612aec..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestCrement/crement_inc.short.ax +++ /dev/null @@ -1,7 +0,0 @@ - -short@test1 = ++short@test2; -short@test3 = short@test4++; -short@test5 = (short@test6++, short@test7++, ++short@test6); -short@test8 = (++short@test6, ++short@test7, short@test6++); -++short@test9 = short@test9; - diff --git a/openvdb_ax/openvdb_ax/test/integration/TestDeclare.cc b/openvdb_ax/openvdb_ax/test/integration/TestDeclare.cc index 098cad22c6..2cb605fa06 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestDeclare.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestDeclare.cc @@ -67,7 +67,7 @@ TestDeclare::testAttributes() { mHarness.addAttributes(unittest_util::nameSequence("float_test", 4), {0.0f, 0.2f, 10.0f, 10.0f}); - mHarness.addAttributes(unittest_util::nameSequence("int_test", 3), + mHarness.addAttributes(unittest_util::nameSequence("int_test", 3), {0, 5, 10}); mHarness.addAttribute("short_test", int16_t(1)); @@ -84,7 +84,7 @@ TestDeclare::testAttributesVolume() { mHarness.addAttributes(unittest_util::nameSequence("float_test", 4), {0.0f, 0.2f, 10.0f, 10.0f}); - mHarness.addAttributes(unittest_util::nameSequence("int_test", 3), + mHarness.addAttributes(unittest_util::nameSequence("int_test", 3), {0, 5, 10}); mHarness.addAttribute("long_test", int64_t(3)); @@ -100,7 +100,7 @@ TestDeclare::testNewAttributes() { mHarness.addExpectedAttributes(unittest_util::nameSequence("float_test", 4), {0.0f, 0.2f, 10.0f, 10.0f}); - mHarness.addExpectedAttributes(unittest_util::nameSequence("int_test", 3), + mHarness.addExpectedAttributes(unittest_util::nameSequence("int_test", 3), {0, 5, 10}); mHarness.addExpectedAttribute("short_test", int16_t(1)); @@ -110,7 +110,7 @@ TestDeclare::testNewAttributes() // Volume data needs to exist to be tested mHarness.addInputVolumes(unittest_util::nameSequence("float_test", 4), {0.0f, 0.2f, 10.0f, 10.0f}); - mHarness.addInputVolumes(unittest_util::nameSequence("int_test", 3), + mHarness.addInputVolumes(unittest_util::nameSequence("int_test", 3), {0, 5, 10}); mHarness.addInputVolumes({"short_test"}, {int16_t(1)}); mHarness.addInputVolumes({"long_test"}, {int64_t(3)}); diff --git a/openvdb_ax/openvdb_ax/test/integration/TestExternals.cc b/openvdb_ax/openvdb_ax/test/integration/TestExternals.cc index b19360227b..0c77fb3617 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestExternals.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestExternals.cc @@ -43,7 +43,7 @@ _T1_@test1 = _T1_$ext1;)"; }; generate(std::vector{ - "bool", "short", "int", "long", "float", "double", + "bool", "int32", "int64", "float", "double", "vec2i", "vec2f", "vec2d", "vec3i", "vec3f", "vec3d", "vec4i", "vec4f", "vec4d", @@ -60,21 +60,14 @@ _T1_@test1 = _T1_$ext1;)"; mHarness.mCustomData->insertData("ext1", openvdb::TypedMetadata(true).copy()); }, }, - { "short", - [&](){ - mHarness.addAttribute("test1", -1); - mHarness.mCustomData.reset(new openvdb::ax::CustomData()); - mHarness.mCustomData->insertData("ext1", openvdb::TypedMetadata(-1).copy()); - }, - }, - { "int", + { "int32", [&](){ mHarness.addAttribute("test1", -2); mHarness.mCustomData.reset(new openvdb::ax::CustomData()); mHarness.mCustomData->insertData("ext1", openvdb::TypedMetadata(-2).copy()); }, }, - { "long", + { "int64", [&](){ mHarness.addAttribute("test1", 3); mHarness.mCustomData.reset(new openvdb::ax::CustomData()); diff --git a/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int.ax b/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int.ax deleted file mode 100644 index aa33d8fefa..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int.ax +++ /dev/null @@ -1,2 +0,0 @@ - -int@test1 = int$ext1; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int32.ax b/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int32.ax new file mode 100644 index 0000000000..5e970c2b73 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int32.ax @@ -0,0 +1,2 @@ + +int32@test1 = int32$ext1; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int64.ax b/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int64.ax new file mode 100644 index 0000000000..456b0f1453 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.int64.ax @@ -0,0 +1,2 @@ + +int64@test1 = int64$ext1; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.long.ax b/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.long.ax deleted file mode 100644 index 7986e4fff5..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.long.ax +++ /dev/null @@ -1,2 +0,0 @@ - -long@test1 = long$ext1; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.short.ax b/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.short.ax deleted file mode 100644 index f634ae5586..0000000000 --- a/openvdb_ax/openvdb_ax/test/integration/TestExternals/external_assign_from.short.ax +++ /dev/null @@ -1,2 +0,0 @@ - -short@test1 = short$ext1; diff --git a/openvdb_ax/openvdb_ax/test/integration/TestLoop.cc b/openvdb_ax/openvdb_ax/test/integration/TestLoop.cc index 46818a2412..6b53b50f71 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestLoop.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestLoop.cc @@ -15,12 +15,14 @@ class TestLoop : public unittest_util::AXTestCase CPPUNIT_TEST(testLoopWhileLoop); CPPUNIT_TEST(testLoopDoWhileLoop); CPPUNIT_TEST(testLoopOverflow); + CPPUNIT_TEST(testLoopErrors); CPPUNIT_TEST_SUITE_END(); void testLoopForLoop(); void testLoopWhileLoop(); void testLoopDoWhileLoop(); void testLoopOverflow(); + void testLoopErrors(); }; CPPUNIT_TEST_SUITE_REGISTRATION(TestLoop); @@ -34,11 +36,11 @@ TestLoop::testLoopForLoop() mHarness.addAttribute("loop_test15", openvdb::Vec3f(0.0,0.0,0.0)); mHarness.addAttribute("loop_test18", openvdb::math::Mat3s(1.0,2.0,3.0, 4.0,5.0,6.0, 7.0,8.0,9.0)); - mHarness.addAttribute("loop_test22", 3); - mHarness.addAttribute("loop_test23", 4); - mHarness.addAttribute("loop_test25", 1); - mHarness.addAttribute("loop_test27", 14); - mHarness.addAttribute("loop_test30", 19); + mHarness.addAttribute("loop_test22", 3); + mHarness.addAttribute("loop_test23", 4); + mHarness.addAttribute("loop_test25", 1); + mHarness.addAttribute("loop_test27", 14); + mHarness.addAttribute("loop_test30", 19); mHarness.executeCode("test/snippets/loop/forLoop"); AXTESTS_STANDARD_ASSERT(); @@ -50,7 +52,7 @@ TestLoop::testLoopWhileLoop() mHarness.addAttribute("loop_test9", openvdb::Vec3f(1.0,2.0,3.0)); mHarness.addAttribute("loop_test16", openvdb::Vec3f(0.0,0.0,0.0)); mHarness.addAttribute("loop_test28", openvdb::Vec3f(0.0,0.0,0.0)); - mHarness.addAttribute("loop_test31", 2); + mHarness.addAttribute("loop_test31", 2); mHarness.executeCode("test/snippets/loop/whileLoop"); AXTESTS_STANDARD_ASSERT(); @@ -62,7 +64,7 @@ TestLoop::testLoopDoWhileLoop() mHarness.addAttribute("loop_test12", openvdb::Vec3f(1.0,2.0,3.0)); mHarness.addAttribute("loop_test17", openvdb::Vec3f(1.0,0.0,0.0)); mHarness.addAttribute("loop_test29", openvdb::Vec3f(1.0,0.0,0.0)); - mHarness.addAttribute("loop_test32", 2); + mHarness.addAttribute("loop_test32", 2); mHarness.executeCode("test/snippets/loop/doWhileLoop"); AXTESTS_STANDARD_ASSERT(); @@ -80,3 +82,10 @@ TestLoop::testLoopOverflow() mHarness.executeCode("test/snippets/loop/loopOverflow"); } +void +TestLoop::testLoopErrors() +{ + const bool success = mHarness.executeCode("test/snippets/loop/loopErrors"); + CPPUNIT_ASSERT(!success); +} + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestStandardFunctions.cc b/openvdb_ax/openvdb_ax/test/integration/TestStandardFunctions.cc index 8b62cda230..972f0cc396 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestStandardFunctions.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestStandardFunctions.cc @@ -15,13 +15,11 @@ #include -#include -#include -#include - #include #include #include +#include +#include using namespace openvdb::points; using namespace openvdb::ax; @@ -71,6 +69,7 @@ class TestStandardFunctions : public unittest_util::AXTestCase CPPUNIT_TEST(pretransform); CPPUNIT_TEST(print); CPPUNIT_TEST(rand); + CPPUNIT_TEST(rand32); CPPUNIT_TEST(sign); CPPUNIT_TEST(signbit); CPPUNIT_TEST(simplexnoise); @@ -116,6 +115,7 @@ class TestStandardFunctions : public unittest_util::AXTestCase void pretransform(); void print(); void rand(); + void rand32(); void sign(); void signbit(); void simplexnoise(); @@ -580,8 +580,9 @@ TestStandardFunctions::lengthsq() void TestStandardFunctions::lerp() { - mHarness.addAttributes(unittest_util::nameSequence("test", 3), {6.0, 21.0, -19.0}); - mHarness.addAttribute("test4", 6.0f); + mHarness.addAttributes(unittest_util::nameSequence("test", 9), + {-1.1, 1.0000001, 1.0000001, -1.0000001, 1.1, -1.1, 6.0, 21.0, -19.0}); + mHarness.addAttribute("test10", 6.0f); testFunctionOptions(mHarness, "lerp"); } @@ -814,28 +815,55 @@ TestStandardFunctions::print() void TestStandardFunctions::rand() { - auto hashToSeed = [](size_t hash) -> uint32_t { + std::mt19937_64 engine; + std::uniform_real_distribution uniform(0.0,1.0); + + size_t hash = std::hash()(2.0); + engine.seed(hash); + + const double expected1 = uniform(engine); + + hash = std::hash()(3.0); + engine.seed(hash); + + const double expected2 = uniform(engine); + const double expected3 = uniform(engine); + + mHarness.addAttributes({"test0", "test1", "test2", "test3"}, + {expected1, expected1, expected2, expected3}); + testFunctionOptions(mHarness, "rand"); +} + +void +TestStandardFunctions::rand32() +{ + auto hashToSeed = [](size_t hash) -> + std::mt19937::result_type + { unsigned int seed = 0; do { seed ^= (uint32_t) hash; } while (hash >>= sizeof(uint32_t) * 8); - return seed; + return std::mt19937::result_type(seed); }; - boost::uniform_01 uniform_01; - size_t hash = boost::hash()(2.0); - boost::mt19937 engine(static_cast(hashToSeed(hash))); + std::mt19937 engine; + std::uniform_real_distribution uniform(0.0,1.0); + + size_t hash = std::hash()(2.0); + engine.seed(hashToSeed(hash)); - const double expected1 = uniform_01(engine); + const double expected1 = uniform(engine); - hash = boost::hash()(3.0); - engine.seed(static_cast(hashToSeed(hash))); - const double expected2 = uniform_01(engine); - const double expected3 = uniform_01(engine); + hash = std::hash()(3.0); + engine.seed(hashToSeed(hash)); + + const double expected2 = uniform(engine); + const double expected3 = uniform(engine); mHarness.addAttributes({"test0", "test1", "test2", "test3"}, {expected1, expected1, expected2, expected3}); - testFunctionOptions(mHarness, "rand"); + testFunctionOptions(mHarness, "rand32"); } void diff --git a/openvdb_ax/openvdb_ax/test/integration/TestTernary.cc b/openvdb_ax/openvdb_ax/test/integration/TestTernary.cc index 1d98e2b61a..334f755f91 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestTernary.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestTernary.cc @@ -13,10 +13,12 @@ class TestTernary : public unittest_util::AXTestCase CPPUNIT_TEST_SUITE(TestTernary); CPPUNIT_TEST(testTernary); CPPUNIT_TEST(testTernaryVoid); + CPPUNIT_TEST(testTernaryErrors); CPPUNIT_TEST_SUITE_END(); void testTernary(); void testTernaryVoid(); + void testTernaryErrors(); }; CPPUNIT_TEST_SUITE_REGISTRATION(TestTernary); @@ -87,3 +89,10 @@ TestTernary::testTernaryVoid() AXTESTS_STANDARD_ASSERT(); } +void +TestTernary::testTernaryErrors() +{ + const bool success = mHarness.executeCode("test/snippets/ternary/ternaryErrors"); + CPPUNIT_ASSERT(!success); +} + diff --git a/openvdb_ax/openvdb_ax/test/integration/TestVDBFunctions.cc b/openvdb_ax/openvdb_ax/test/integration/TestVDBFunctions.cc index 49f947693d..b719d985a4 100644 --- a/openvdb_ax/openvdb_ax/test/integration/TestVDBFunctions.cc +++ b/openvdb_ax/openvdb_ax/test/integration/TestVDBFunctions.cc @@ -407,7 +407,7 @@ TestVDBFunctions::testValidContext() // Don't check internal functions if (func.second.isInternal()) continue; - const openvdb::ax::codegen::FunctionGroup::Ptr ptr = func.second.function(); + const openvdb::ax::codegen::FunctionGroup* const ptr = func.second.function(); CPPUNIT_ASSERT(ptr); const auto& signatures = ptr->list(); CPPUNIT_ASSERT(!signatures.empty()); @@ -430,7 +430,7 @@ TestVDBFunctions::testValidContext() // Don't check internal functions if (func.second.isInternal()) continue; - const openvdb::ax::codegen::FunctionGroup::Ptr ptr = func.second.function(); + const openvdb::ax::codegen::FunctionGroup* const ptr = func.second.function(); CPPUNIT_ASSERT(ptr); const auto& signatures = ptr->list(); CPPUNIT_ASSERT(!signatures.empty()); diff --git a/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalErrors b/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalErrors new file mode 100644 index 0000000000..129234d522 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalErrors @@ -0,0 +1,13 @@ +if ({1,1,1}) { + true; +} + +if (false) { + true; +} else if ("foo") { + true; +} + +if ({1,1,1} < 1) { + true; +} \ No newline at end of file diff --git a/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalScopingStatement b/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalScopingStatement index 9f74a64e96..7a352e47b7 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalScopingStatement +++ b/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalScopingStatement @@ -1,4 +1,4 @@ -int@int_test = 0; +int32@int_test = 0; if(true) { if (true) { diff --git a/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalSimpleElseIf b/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalSimpleElseIf index e99ecb7e6c..c7ca08e69b 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalSimpleElseIf +++ b/openvdb_ax/openvdb_ax/test/snippets/conditional/conditionalSimpleElseIf @@ -1,9 +1,9 @@ -int@int_test = 2; +int32@int_test = 2; bool@bool_test = false; -if(int@int_test == 0) ; -else if (int@int_test == 1) ; -else if (int@int_test == 2) bool@bool_test = true; +if(int32@int_test == 0) ; +else if (int32@int_test == 1) ; +else if (int32@int_test == 2) bool@bool_test = true; else -int@int_test = 0; +int32@int_test = 0; diff --git a/openvdb_ax/openvdb_ax/test/snippets/declare/declareAttributes b/openvdb_ax/openvdb_ax/test/snippets/declare/declareAttributes index cc257edf00..fb7bce3abe 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/declare/declareAttributes +++ b/openvdb_ax/openvdb_ax/test/snippets/declare/declareAttributes @@ -1,9 +1,9 @@ @float_test1; -int@int_test1; +int32@int_test1; -short@short_test = 1s; -int@int_test2 = 5; -long@long_test = 3l; +int16@short_test = 1s; +int32@int_test2 = 5; +int64@long_test = 3l; float@float_test2 = 0.2f; double@double_test = 0.3; diff --git a/openvdb_ax/openvdb_ax/test/snippets/declare/declareAttributesVolume b/openvdb_ax/openvdb_ax/test/snippets/declare/declareAttributesVolume index 1953231694..359a535e59 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/declare/declareAttributesVolume +++ b/openvdb_ax/openvdb_ax/test/snippets/declare/declareAttributesVolume @@ -2,10 +2,10 @@ // are not supported at the moment. Can be deprecated if this ever changes @float_test1; -int@int_test1; +int32@int_test1; -int@int_test2 = 5; -long@long_test = 3l; +int32@int_test2 = 5; +int64@long_test = 3l; float@float_test2 = 0.2f; double@double_test = 0.3; diff --git a/openvdb_ax/openvdb_ax/test/snippets/declare/declareLocalVariables b/openvdb_ax/openvdb_ax/test/snippets/declare/declareLocalVariables index 9e735740bc..dc1fe8a6a9 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/declare/declareLocalVariables +++ b/openvdb_ax/openvdb_ax/test/snippets/declare/declareLocalVariables @@ -1,9 +1,9 @@ float a_float; -int a_int; +int32 a_int; -short a_short = 1s; -int b_int = 5; -long a_long = 3l; +int16 a_short = 1s; +int32 b_int = 5; +int64 a_long = 3l; float b_float = 0.2f; double a_double = 0.3; diff --git a/openvdb_ax/openvdb_ax/test/snippets/function/abs b/openvdb_ax/openvdb_ax/test/snippets/function/abs index d000c39c92..75430b70ff 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/function/abs +++ b/openvdb_ax/openvdb_ax/test/snippets/function/abs @@ -1,7 +1,7 @@ -int@test1 = abs(-3); -int@test2 = abs(3); -int@test3 = abs(0); -long@test4 = abs(-2147483649l); +int32@test1 = abs(-3); +int32@test2 = abs(3); +int32@test3 = abs(0); +int64@test4 = abs(-2147483649l); float@test5 = abs(0.3f); float@test6 = abs(-0.3f); double@test7 = abs(1.79769e+308); diff --git a/openvdb_ax/openvdb_ax/test/snippets/function/hash b/openvdb_ax/openvdb_ax/test/snippets/function/hash index 8fb73a6e95..b32f4c3b31 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/function/hash +++ b/openvdb_ax/openvdb_ax/test/snippets/function/hash @@ -1,5 +1,5 @@ -long@test1 = hash(""); -long@test2 = hash("0"); -long@test3 = hash("abc"); -long@test4 = hash("123"); +int64@test1 = hash(""); +int64@test2 = hash("0"); +int64@test3 = hash("abc"); +int64@test4 = hash("123"); diff --git a/openvdb_ax/openvdb_ax/test/snippets/function/lerp b/openvdb_ax/openvdb_ax/test/snippets/function/lerp index 9cf3e2a0f2..413a57ca12 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/function/lerp +++ b/openvdb_ax/openvdb_ax/test/snippets/function/lerp @@ -1,4 +1,13 @@ -double@test1 = lerp(1.0, 11.0, 0.5); -double@test2 = lerp(1.0, 11.0, 2.0); -double@test3 = lerp(1.0, 11.0,-2.0); -float@test4 = lerp(1.0f, 11.0f, 0.5f); +double@test1 = lerp(-1.1, 1.0000001, 0.0); +// test formulation returns b when t = 1.0 +double@test2 = lerp(-1.1, 1.0000001, 1.0); +double@test3 = lerp(1.1, 1.0000001, 1.0); +double@test4 = lerp(-1.1, -1.0000001, 1.0); +// test formulation returns exact input when a==b +double@test5 = lerp(1.1, 1.1, 100.0); +double@test6 = lerp(-1.1, -1.1, 100.0); +// +double@test7 = lerp(1.0, 11.0, 0.5); +double@test8 = lerp(1.0, 11.0, 2.0); +double@test9 = lerp(1.0, 11.0,-2.0); +float@test10 = lerp(1.0f, 11.0f, 0.5f); diff --git a/openvdb_ax/openvdb_ax/test/snippets/function/rand32 b/openvdb_ax/openvdb_ax/test/snippets/function/rand32 new file mode 100644 index 0000000000..e6b3ce14f9 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/snippets/function/rand32 @@ -0,0 +1,6 @@ + +double seed = 2.0; +double@test0 = rand32(seed); +double@test1 = rand32(seed); // should be the same as test0 +double@test2 = rand32(seed + 1.0); // should be different to test0 and test1 +double@test3 = rand32(); // should advance the generator from seed+1 diff --git a/openvdb_ax/openvdb_ax/test/snippets/loop/doWhileLoop b/openvdb_ax/openvdb_ax/test/snippets/loop/doWhileLoop index 187b775cc2..6798dadf6d 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/loop/doWhileLoop +++ b/openvdb_ax/openvdb_ax/test/snippets/loop/doWhileLoop @@ -1,7 +1,7 @@ // do loop { vec3f v = 0.0f; - int i = 0; + int32 i = 0; do { v[i] = i+1; i++; @@ -14,7 +14,7 @@ // do loop false condition { vec3f v = 0.0f; - int i = 0; + int32 i = 0; do { v[i] = i + 1; } @@ -26,7 +26,7 @@ // do loop multi expression false condition { vec3f v = 0.0f; - int i = 0; + int32 i = 0; do { v[i] = i + 1; } @@ -37,13 +37,13 @@ // do while loop declaration condition { - int k = 1; + int32 k = 1; float j = 1.1f; do { k++; j--; } - while(int i = floor(j)) + while(int32 i = floor(j)) - int@loop_test32 = k; + int32@loop_test32 = k; } diff --git a/openvdb_ax/openvdb_ax/test/snippets/loop/forLoop b/openvdb_ax/openvdb_ax/test/snippets/loop/forLoop index 5d0b123231..63694e8b76 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/loop/forLoop +++ b/openvdb_ax/openvdb_ax/test/snippets/loop/forLoop @@ -1,7 +1,7 @@ // for loop { vec3f v = 0.0f; - for(int i = 0; i < 3; ++i) { + for(int32 i = 0; i < 3; ++i) { v[i] = i + 1; } vec3f@loop_test1 = v; @@ -10,7 +10,7 @@ // for loop without initial statement { vec3f v = 0.0f; - int i = 0; + int32 i = 0; for(; i < 3; ++i) { v[i] = i + 1; } @@ -20,8 +20,8 @@ // for loop without iteration statement { vec3f v = 0.0f; - int i = 1; - for(int i = 0; i < 3;) { + int32 i = 1; + for(int32 i = 0; i < 3;) { v[i] = i + 1; i++; } @@ -31,7 +31,7 @@ // for loop false condition { vec3f v = 0.0f; - for(int i = 0; i < 0; ++i) { + for(int32 i = 0; i < 0; ++i) { v[i] = i + 1; } vec3f@loop_test15 = v; @@ -40,8 +40,8 @@ // nested for loop { mat3f mat = 0.0f; - for(int i = 0; i < 3; ++i) { - for (int j = 0; j < 3; ++j) { + for(int32 i = 0; i < 3; ++i) { + for (int32 j = 0; j < 3; ++j) { mat[i,j] = i * 3 + j + 1; } } @@ -50,42 +50,42 @@ // for loop multi expressions { - int j = 0; - for(int i = 0; i < 3; ++i, ++j) {} - int@loop_test22 = j; + int32 j = 0; + for(int32 i = 0; i < 3; ++i, ++j) {} + int32@loop_test22 = j; } // for loop declaration condition { - int j = 0; - for (int i = 5; int k = floor(i - 1); --i) j++; + int32 j = 0; + for (int32 i = 5; int32 k = floor(i - 1); --i) j++; - int@loop_test23 = j; + int32@loop_test23 = j; } // for loop multi expression condition { - int j = 0; - int k = 0; - int l = 1; - for (int i = 5; k != 0, l--; --i) j++; + int32 j = 0; + int32 k = 0; + int32 l = 1; + for (int32 i = 5; k != 0, l--; --i) j++; - int@loop_test25 = j; + int32@loop_test25 = j; } // for loop multi expression initial { - int i = 0; - int j = 0; + int32 i = 0; + int32 j = 0; for (i -= 10, j += 4; i < 10; i += j) { j *= 2; - int@loop_test27 = i + j; + int32@loop_test27 = i + j; }; } // for loop multi declaration initial { - for (int i = 0, j = 10; i < 10; ++i) { - int@loop_test30 = i + j; + for (int32 i = 0, j = 10; i < 10; ++i) { + int32@loop_test30 = i + j; }; } diff --git a/openvdb_ax/openvdb_ax/test/snippets/loop/loopErrors b/openvdb_ax/openvdb_ax/test/snippets/loop/loopErrors new file mode 100644 index 0000000000..3a3d21088d --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/snippets/loop/loopErrors @@ -0,0 +1,16 @@ +for (int i; {1,1,1}; ++i) { + true; +} + +for (int i; {1,1,1} < 1; ++i) { + true; +} + +do { + true; +} while ("foo") + +mat3f mat = identity3(); +while (mat) { + true; +} \ No newline at end of file diff --git a/openvdb_ax/openvdb_ax/test/snippets/loop/loopOverflow b/openvdb_ax/openvdb_ax/test/snippets/loop/loopOverflow index c62f264acd..aca8743cd8 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/loop/loopOverflow +++ b/openvdb_ax/openvdb_ax/test/snippets/loop/loopOverflow @@ -1,6 +1,6 @@ mat4d m1=0; vec3f v1=1; -for (int i = 0; i < 10000000; ++i) { +for (int32 i = 0; i < 10000000; ++i) { // 10000000 x alloca inside of loop mat4d m2 = m1; // sret function, 10000000 x alloca of result diff --git a/openvdb_ax/openvdb_ax/test/snippets/loop/whileLoop b/openvdb_ax/openvdb_ax/test/snippets/loop/whileLoop index c3ddc569bb..591db4ea08 100644 --- a/openvdb_ax/openvdb_ax/test/snippets/loop/whileLoop +++ b/openvdb_ax/openvdb_ax/test/snippets/loop/whileLoop @@ -1,7 +1,7 @@ // while loop { vec3f v = 0.0f; - int i = 0; + int32 i = 0; while(i < 3) { v[i] = i+1; i++; @@ -12,7 +12,7 @@ // while loop false condition { vec3f v = 0.0f; - int i = 0; + int32 i = 0; while(false) { v[i] = i + 1; } @@ -22,7 +22,7 @@ // while loop multi expression false condition { vec3f v = 0.0f; - int i = 0; + int32 i = 0; while(true, true, true, false) { v[i] = i + 1; } @@ -31,13 +31,13 @@ // while loop declaration condition { - int k = 1; + int32 k = 1; float j = 1.1f; - while(int i = floor(j)) { + while(int32 i = floor(j)) { k++; j--; } - int@loop_test31 = k; + int32@loop_test31 = k; } diff --git a/openvdb_ax/openvdb_ax/test/snippets/ternary/ternaryErrors b/openvdb_ax/openvdb_ax/test/snippets/ternary/ternaryErrors new file mode 100644 index 0000000000..0732f37f01 --- /dev/null +++ b/openvdb_ax/openvdb_ax/test/snippets/ternary/ternaryErrors @@ -0,0 +1,8 @@ +{1,1,1} ? 0 : 1; + +{1,0,0,0,1,0,0,0,1} & 1 ? 0 : 1; + +"foo" ? : "bar"; + +"foo" ? : 1; + diff --git a/tsc/meetings/2020-10-20.md b/tsc/meetings/2020-10-20.md new file mode 100644 index 0000000000..bd30d2aeb8 --- /dev/null +++ b/tsc/meetings/2020-10-20.md @@ -0,0 +1,103 @@ +Minutes from 67th OpenVDB TSC meeting, Oct 20th, 2020, (EDT) + +Attendees: *Nick* A., *Jeff* L., *Ken* M., *Dan* B. + +Additional Attendees: Johannes Meng (Intel), JT Nelson (Blender), +Andre Pradhana (DW), Bruce Cherniak (Intel) + +Regrets: *Peter* C. + +Agenda: + +1) Confirm quorum +2) Secretary +3) ASWF Questions +4) Screen-sharing +5) Faster CSG Operations (PR785) +6) Review Process Retrospective +7) Next Meeting + +1) Quorum was confirmed. + +2) Secretary was Dan Bailey + +3) ASWF Questions + +Ken is providing some project-specific questions to the ASWF tomorrow. Various +questions posed such as locking-down the tree configuration, usage of the Maya +plugin, etc. Any others people think of, please share with Ken before tomorrow. + +4) Screen-sharing + +Screen-sharing is still disabled in our ASWF calls. Ken is joining the meeting +using his NVidia Zoom account and believes the host account is the official +openvdb gmail account. Ken to look into how to unlock screen-sharing and/or to +discuss with John about changing which account is the host account. In this +instance, Dan's ILM Zoom account was used to host a new call with screen-sharing +enabled. + +5) Faster CSG Operations (PR785) + +Dan re-presented the theory behind this functionality as presented in the +Siggraph 2019 OpenVDB Course and gave an overview of the code changes in the PR, +answering questions from the TSC as they came up. Changes are organized in the +PR by commit. + +Some areas of discussion and/or investigation include: + +Ken highlighted that different words of a node mask could in theory be modified +concurrently to unlock parallelism across a single node. + +Nick and Jeff wish to try and resolve the confusion of sometimes using a bool +threaded parameter and sometimes using a bool serial parameter across the +codebase in general. Using bool serial is probably the right decision here to +maintain consistency, but would be nice to fix in an independent effort. Jeff +suggested using an enum to maintain backwards compatibility. + +Ken raised that the new DynamicNodeManager class needs more documentation. Dan +to address this. + +Nick raised that there were issues in the past with threading a core method that +was previously unthreaded as a result of nested parallelism. Dan to investigate +whether there are situations in which construction of the LeafManager or +NodeManager happens inside a thread. + +Jeff and Ken raised concerns with the TreeToMerge class accepting either const +or non-const trees and users inadvertently picking the wrong one. The suggestion +proposed here is to add a dummy class parameter to each constructor similar to +tbb::split to make construction more explicit. Potentially this dummy class +could be part of the Types header. Dan to look into making this change. + +This feature has been deployed at ILM along with the VDB Merge SOP that is not +part of this PR. Main item of feedback was that the Tree visitor pattern was +previously being used with a const tree as the NodeManager required a non-const +tree. Dan has extended the NodeManager to accept a const tree following the +implementation of the LeafManager which also does this. This change is now +included as part of this PR. + +Jeff proposes that we aim to approve this PR by the TSC meeting next week. + +6) Review Process Retrospective + +Big changes such as AX, NanoVDB and the PR discussed here are hard to get into +the codebase. Bikeshedding occurs and smaller, simpler changes tend to be +discussed and merged as priority. Need to keep trying to address how to unblock +changes that are hard to review and particularly those that are refactoring +large portions of the existing codebase. + +Public API is most important to review as that can be time-consuming to try and +change later. Bugs in the implementation details will often be discovered in due +course but that should not hold up features progressing. Provided there is +decent unit test coverage of new functionality being added and that all of the +existing unit tests pass, that should help to lower the barrier to approval. + +Ken reiterates that we should perceive the master branch in GitHub as in +development. Users deploying directly from this take on a fair amount of risk. + +In general, all in favor of using this live code review process again in these +types of cases to help push the project forwards. + +7) Next Meeting + +Next meeting is October 27th, 2020. 1pm-2pm EDT (GMT-4). Jeff will be dressed up +for Halloween.