220    using Range = std::pair<const IndexType*, const IndexType*>;
 
  221    using RangeDeque = std::deque<Range>;
 
  222    using RangeDequeCIter = 
typename RangeDeque::const_iterator;
 
  223    using IndexArray = std::unique_ptr<IndexType[]>;
 
  226    std::unique_ptr<ConstAccessorPtr[]> mAccessorList;
 
  230    RangeDeque      mRangeList;
 
  231    RangeDequeCIter mIter;
 
  233    IndexArray      mIndexArray;
 
  234    size_t          mIndexArraySize, mAccessorListSize;
 
  244namespace particle_atlas_internal {
 
  247template<
typename ParticleArrayT>
 
  248struct ComputeExtremas
 
  250    using PosType = 
typename ParticleArrayT::PosType;
 
  251    using ScalarType = 
typename PosType::value_type;
 
  253    ComputeExtremas(
const ParticleArrayT& particles)
 
  254        : particleArray(&particles)
 
  255        , minRadius(
std::numeric_limits<ScalarType>::max())
 
  256        , maxRadius(-
std::numeric_limits<ScalarType>::max())
 
  260    ComputeExtremas(ComputeExtremas& rhs, tbb::split)
 
  261        : particleArray(rhs.particleArray)
 
  262        , minRadius(
std::numeric_limits<ScalarType>::max())
 
  263        , maxRadius(-
std::numeric_limits<ScalarType>::max())
 
  267    void operator()(
const tbb::blocked_range<size_t>& range) {
 
  269        ScalarType radius, tmpMin = minRadius, tmpMax = maxRadius;
 
  271        for (
size_t n = range.begin(), N = range.end(); n != N; ++n) {
 
  272            particleArray->getRadius(n, radius);
 
  273            tmpMin = std::min(radius, tmpMin);
 
  274            tmpMax = std::max(radius, tmpMax);
 
  277        minRadius = std::min(minRadius, tmpMin);
 
  278        maxRadius = std::max(maxRadius, tmpMax);
 
  281    void join(
const ComputeExtremas& rhs) {
 
  282        minRadius = std::min(minRadius, rhs.minRadius);
 
  283        maxRadius = std::max(maxRadius, rhs.maxRadius);
 
  286    ParticleArrayT 
const * 
const particleArray;
 
  287    ScalarType minRadius, maxRadius;
 
  291template<
typename ParticleArrayT, 
typename Po
intIndex>
 
  292struct SplittableParticleArray
 
  294    using Ptr = SharedPtr<SplittableParticleArray>;
 
  295    using ConstPtr = SharedPtr<const SplittableParticleArray>;
 
  296    using ParticleArray = ParticleArrayT;
 
  298    using PosType = 
typename ParticleArray::PosType;
 
  299    using ScalarType = 
typename PosType::value_type;
 
  301    SplittableParticleArray(
const ParticleArrayT& particles)
 
  302        : mIndexMap(), mParticleArray(&particles), mSize(particles.size())
 
  307    SplittableParticleArray(
const ParticleArrayT& particles, 
double minR, 
double maxR)
 
  308        : mIndexMap(), mParticleArray(&particles), mSize(particles.size())
 
  310        mMinRadius = ScalarType(minR);
 
  311        mMaxRadius = ScalarType(maxR);
 
  314    const ParticleArrayT& particleArray()
 const { 
return *mParticleArray; }
 
  316    size_t size()
 const { 
return mSize; }
 
  318    void getPos(
size_t n, PosType& xyz)
 const 
  319        { 
return mParticleArray->getPos(getGlobalIndex(n), xyz); }
 
  320    void getRadius(
size_t n, ScalarType& radius)
 const 
  321        { 
return mParticleArray->getRadius(getGlobalIndex(n), radius); }
 
  323    ScalarType minRadius()
 const { 
return mMinRadius; }
 
  324    ScalarType maxRadius()
 const { 
return mMaxRadius; }
 
  326    size_t getGlobalIndex(
size_t n)
 const { 
return mIndexMap ? size_t(mIndexMap[n]) : n; }
 
  330    Ptr 
split(ScalarType maxRadiusLimit) {
 
  332        if (mMaxRadius < maxRadiusLimit) 
return Ptr();
 
  334        std::unique_ptr<bool[]> mask{
new bool[mSize]};
 
  336        tbb::parallel_for(tbb::blocked_range<size_t>(0, mSize),
 
  337            MaskParticles(*
this, mask, maxRadiusLimit));
 
  339        Ptr output(
new SplittableParticleArray(*
this, mask));
 
  340        if (output->size() == 0) 
return Ptr();
 
  343        for (
size_t n = 0, N = mSize; n < N; ++n) {
 
  344            newSize += size_t(!mask[n]);
 
  347        std::unique_ptr<PointIndex[]> newIndexMap{
new PointIndex[newSize]};
 
  349        setIndexMap(newIndexMap, mask, 
false);
 
  352        mIndexMap.swap(newIndexMap);
 
  361    SplittableParticleArray(
const SplittableParticleArray&);
 
  362    SplittableParticleArray& operator=(
const SplittableParticleArray&);
 
  365    SplittableParticleArray(
const SplittableParticleArray& other,
 
  366        const std::unique_ptr<
bool[]>& mask)
 
  367        : mIndexMap(), mParticleArray(&other.particleArray()), mSize(0)
 
  369        for (
size_t n = 0, N = other.size(); n < N; ++n) {
 
  370            mSize += size_t(mask[n]);
 
  374            mIndexMap.reset(
new PointIndex[mSize]);
 
  375            other.setIndexMap(mIndexMap, mask, 
true);
 
  381    struct MaskParticles {
 
  382        MaskParticles(
const SplittableParticleArray& particles,
 
  383            const std::unique_ptr<
bool[]>& mask, ScalarType radius)
 
  384            : particleArray(&particles)
 
  385            , particleMask(mask.get())
 
  386            , radiusLimit(radius)
 
  390        void operator()(
const tbb::blocked_range<size_t>& range)
 const {
 
  391            const ScalarType maxRadius = radiusLimit;
 
  393            for (
size_t n = range.begin(), N = range.end(); n != N; ++n) {
 
  394                particleArray->getRadius(n, radius);
 
  395                particleMask[n] = !(radius < maxRadius);
 
  399        SplittableParticleArray 
const * 
const particleArray;
 
  400        bool                          * 
const particleMask;
 
  401        ScalarType                      
const radiusLimit;
 
  404    inline void updateExtremas() {
 
  405        ComputeExtremas<SplittableParticleArray> 
op(*
this);
 
  406        tbb::parallel_reduce(tbb::blocked_range<size_t>(0, mSize), op);
 
  407        mMinRadius = 
op.minRadius;
 
  408        mMaxRadius = 
op.maxRadius;
 
  411    void setIndexMap(std::unique_ptr<PointIndex[]>& newIndexMap,
 
  412        const std::unique_ptr<
bool[]>& mask, 
bool maskValue)
 const 
  414        if (mIndexMap.get() != 
nullptr) {
 
  416            for (
size_t idx = 0, n = 0, N = mSize; n < N; ++n) {
 
  417                if (mask[n] == maskValue) newIndexMap[idx++] = indices[n];
 
  420            for (
size_t idx = 0, n = 0, N = mSize; n < N; ++n) {
 
  421                if (mask[n] == maskValue) {
 
  422                    newIndexMap[idx++] = 
PointIndex(
static_cast<typename PointIndex::IntType
>(n));
 
  431    std::unique_ptr<PointIndex[]> mIndexMap;
 
  432    ParticleArrayT 
const * 
const    mParticleArray;
 
  434    ScalarType                      mMinRadius, mMaxRadius;
 
  438template<
typename ParticleArrayType, 
typename Po
intIndexLeafNodeType>
 
  441    RemapIndices(
const ParticleArrayType& particles, std::vector<PointIndexLeafNodeType*>& nodes)
 
  442        : mParticles(&particles)
 
  443        , mNodes(nodes.
empty() ? nullptr : &nodes.front())
 
  447    void operator()(
const tbb::blocked_range<size_t>& range)
 const 
  449        using PointIndexType = 
typename PointIndexLeafNodeType::ValueType;
 
  450        for (
size_t n = range.begin(), N = range.end(); n != N; ++n) {
 
  452            PointIndexLeafNodeType& node = *mNodes[n];
 
  453            const size_t numIndices = node.indices().size();
 
  455            if (numIndices > 0) {
 
  456                PointIndexType* begin = &node.indices().front();
 
  457                const PointIndexType* end = begin + numIndices;
 
  459                while (begin < end) {
 
  460                    *begin = PointIndexType(
static_cast<typename PointIndexType::IntType
>(
 
  461                        mParticles->getGlobalIndex(*begin)));
 
  468    ParticleArrayType         
const * 
const mParticles;
 
  469    PointIndexLeafNodeType  * 
const * 
const mNodes;
 
  473template<
typename ParticleArrayType, 
typename IndexT>
 
  474struct RadialRangeFilter
 
  476    using PosType = 
typename ParticleArrayType::PosType;
 
  477    using ScalarType = 
typename PosType::value_type;
 
  479    using Range = std::pair<const IndexT*, const IndexT*>;
 
  480    using RangeDeque = std::deque<Range>;
 
  481    using IndexDeque = std::deque<IndexT>;
 
  483    RadialRangeFilter(RangeDeque& ranges, IndexDeque& indices, 
const PosType& xyz,
 
  484        ScalarType radius, 
const ParticleArrayType& particles, 
bool hasUniformRadius = 
false)
 
  489        , mParticles(&particles)
 
  490        , mHasUniformRadius(hasUniformRadius)
 
  492        if (mHasUniformRadius) {
 
  493            ScalarType uniformRadius;
 
  494            mParticles->getRadius(0, uniformRadius);
 
  495            mRadius = mRadius + uniformRadius;
 
  500    template <
typename LeafNodeType>
 
  501    void filterLeafNode(
const LeafNodeType& leaf)
 
  503        const size_t numIndices = leaf.indices().size();
 
  504        if (numIndices > 0) {
 
  505            const IndexT* begin = &leaf.indices().front();
 
  506            filterVoxel(leaf.origin(), begin, begin + numIndices);
 
  510    void filterVoxel(
const Coord&, 
const IndexT* begin, 
const IndexT* end)
 
  514        if (mHasUniformRadius) {
 
  516            const ScalarType searchRadiusSqr = mRadius;
 
  518            while (begin < end) {
 
  519                mParticles->getPos(
size_t(*begin), pos);
 
  520                const ScalarType distSqr = (mCenter - pos).lengthSqr();
 
  521                if (distSqr < searchRadiusSqr) {
 
  522                    mIndices.push_back(*begin);
 
  527            while (begin < end) {
 
  528                const size_t idx = size_t(*begin);
 
  529                mParticles->getPos(idx, pos);
 
  532                mParticles->getRadius(idx, radius);
 
  534                ScalarType searchRadiusSqr = mRadius + radius;
 
  535                searchRadiusSqr *= searchRadiusSqr;
 
  537                const ScalarType distSqr = (mCenter - pos).lengthSqr();
 
  539                if (distSqr < searchRadiusSqr) {
 
  540                    mIndices.push_back(*begin);
 
  549    RadialRangeFilter(
const RadialRangeFilter&);
 
  550    RadialRangeFilter& operator=(
const RadialRangeFilter&);
 
  553    IndexDeque&                     mIndices;
 
  554    PosType                   
const mCenter;
 
  556    ParticleArrayType 
const * 
const mParticles;
 
  557    bool                      const mHasUniformRadius;
 
  561template<
typename ParticleArrayType, 
typename IndexT>
 
  564    using PosType = 
typename ParticleArrayType::PosType;
 
  565    using ScalarType = 
typename PosType::value_type;
 
  567    using Range = std::pair<const IndexT*, const IndexT*>;
 
  568    using RangeDeque = std::deque<Range>;
 
  569    using IndexDeque = std::deque<IndexT>;
 
  571    BBoxFilter(RangeDeque& ranges, IndexDeque& indices,
 
  572        const BBoxd& bbox, 
const ParticleArrayType& particles, 
bool hasUniformRadius = 
false)
 
  575        , mBBox(PosType(bbox.
min()), PosType(bbox.
max()))
 
  576        , mCenter(mBBox.getCenter())
 
  577        , mParticles(&particles)
 
  578        , mHasUniformRadius(hasUniformRadius)
 
  579        , mUniformRadiusSqr(ScalarType(0.0))
 
  581        if (mHasUniformRadius) {
 
  582            mParticles->getRadius(0, mUniformRadiusSqr);
 
  583            mUniformRadiusSqr *= mUniformRadiusSqr;
 
  587    template <
typename LeafNodeType>
 
  588    void filterLeafNode(
const LeafNodeType& leaf)
 
  590        const size_t numIndices = leaf.indices().size();
 
  591        if (numIndices > 0) {
 
  592            const IndexT* begin = &leaf.indices().front();
 
  593            filterVoxel(leaf.origin(), begin, begin + numIndices);
 
  597    void filterVoxel(
const Coord&, 
const IndexT* begin, 
const IndexT* end)
 
  601        if (mHasUniformRadius) {
 
  602            const ScalarType radiusSqr = mUniformRadiusSqr;
 
  604            while (begin < end) {
 
  606                mParticles->getPos(
size_t(*begin), pos);
 
  607                if (mBBox.isInside(pos)) {
 
  608                    mIndices.push_back(*begin++);
 
  612                const ScalarType distSqr = pointToBBoxDistSqr(pos);
 
  613                if (!(distSqr > radiusSqr)) {
 
  614                    mIndices.push_back(*begin);
 
  621            while (begin < end) {
 
  623                const size_t idx = size_t(*begin);
 
  624                mParticles->getPos(idx, pos);
 
  625                if (mBBox.isInside(pos)) {
 
  626                    mIndices.push_back(*begin++);
 
  631                mParticles->getRadius(idx, radius);
 
  632                const ScalarType distSqr = pointToBBoxDistSqr(pos);
 
  633                if (!(distSqr > (radius * radius))) {
 
  634                    mIndices.push_back(*begin);
 
  643    BBoxFilter(
const BBoxFilter&);
 
  644    BBoxFilter& operator=(
const BBoxFilter&);
 
  646    ScalarType pointToBBoxDistSqr(
const PosType& pos)
 const 
  648        ScalarType distSqr = ScalarType(0.0);
 
  650        for (
int i = 0; i < 3; ++i) {
 
  652            const ScalarType a = pos[i];
 
  654            ScalarType b = mBBox.min()[i];
 
  656                ScalarType delta = b - a;
 
  657                distSqr += delta * delta;
 
  662                ScalarType delta = a - b;
 
  663                distSqr += delta * delta;
 
  671    IndexDeque&                     mIndices;
 
  672    math::BBox<PosType>       
const mBBox;
 
  673    PosType                   
const mCenter;
 
  674    ParticleArrayType 
const * 
const mParticles;
 
  675    bool                      const mHasUniformRadius;
 
  676    ScalarType                      mUniformRadiusSqr;
 
  687template<
typename Po
intIndexGr
idType>
 
  688template<
typename ParticleArrayType>
 
  691    const ParticleArrayType& particles, 
double minVoxelSize, 
size_t maxLevels)
 
  693    using SplittableParticleArray =
 
  694        typename particle_atlas_internal::SplittableParticleArray<ParticleArrayType, IndexType>;
 
  695    using SplittableParticleArrayPtr = 
typename SplittableParticleArray::Ptr;
 
  696    using ScalarType = 
typename ParticleArrayType::ScalarType;
 
  700    particle_atlas_internal::ComputeExtremas<ParticleArrayType> extremas(particles);
 
  701    tbb::parallel_reduce(tbb::blocked_range<size_t>(0, particles.size()), extremas);
 
  702    const double firstMin = extremas.minRadius;
 
  703    const double firstMax = extremas.maxRadius;
 
  704    const double firstVoxelSize = std::max(minVoxelSize, firstMin);
 
  706    if (!(firstMax < (firstVoxelSize * 
double(2.0))) && maxLevels > 1) {
 
  708        std::vector<SplittableParticleArrayPtr> 
levels;
 
  709        levels.push_back(SplittableParticleArrayPtr(
 
  710                new SplittableParticleArray(particles, firstMin, firstMax)));
 
  712        std::vector<double> voxelSizeArray;
 
  713        voxelSizeArray.push_back(firstVoxelSize);
 
  715        for (
size_t n = 0; n < maxLevels; ++n) {
 
  717            const double maxParticleRadius = double(
levels.back()->maxRadius());
 
  718            const double particleRadiusLimit = voxelSizeArray.back() * double(2.0);
 
  719            if (maxParticleRadius < particleRadiusLimit) 
break;
 
  721            SplittableParticleArrayPtr newLevel =
 
  722                levels.back()->split(ScalarType(particleRadiusLimit));
 
  723            if (!newLevel) 
break;
 
  725            levels.push_back(newLevel);
 
  726            voxelSizeArray.push_back(
double(newLevel->minRadius()));
 
  729        size_t numPoints = 0;
 
  731        using PointIndexTreeType = 
typename PointIndexGridType::TreeType;
 
  732        using PointIndexLeafNodeType = 
typename PointIndexTreeType::LeafNodeType;
 
  734        std::vector<PointIndexLeafNodeType*> nodes;
 
  736        for (
size_t n = 0, N = 
levels.size(); n < N; ++n) {
 
  738            const SplittableParticleArray& particleArray = *
levels[n];
 
  740            numPoints += particleArray.size();
 
  742            mMinRadiusArray.push_back(
double(particleArray.minRadius()));
 
  743            mMaxRadiusArray.push_back(
double(particleArray.maxRadius()));
 
  749            grid->tree().getNodes(nodes);
 
  751            tbb::parallel_for(tbb::blocked_range<size_t>(0, nodes.size()),
 
  752                particle_atlas_internal::RemapIndices<SplittableParticleArray,
 
  753                    PointIndexLeafNodeType>(particleArray, nodes));
 
  755            mIndexGridArray.push_back(grid);
 
  759        mMinRadiusArray.push_back(firstMin);
 
  760        mMaxRadiusArray.push_back(firstMax);
 
  761        mIndexGridArray.push_back(
 
 
  767template<
typename Po
intIndexGr
idType>
 
  768template<
typename ParticleArrayType>
 
  771    const ParticleArrayType& particles, 
double minVoxelSize, 
size_t maxLevels)
 
  774    ret->construct(particles, minVoxelSize, maxLevels);
 
 
  783template<
typename Po
intIndexGr
idType>
 
  790    , mIter(mRangeList.begin())
 
  793    , mAccessorListSize(atlas.
levels())
 
  795    if (mAccessorListSize > 0) {
 
  797        for (
size_t n = 0, N = mAccessorListSize; n < N; ++n) {
 
 
  804template<
typename Po
intIndexGr
idType>
 
  808    mIter = mRangeList.begin();
 
  809    if (!mRangeList.empty()) {
 
  810        mRange = mRangeList.front();
 
  811    } 
else if (mIndexArray) {
 
  812        mRange.first = mIndexArray.get();
 
  813        mRange.second = mRange.first + mIndexArraySize;
 
  815        mRange.first = 
static_cast<IndexType*
>(
nullptr);
 
  816        mRange.second = 
static_cast<IndexType*
>(
nullptr);
 
 
  821template<
typename Po
intIndexGr
idType>
 
  826    if (mRange.first >= mRange.second && mIter != mRangeList.end()) {
 
  828        if (mIter != mRangeList.end()) {
 
  830        } 
else if (mIndexArray) {
 
  831            mRange.first = mIndexArray.get();
 
  832            mRange.second = mRange.first + mIndexArraySize;
 
 
  838template<
typename Po
intIndexGr
idType>
 
  842    if (!this->
test()) 
return false;
 
 
  848template<
typename Po
intIndexGr
idType>
 
  853    typename RangeDeque::const_iterator it =
 
  854        mRangeList.begin(), end = mRangeList.end();
 
  856    for ( ; it != end; ++it) {
 
  857        count += it->second - it->first;
 
  860    return count + mIndexArraySize;
 
 
 
  914    const Vec3d& center, 
double radius, 
const ParticleArrayType& particles)
 
  916    using PosType = 
typename ParticleArrayType::PosType;
 
  917    using ScalarType = 
typename ParticleArrayType::ScalarType;
 
  923    std::deque<IndexType> filteredIndices;
 
  924    std::vector<CoordBBox> searchRegions;
 
  926    const double iRadius = radius * double(1.0 / std::sqrt(3.0));
 
  928    const Vec3d ibMin(center[0] - iRadius, center[1] - iRadius, center[2] - iRadius);
 
  929    const Vec3d ibMax(center[0] + iRadius, center[1] + iRadius, center[2] + iRadius);
 
  931    const Vec3d bMin(center[0] - radius, center[1] - radius, center[2] - radius);
 
  932    const Vec3d bMax(center[0] + radius, center[1] + radius, center[2] + radius);
 
  934    const PosType pos = PosType(center);
 
  935    const ScalarType dist = ScalarType(radius);
 
  937    for (
size_t n = 0, N = mAccessorListSize; n < N; ++n) {
 
  939        const double maxRadius = mAtlas->maxRadius(n);
 
  942        const openvdb::math::Transform& xform = mAtlas->pointIndexGrid(n).transform();
 
  947            xform.worldToIndexCellCentered(ibMin),
 
  948            xform.worldToIndexCellCentered(ibMax));
 
  950        inscribedRegion.
expand(-1); 
 
  953        point_index_grid_internal::pointIndexSearch(mRangeList, acc, inscribedRegion);
 
  955        searchRegions.clear();
 
  958            xform.worldToIndexCellCentered(bMin - 
maxRadius),
 
  959            xform.worldToIndexCellCentered(bMax + 
maxRadius));
 
  961        inscribedRegion.
expand(1);
 
  962        point_index_grid_internal::constructExclusiveRegions(
 
  963            searchRegions, region, inscribedRegion);
 
  965        using FilterType = particle_atlas_internal::RadialRangeFilter<ParticleArrayType, IndexType>;
 
  966        FilterType filter(mRangeList, filteredIndices, pos, dist, particles, uniformRadius);
 
  968        for (
size_t i = 0, I = searchRegions.size(); i < I; ++i) {
 
  969            point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[i]);
 
  973    point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize);