#pragma once #include "Point.h" /// /// CarToRas class. /// namespace EmberNs { /// /// When iterating, everything is positioned in terms of a carteseian plane with 0,0 in the center like so: /// [-1,1] [1,1] /// [-1,-1] [1,-1] /// However, when accumulating to the histogram, the data is stored in the traditional raster coordinate system /// of 0,0 at the top left and x,y at the bottom right. This class provides functionality to convert from one /// to the other and is used when accumulating a sub batch of iteration results to the histogram. /// Note the functions use reference arguments for the converted values because they are slightly faster than returning a value. /// Template argument expected to be float or double. /// template class EMBER_API CarToRas { public: /// /// Empty constructor. This class should never be used unless it's been properly constructed with the constructor that takes arguments. /// CarToRas() = default; /// /// Constructor that takes arguments to set up the bounds and passes them to Init(). /// /// The lower left x of the cartesian plane /// The lower left y of the cartesian plane /// The upper right x of the cartesian plane /// The upper right y of the cartesian plane /// The width in pixels of the raster image/histogram /// The height in pixels of the raster image/histogram /// The aspect ratio, generally 1 CarToRas(T carLlX, T carLlY, T carUrX, T carUrY, size_t rasW, size_t rasH, T aspectRatio) { Init(carLlX, carLlY, carUrX, carUrY, rasW, rasH, aspectRatio); } /// /// Default copy constructor. /// /// The CarToRas object to copy CarToRas(const CarToRas& carToRas) { CarToRas::operator=(carToRas); } /// /// Copy constructor to copy a CarToRas object of type U. /// /// The CarToRas object to copy template CarToRas(const CarToRas& carToRas) { CarToRas::operator=(carToRas); } /// /// Default assignment operator. /// /// The CarToRas object to copy CarToRas& operator = (const CarToRas& carToRas) { if (this != &carToRas) CarToRas::operator=(carToRas); return *this; } /// /// Assignment operator to assign a CarToRas object of type U. /// /// The CarToRas object to copy. /// Reference to updated self template CarToRas& operator = (const CarToRas& carToRas) { m_RasWidth = carToRas.RasWidth(); m_RasHeight = carToRas.RasHeight(); m_OneRow = T(carToRas.OneRow()); m_OneCol = T(carToRas.OneCol()); m_PixPerImageUnitW = T(carToRas.PixPerImageUnitW()); m_RasLlX = T(carToRas.RasLlX()); m_PixPerImageUnitH = T(carToRas.PixPerImageUnitH()); m_RasLlY = T(carToRas.RasLlY()); m_CarLlX = T(carToRas.CarLlX()); m_CarLlY = T(carToRas.CarLlY()); m_CarUrX = T(carToRas.CarUrX()); m_CarUrY = T(carToRas.CarUrY()); m_PadCarLlX = T(carToRas.PadCarLlX()); m_PadCarLlY = T(carToRas.PadCarLlY()); m_PadCarUrX = T(carToRas.PadCarUrX()); m_PadCarUrY = T(carToRas.PadCarUrY()); m_CarHalfX = T(carToRas.CarHalfX()); m_CarHalfY = T(carToRas.CarHalfY()); m_CarCenterX = T(carToRas.CarCenterX()); m_CarCenterY = T(carToRas.CarCenterY()); return *this; } /// /// Initialize the dimensions with the specified bounds. /// /// The lower left x of the cartesian plane /// The lower left y of the cartesian plane /// The upper right x of the cartesian plane /// The upper right y of the cartesian plane /// The width in pixels of the raster image/histogram /// The height in pixels of the raster image/histogram /// The aspect ratio, generally 1 void Init(T carLlX, T carLlY, T carUrX, T carUrY, size_t rasW, size_t rasH, T aspectRatio) { m_RasWidth = rasW; m_RasHeight = rasH; m_CarLlX = carLlX; m_CarLlY = carLlY; m_CarUrX = carUrX; m_CarUrY = carUrY; T carW = m_CarUrX - m_CarLlX;//Right minus left. T carH = m_CarUrY - m_CarLlY;//Top minus bottom. T invSizeW = T(1.0) / carW; T invSizeH = T(1.0) / carH; m_PixPerImageUnitW = static_cast(rasW) * invSizeW; m_RasLlX = m_PixPerImageUnitW * carLlX; m_PixPerImageUnitH = static_cast(rasH) * invSizeH; m_RasLlY = m_PixPerImageUnitH * carLlY; m_OneRow = std::abs(m_CarUrY - m_CarLlY) / m_RasHeight; m_OneCol = std::abs(m_CarUrX - m_CarLlX) / m_RasWidth; m_PadCarLlX = m_CarLlX + m_OneCol; m_PadCarUrX = m_CarUrX - m_OneCol; m_PadCarLlY = m_CarLlY + m_OneRow; m_PadCarUrY = m_CarUrY - m_OneRow; m_CarHalfX = (m_CarUrX - m_CarLlX) / 2; m_CarHalfY = (m_CarUrY - m_CarLlY) / 2; m_CarCenterX = m_CarLlX + m_CarHalfX; m_CarCenterY = m_CarLlY + m_CarHalfY; } /// /// Convert a cartesian x, y coordinate to a raster x, y coordinate. /// This will flip the Y coordinate, so points that hit the bottom of the cartesian plane will /// be mapped to the top of the histogram and vice versa. /// There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test, /// but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform an additional check after this call to make sure the /// mapped point is in bounds. /// /// The cartesian x /// The cartesian y /// The converted raster x /// The converted raster y inline void Convert(T cartX, T cartY, size_t& rasX, size_t& rasY) { rasX = static_cast(m_PixPerImageUnitW * cartX - m_RasLlX); rasY = static_cast(m_RasLlY - (m_PixPerImageUnitH * cartY)); } /// /// Convert a cartesian x, y coordinate to a single raster buffer index. /// This will flip the Y coordinate, so points that hit the bottom of the cartesian plane will /// be mapped to the top of the histogram and vice versa. /// There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test, /// but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform an additional check after this call to make sure the /// mapped point is in bounds. /// /// The cartesian x /// The cartesian y /// The converted single raster buffer index inline void Convert(T cartX, T cartY, size_t& singleBufferIndex) { singleBufferIndex = static_cast(m_PixPerImageUnitW * cartX - m_RasLlX) + (m_RasWidth * static_cast(m_PixPerImageUnitH * cartY - m_RasLlY)); } /// /// Convert a cartesian x, y point to a single raster buffer index. /// This will flip the Y coordinate, so points that hit the bottom of the cartesian plane will /// be mapped to the top of the histogram and vice versa. /// This is the most efficient possible way of converting, consisting of only /// a multiply and subtract per coordinate element. /// There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test, /// but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform an additional check after this call to make sure the /// mapped point is in bounds. /// /// The cartesian y /// The converted single raster buffer index inline void Convert(Point& point, size_t& singleBufferIndex) { //singleBufferIndex = static_cast(Round(m_PixPerImageUnitW * point.m_X - m_RasLlX) + (m_RasWidth * Round(m_PixPerImageUnitH * point.m_Y - m_RasLlY))); singleBufferIndex = static_cast(m_PixPerImageUnitW * point.m_X - m_RasLlX) + (m_RasWidth * static_cast(m_PixPerImageUnitH * point.m_Y - m_RasLlY)); } /// /// Determine if a point in the cartesian plane can be converted to a point within the raster plane. /// There is a very slim chance that a point will be right on the border and will technically be in bounds, passing the InBounds() test, /// but ends up being mapped to a histogram bucket that is out of bounds due to roundoff error. Perform an additional check after this call to make sure the /// mapped point is in bounds. /// /// The point to test /// True if within bounds, else false inline bool InBounds(Point& point) { //Debug check for hitting the very first pixel in the image. //if (point.m_Y > m_CarLlY && point.m_Y <= m_PadCarLlY && //Mapped to top row... // point.m_X > m_CarLlX && point.m_X <= m_PadCarLlX)//...first col. //{ // cout << "First pixel hit.\n"; //} return point.m_X >= m_CarLlX && point.m_X < m_CarUrX && point.m_Y < m_CarUrY && point.m_Y >= m_CarLlY; } /// /// Accessors. /// inline size_t RasWidth() const { return m_RasWidth; } inline size_t RasHeight() const { return m_RasHeight; } inline T OneRow() const { return m_OneRow; } inline T OneCol() const { return m_OneCol; } inline T PixPerImageUnitW() const { return m_PixPerImageUnitW; } inline T RasLlX() const { return m_RasLlX; } inline T PixPerImageUnitH() const { return m_PixPerImageUnitH; } inline T RasLlY() const { return m_RasLlY; } inline T CarLlX() const { return m_CarLlX; } inline T CarLlY() const { return m_CarLlY; } inline T CarUrX() const { return m_CarUrX; } inline T CarUrY() const { return m_CarUrY; } inline T PadCarLlX() const { return m_PadCarLlX; } inline T PadCarLlY() const { return m_PadCarLlY; } inline T PadCarUrX() const { return m_PadCarUrX; } inline T PadCarUrY() const { return m_PadCarUrY; } inline T CarHalfX() const { return m_CarHalfX; } inline T CarHalfY() const { return m_CarHalfY; } inline T CarCenterX() const { return m_CarCenterX; } inline T CarCenterY() const { return m_CarCenterY; } private: size_t m_RasWidth, m_RasHeight;//The width and height of the raster image. T m_OneRow;//The distance that one raster row represents in the cartesian plane. T m_OneCol;//The distance that one raster column represents in the cartesian plane. T m_PixPerImageUnitW;//The number of columns in the raster plane that a horizontal distance of 1 in the cartesian plane represents. The higher the number, the more zoomed in. T m_RasLlX;//The lower left x of the raster image plane. T m_PixPerImageUnitH;//The number of rows in the raster plane that a vertical distance of 1 in the cartesian plane represents. The higher the number, the more zoomed in. T m_RasLlY;//The lower left y of the raster image plane. T m_CarLlX, m_CarLlY, m_CarUrX, m_CarUrY;//The bounds of the cartesian plane. T m_PadCarLlX, m_PadCarLlY, m_PadCarUrX, m_PadCarUrY;//The bounds of the cartesian plane padded by one raster row and column on each side. T m_CarHalfX, m_CarHalfY;//The distance from the center of the of the cartesian plane to the edges. T m_CarCenterX, m_CarCenterY;//The center of the cartesian plane. }; }