// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 #pragma once namespace frantic { namespace volumetrics { namespace levelset { // Forward declaration of the rle_index_spec classes. class rle_index_spec; struct run_data; /** * This rle_run_iterator is designed to provide convenient iteration over all the runs for * one scanline in an rle_index_spec. * * Here's an example usage for one run: * *
 *  // Make i the begin iterator for the scanline being processed.
 *  rle_run_iterator i(ris, ris.y_to_b(y), ris.z_to_c(z));
 *  // Make ie the past-the-end iterator using the default constructor.
 *  rle_run_iterator ie;
 *  for( ; i != ie; ++i ) {
 *    // i.get_xmin() is the starting X coordinate
 *    // i.get_xsize() is the length of the run
 *    // i.get_data_index() is the data index or region code for the run
 *  }
 *  // At loop end, i.get_xmin() is the X coordinate at the very end of the processed scanline (assuming a non-empty
 * scanline)
 *  // (NOTE that ie.get_xmin() is *NOT* that same X coordinate, note how it was created without knowledge of the
 * scanline)
 * 
 *
 * Here's how you would typically use it to visit all runs in an rle_index_spec:
 *
 * 
 *  const rle_index_spec& ris = ...;
 *  boundbox3 outerBounds = ris.outer_bounds();
 *  for( int z = outerBounds.zminimum(); z <= outerBounds.zmaximum(); ++z ) {
 *    for( int y = outerBounds.yminimum(); y <= outerBounds.ymaximum(); ++y ) {
 *      rle_run_iterator i(ris, ris.y_to_b(y), ris.z_to_c(z)), ie;
 *      for( ; i != ie; ++i ) {
 *        // i.get_xmin() is the starting X coordinate
 *        // i.get_xsize() is the length of the run
 *        // i.get_data_index() is the data index or region code for the run
 *      }
 *      // Here, i.get_xmin() is the X coordinate at the very end of the processed scanline (assuming a non-empty
 * scanline)
 *      // (NOTE that ie.get_xmin() is *NOT* that same X coordinate, note how it was created without knowledge of the
 * scanline)
 *    }
 *  }
 * 
 */
class rle_run_iterator {
    const run_data *m_rd, *m_rdEnd;
    int m_exteriorRegionCode;
    boost::int32_t m_dataIndex;
    boost::int32_t m_xStart, m_xSize;
  public:
    /**
     * Constructs an iterator which will iterate over the runs in the scanline of the given BC coordinates.
     *
     * @param  ris  The rle_index_spec for run iteration.
     * @param  b    The b coordinate for the scanline.
     * @param  c    The c coordinate for the scanline.
     */
    rle_run_iterator( const rle_index_spec& ris, int b, int c );
    /**
     * Constructs the 'past-the-end' iterator.
     */
    rle_run_iterator();
    /**
     * Pre-increments the iterator.
     *
     * @todo Do both pre- and post-increment correctly
     */
    rle_run_iterator& operator++();
    /**
     * For equality comparison, we don't check everything.  But, the subset of what we
     * check is enough to determine equality.  Note that we're checking the things most likely
     * to be different first, to be able to return false as quickly as possible in the common
     * cases.
     */
    bool operator==( const rle_run_iterator& iter ) const { return m_rd == iter.m_rd; }
    bool operator!=( const rle_run_iterator& iter ) const { return !operator==( iter ); }
    /**
     * This gets the data index of the run, or the region code of the run if it
     * is an undefined run.
     */
    boost::int32_t get_data_index() const { return m_dataIndex; }
    /**
     * This returns the minimum X coordinate of the run.
     */
    boost::int32_t get_xmin() const { return m_xStart; }
    /**
     * This returns the maximum X coordinate of the run.
     */
    boost::int32_t get_xmax() const { return m_xStart + m_xSize - 1; }
    /**
     * This returns the size of the run in the X dimension.
     */
    boost::int32_t get_xsize() const { return m_xSize; }
    /**
     * This gets the data index of the next run, or the region code of the run if it
     * is an undefined run.  It's more costly than retrieving the current index due
     * to the extra indirection.
     */
    boost::int32_t get_next_run_data_index() const {
        if( m_rd == m_rdEnd )
            return m_exteriorRegionCode;
        const run_data* next_rd = m_rd + 1;
        if( next_rd != m_rdEnd )
            return next_rd->dataIndex;
        else
            return m_exteriorRegionCode;
    }
};
} // namespace levelset
} // namespace volumetrics
} // namespace frantic