/** * @defgroup table_3d 3D Tables * @brief Structures and functions related to 3D tables, such as VE, Spark Advance, AFR etc. * * Logical: * - each 3D table is a continuous height map spread over a cartesian (x, y) plane * - Continuous: we expect to interpolate between any 4 points * - The axes are * - Bounded. I.e. non-infinite * - Non-linear. I.e. x[n]-x[n-1] != x[n+1]-x[n] * - Increasing. I.e. x[n] >= x[n-1] * - Do not have to start at [0,0] * * E.g. for a 3x3 table, this is what the TS table editor would show: *
 *      Y-Max    V6      V7      V8
 *      Y-Int    V3      V4      V5
 *      Y-Min    V0      V1      V2
 *              X-Min   X-Int   X-Max
 * 
* * In memory, we store rows in reverse: * - Both axes are inverted: * - x[0] stores \c X-Max * - x[2] stores \c X-Min * - y[0] stores \c Y-Max * - y[2] stores \c Y-Min * - The value locations match the axes. * - value[0][0] stores \c V6. * - value[2][0] stores \c V0. * * I.e. *
 *      Y-Min    V0      V1      V2
 *      Y-Int    V3      V4      V5
 *      Y-Max    V6      V7      V8
 *              X-Min   X-Int   X-Max
 * 
* @{ */ /** \file * @brief 3D table data types and functions */ #pragma once #include "table3d_interpolate.h" #include "table3d_axes.h" #include "table3d_values.h" #define TO_TYPE_KEY(size, xDom, yDom) table3d ## size ## xDom ## yDom ## _key /** * @brief Table \b type identifiers. Limited compile time RTTI * * With no virtual functions (they have quite a bit of overhead in both space & * time), we have to pass void* around in certain cases. In order to cast that * back to a concrete table type, we need to somehow identify the type. * * Once approach is to register each type - but that requires a central registry * which will use RAM. * * Since we have a compile time fixed set of table types, we can map a unique * identifier to the type via a cast - this enum is that unique identifier. * * Typically used in conjunction with the '#CONCRETE_TABLE_ACTION' macro */ enum table_type_t { table_type_None, #define TABLE3D_GEN_TYPEKEY(size, xDom, yDom) TO_TYPE_KEY(size, xDom, yDom), TABLE3D_GENERATOR(TABLE3D_GEN_TYPEKEY) }; // Generate the 3D table types #define TABLE3D_GEN_TYPE(size, xDom, yDom) \ /** @brief A 3D table with size x size dimensions, xDom x-axis and yDom y-axis */ \ struct TABLE3D_TYPENAME_BASE(size, xDom, yDom) \ { \ typedef TABLE3D_TYPENAME_AXIS(size, xDom) xaxis_t; \ typedef TABLE3D_TYPENAME_AXIS(size, yDom) yaxis_t; \ typedef TABLE3D_TYPENAME_VALUE(size, xDom, yDom) value_t; \ /* This will take up zero space unless we take the address somewhere */ \ static constexpr table_type_t type_key = TO_TYPE_KEY(size, xDom, yDom); \ \ table3DGetValueCache get_value_cache; \ value_t values; \ xaxis_t axisX; \ yaxis_t axisY; \ }; TABLE3D_GENERATOR(TABLE3D_GEN_TYPE) // Generate get3DTableValue() functions #define TABLE3D_GEN_GET_TABLE_VALUE(size, xDom, yDom) \ static inline table3d_value_t get3DTableValue(TABLE3D_TYPENAME_BASE(size, xDom, yDom) *pTable, table3d_axis_t y, table3d_axis_t x) \ { \ return get3DTableValue( &pTable->get_value_cache, \ TABLE3D_TYPENAME_BASE(size, xDom, yDom)::value_t::row_size, \ pTable->values.values, \ pTable->axisX.axis, \ pTable->axisY.axis, \ y, x); \ } TABLE3D_GENERATOR(TABLE3D_GEN_GET_TABLE_VALUE) // =============================== Table function calls ========================= // With no templates or inheritance we need some way to call functions // for the various distinct table types. CONCRETE_TABLE_ACTION dispatches // to a caller defined function overloaded by the type of the table. #define CONCRETE_TABLE_ACTION_INNER(size, xDomain, yDomain, action, ...) \ case TO_TYPE_KEY(size, xDomain, yDomain): action(size, xDomain, yDomain, ##__VA_ARGS__); #define CONCRETE_TABLE_ACTION(testKey, action, defaultAction, ...) \ switch ((table_type_t)testKey) { \ TABLE3D_GENERATOR(CONCRETE_TABLE_ACTION_INNER, action, ##__VA_ARGS__ ) \ default: defaultAction; } // =============================== Table function calls ========================= table_value_iterator rows_begin(void *pTable, table_type_t key); table_axis_iterator x_begin(void *pTable, table_type_t key); table_axis_iterator x_rbegin(void *pTable, table_type_t key); table_axis_iterator y_begin(void *pTable, table_type_t key); table_axis_iterator y_rbegin(void *pTable, table_type_t key); /** @} */