speeduino/speeduino/table3d_values.h

203 lines
6.8 KiB
C++

/**
* @addtogroup table_3d
* @{
*/
/** \file
* @brief 3D table value structs and iterators
*/
#pragma once
#include "table3d_typedefs.h"
// ========================= INTRA-ROW ITERATION =========================
/** @brief Iterate through a table row. I.e. constant Y, changing X
*
* Instances of this class are normally created via a table_value_iterator instance.
*/
class table_row_iterator {
public:
/**
* @brief Construct
* @param pRowStart Pointer to the 1st element in the row
* @param rowWidth The number of elements to in the row
*/
table_row_iterator(const table3d_value_t *pRowStart, table3d_dim_t rowWidth)
: pValue(pRowStart), pEnd(pRowStart+rowWidth) //cppcheck-suppress misra-c2012-10.4
{
}
/** @brief Pointer to the end of the row */
const table3d_value_t* end(void) const { return pEnd; }
/** @copydoc table_row_iterator::end() const */
table3d_value_t* end(void) { return const_cast<table3d_value_t *>(pEnd); }
/** @brief Advance the iterator
* @param steps The number of elements to move the iterator
*/
table_row_iterator& advance(table3d_dim_t steps)
{
pValue = pValue + steps;
return *this;
}
/** @brief Increment the iterator by one element*/
table_row_iterator& operator++(void)
{
return advance(1);
}
/** @brief Test for end of iteration */
bool at_end(void) const
{
return pValue == pEnd;
}
/** @brief Dereference the iterator */
const table3d_value_t& operator*(void) const
{
return *pValue;
}
/** @copydoc table_row_iterator::operator*() const */
table3d_value_t& operator*(void)
{
return *const_cast<table3d_value_t *>(pValue);
}
/** @brief Number of elements available */
table3d_dim_t size(void) const { return pEnd-pValue; }
private:
const table3d_value_t *pValue;
const table3d_value_t *pEnd;
};
// ========================= INTER-ROW ITERATION =========================
/** @brief Iterate through a tables values, row by row. */
class table_value_iterator
{
public:
/**
* @brief Construct
* @param pValues Pointer to the 1st value in a 1-d array
* @param axisSize The number of columns & elements per row (square tables only)
*/
table_value_iterator(const table3d_value_t *pValues, table3d_dim_t axisSize)
: pRowsStart(pValues + (axisSize*(axisSize-1U))), //cppcheck-suppress misra-c2012-10.4
pRowsEnd(pValues - axisSize),
rowWidth(axisSize)
{
// Table values are not linear in memory - rows are in reverse order
// E.g. a 4x4 table with logical element [0][0] at the bottom left
// (normal cartesian coordinates) has this layout.
// 0 1 2 3
// 4 5 6 7
// 8 9 10 11
// 12 13 14 15
// So we start at row 3 (index 12 of the array) and iterate towards
// the start of the array
//
// This all supports fast 3d interpolation.
}
/** @brief Advance the iterator
* @param rows The number of \b rows to move
*/
table_value_iterator& advance(table3d_dim_t rows)
{
pRowsStart = pRowsStart - (rowWidth * rows);
return *this;
}
/** @brief Increment the iterator by one \b row */
table_value_iterator& operator++(void)
{
return advance(1U);
}
/** @brief Dereference the iterator to access a row of data */
const table_row_iterator operator*(void) const
{
return table_row_iterator(pRowsStart, rowWidth);
}
/** @copydoc table_value_iterator::operator*() const */
table_row_iterator operator*(void)
{
return table_row_iterator(pRowsStart, rowWidth);
}
/** @brief Test for end of iteration */
bool at_end(void) const
{
return pRowsStart == pRowsEnd;
}
private:
const table3d_value_t *pRowsStart;
const table3d_value_t *pRowsEnd;
table3d_dim_t rowWidth;
};
#define TABLE3D_TYPENAME_VALUE(size, xDom, yDom) CONCAT(TABLE3D_TYPENAME_BASE(size, xDom, yDom), _values)
#define TABLE3D_GEN_VALUES(size, xDom, yDom) \
/** @brief The values for a 3D table with size x size dimensions, xDom x-axis and yDom y-axis */ \
struct TABLE3D_TYPENAME_VALUE(size, xDom, yDom) { \
/** @brief The number of items in a row. I.e. it's length */ \
static constexpr table3d_dim_t row_size = (size); \
/** @brief The number of rows */ \
static constexpr table3d_dim_t num_rows = (size); \
/** \
@brief The row values \
@details Table values are not linear in memory - rows are in reverse order<br> \
E.g. a 3x3 table with logical element [0][0] at the bottom left \
(normal cartesian coordinates) has this layout:<br> \
6, 7, 8, 3, 4, 5, 0, 1, 2 \
*/ \
table3d_value_t values[(uint16_t)row_size*num_rows]; \
\
/** @brief Iterate over the values */ \
table_value_iterator begin(void) \
{ \
return table_value_iterator(values, row_size); \
} \
\
/** \
@brief Direct access to table value element from a linear index \
@details Since table values aren't laid out linearly, converting a linear \
offset to the equivalent memory address requires a modulus operation.<br> \
<br> \
This is slow, since AVR hardware has no divider. We can gain performance \
in 2 ways:<br> \
1. Forcing uint8_t calculations. These are much faster than 16-bit calculations<br> \
2. Compiling this per table *type*. This encodes the axis length as a constant \
thus allowing the optimising compiler more opportunity. E.g. for axis lengths \
that are a power of 2, the modulus can be optimised to add/multiply/shift - much \
cheaper than calling a software division routine such as __udivmodqi4<br> \
<br> \
THIS IS WORTH 20% to 30% speed up<br> \
<br> \
This limits us to 16x16 tables. If we need bigger and move to 16-bit \
operations, consider using libdivide. <br> \
*/ \
table3d_value_t& value_at(table3d_dim_t linear_index) \
{ \
static_assert(row_size<17U, "Table is too big"); \
static_assert(num_rows<17U, "Table is too big"); \
/* Zero length will mess up unsigned calcs */ \
static_assert(row_size>0U, "No zero length rows"); \
static_assert(num_rows>0U, "No empty tables"); \
constexpr table3d_dim_t first_index = row_size*(table3d_dim_t)(num_rows-1U); \
const table3d_dim_t index = (table3d_dim_t)(first_index + (table3d_dim_t)(2U*(linear_index % row_size)) - linear_index); \
return values[index]; \
} \
};
TABLE3D_GENERATOR(TABLE3D_GEN_VALUES)
/** @} */