Program Listing for File AABBtree.cc

Return to documentation for file (src/AABBtree.cc)

/*
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 *                                                                     *
 * The ACME project                                                    *
 *                                                                     *
 * Copyright (c) 2020, Davide Stocco and Enrico Bertolazzi.            *
 *                                                                     *
 * The ACME project and its components are supplied under the terms of *
 * the open source BSD 2-Clause License. The contents of the ACME      *
 * project and its components may not be copied or disclosed except in *
 * accordance with the terms of the BSD 2-Clause License.              *
 *                                                                     *
 * URL: https://opensource.org/licenses/BSD-2-Clause                   *
 *                                                                     *
 *    Davide Stocco                                                    *
 *    Department of Industrial Engineering                             *
 *    University of Trento                                             *
 *    e-mail: davide.stocco@unitn.it                                   *
 *                                                                     *
 *    Enrico Bertolazzi                                                *
 *    Department of Industrial Engineering                             *
 *    University of Trento                                             *
 *    e-mail: enrico.bertolazzi@unitn.it                               *
 *                                                                     *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/


#ifndef DOXYGEN_SHOULD_SKIP_THIS

#include "acme.hh"

namespace acme
{

  /*\
   |      _        _    ____  ____  _
   |     / \      / \  | __ )| __ )| |_ _ __ ___  ___
   |    / _ \    / _ \ |  _ \|  _ \| __| '__/ _ \/ _ \
   |   / ___ \  / ___ \| |_) | |_) | |_| | |  __/  __/
   |  /_/   \_\/_/   \_\____/|____/ \__|_|  \___|\___|
   |
  \*/

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  AABBtree::~AABBtree(void)
  {
    this->m_ptrbox.reset();
    this->m_children.clear();
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  AABBtree::AABBtree(void)
  {
    this->m_ptrbox.reset();
    this->m_children.clear();
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  void
  AABBtree::clear(void)
  {
    this->m_ptrbox.reset();
    this->m_children.clear();
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  bool
  AABBtree::isEmpty(void)
    const
  {
    return this->m_children.empty() && !this->m_ptrbox;
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  void
  AABBtree::build(
    aabb::vecptr const & boxes
  )
  {
    clear();

    if (boxes.empty())
      {return;}

    integer size = boxes.size();

    if (size == 1)
    {
      this->m_ptrbox = boxes.front();
      return;
    }

    this->m_ptrbox = std::make_shared<aabb const>(boxes);

    real xmin = this->m_ptrbox->min(0);
    real ymin = this->m_ptrbox->min(1);
    real zmin = this->m_ptrbox->min(2);
    real xmax = this->m_ptrbox->max(0);
    real ymax = this->m_ptrbox->max(1);
    real zmax = this->m_ptrbox->max(2);

    aabb::vecptr pos_boxes;
    aabb::vecptr neg_boxes;

    if ((xmax - xmin) > (ymax - ymin) && (xmax - xmin) > (zmax - zmin))
    {
      real cut_pos = (xmax + xmin) / real(2.0);
      aabb::vecptr::const_iterator it;
      for (it = boxes.begin(); it != boxes.end(); ++it)
      {
        real xmid = ((*it)->min(0) + (*it)->max(0)) / real(2.0);
        if (xmid > cut_pos)
          {pos_boxes.push_back(*it);}
        else
          {neg_boxes.push_back(*it);}
      }
    }
    else if ((ymax - ymin) > (xmax - xmin) && (ymax - ymin) > (zmax - zmin))
    {
      real cut_pos = (ymax + ymin) / real(2.0);
      aabb::vecptr::const_iterator it;
      for (it = boxes.begin(); it != boxes.end(); ++it)
      {
        real ymid = ((*it)->min(1) + (*it)->max(1)) / real(2.0);
        if (ymid > cut_pos)
          {pos_boxes.push_back(*it);}
        else
          {neg_boxes.push_back(*it);}
      }
    }
    else
    {
      real cut_pos = (zmax + zmin) / real(2.0);
      aabb::vecptr::const_iterator it;
      for (it = boxes.begin(); it != boxes.end(); ++it)
      {
        real zmid = ((*it)->min(1) + (*it)->max(1)) / real(2.0);
        if (zmid > cut_pos)
          {pos_boxes.push_back(*it);}
        else
          {neg_boxes.push_back(*it);}
      }
    }

    if (neg_boxes.empty())
    {
      aabb::vecptr::iterator mid_idx;
      mid_idx = pos_boxes.begin() + pos_boxes.size() / real(2.0);
      neg_boxes.insert(neg_boxes.end(), mid_idx, pos_boxes.end());
      pos_boxes.erase(mid_idx, pos_boxes.end());
    }
    else if (pos_boxes.empty())
    {
      aabb::vecptr::iterator mid_idx;
      mid_idx = neg_boxes.begin() + neg_boxes.size() / real(2.0);
      pos_boxes.insert(pos_boxes.end(), mid_idx, neg_boxes.end());
      neg_boxes.erase(mid_idx, neg_boxes.end());
    }

    AABBtree::ptr neg = std::make_shared<AABBtree>();
    AABBtree::ptr pos = std::make_shared<AABBtree>();

    neg->build(neg_boxes);
    if (!neg->isEmpty())
      {this->m_children.push_back(neg);}

    pos->build(pos_boxes);
    if (!pos->isEmpty())
      {this->m_children.push_back(pos);}
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  void
  AABBtree::print(
    out_stream & os,
    integer      level
  )
    const
  {
    if (this->isEmpty())
    {
      os << "[EMPTY AABB tree]" << std::endl;
    }
    else
    {
      os << std::scientific
         << std::showpoint
         << std::setprecision(6)
         << "Box =" << std::endl
         << "Minimum = [ " << this->m_ptrbox->min(0) << ", " << this->m_ptrbox->min(1) << ", " << this->m_ptrbox->min(2) << " ]'" << std::endl
         << "Maximum = [ " << this->m_ptrbox->max(0) << ", " << this->m_ptrbox->max(1) << ", " << this->m_ptrbox->max(2) << " ]'" << std::endl
         << std::endl;
      AABBtree::vecptr::const_iterator it;
      for (it = this->m_children.begin(); it != this->m_children.end(); ++it)
      {
        (*it)->print(os, level + 1);
      }
    }
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  void
  AABBtree::intersection(
    AABBtree         const & tree,
    aabb::vecpairptr       & intersection_list,
    bool                     swap_tree
  )
    const
  {
    if (!tree.m_ptrbox->intersects(*this->m_ptrbox))
      return;
    integer icase = (this->m_children.empty() ? 0 : 1) +
                    (tree.m_children.empty() ? 0 : 2);
    switch (icase)
    {
    case 0: // Both are leafs
      if (swap_tree)
           {intersection_list.push_back(aabb::pairptr(tree.m_ptrbox, this->m_ptrbox));}
      else {intersection_list.push_back(aabb::pairptr(this->m_ptrbox, tree.m_ptrbox));}
      break;
    case 1: // First is a tree, second is a leaf
    {
      AABBtree::vecptr::const_iterator it;
      for (it = this->m_children.begin(); it != this->m_children.end(); ++it)
      {
        tree.intersection(**it, intersection_list, !swap_tree);
      }
    }
    break;
    case 2: // First leaf, second is a tree
    {
      AABBtree::vecptr::const_iterator it;
      for (it = tree.m_children.begin(); it != tree.m_children.end(); ++it)
      {
        this->intersection(**it, intersection_list, swap_tree);
      }
    }
    break;
    case 3: // First is a tree, second is a tree
    {
      AABBtree::vecptr::const_iterator c1;
      AABBtree::vecptr::const_iterator c2;
      for (c1 = this->m_children.begin(); c1 != this->m_children.end(); ++c1)
      {
        for (c2 = tree.m_children.begin(); c2 != tree.m_children.end(); ++c2)
        {
          (*c1)->intersection(**c2, intersection_list, swap_tree);
        }
      }
    }
    break;
    }
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  real
  AABBtree::minimumExteriorDistance(
    point    const & query,
    AABBtree const & tree,
    real             distance)
  {
    AABBtree::vecptr const & tree_children = tree.m_children;
    if (tree_children.empty())
    {
      real dst = tree.m_ptrbox->exteriorDistance(query);
      return std::min(dst, distance);
    }
    real dmin = tree.m_ptrbox->centerDistance(query);
    if (dmin > distance) {return distance;}
    AABBtree::vecptr::const_iterator it;
    for (it = tree_children.begin(); it != tree_children.end(); ++it)
    {
      distance = minimumExteriorDistance(query, **it, distance);
    }
    return distance;
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  void
  AABBtree::selectLessThanDistance(
    point        const & query,
    real                 distance,
    AABBtree     const & tree,
    aabb::vecptr       & candidate_list
  )
  {
    AABBtree::vecptr const & tree_children = tree.m_children;
    real dst= tree.m_ptrbox->centerDistance(query);
    if (dst <= distance)
    {
      if (tree_children.empty())
      {
        candidate_list.push_back(tree.m_ptrbox);
      }
      else
      {
        AABBtree::vecptr::const_iterator it;
        for (it = tree_children.begin(); it != tree_children.end(); ++it)
        {
          selectLessThanDistance(query, distance, **it, candidate_list);
        }
      }
    }
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  void
  AABBtree::selectMinimumDistance(
    point        const & query,
    aabb::vecptr       & candidate_list
  )
    const
  {
    real distance = this->minimumExteriorDistance(query, *this, INFTY);
    this->selectLessThanDistance(query, distance, *this, candidate_list);
  }

  // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

} // namespace acme

#endif