Skip to content

Instantly share code, notes, and snippets.

@Const-me Const-me/Grid.hpp
Created Jul 3, 2019

Embed
What would you like to do?
#pragma once
#include <assert.h>
#include <vector>
#include <algorithm>
// 2D vector of integers
struct Vector2i
{
int x, y;
Vector2i() = default;
Vector2i( int _x, int _y ) : x( _x ), y( _y ) { }
Vector2i operator+( const Vector2i that ) const
{
// C++ books say "pass by const reference", but on modern systems small structures normally passed in registers, faster.
return Vector2i{ x + that.x, y + that.y };
}
void operator+=( const Vector2i that )
{
x += that.x;
y += that.y;
// C++ books say "return *this", but I don't think code like `a += b += c` should compile. Deliberately making it fail.
}
// Implement whatever operators you need for them
};
// 2D grid of items, where items are kept in continuous block of RAM.
// For very small items like bytes, you'd probably want line padding. For very large ones, you might want vector of vectors, one per line. Both are performance optimizations, you might not need either of them, ever.
template<class T>
class Grid
{
Vector2i size;
std::vector<T> data;
size_t getIndex( const Vector2i pos ) const
{
assert( pos.x >= 0 && pos.y >= 0 && pos.x < size.x && pos.y < size.y );
return (size_t)pos.y * (size_t)size.x + (size_t)pos.x;
}
public:
Grid()
{
size.x = size.y = 0;
}
void resize( const Vector2i newSize )
{
assert( newSize.x >= 0 && newSize.y >= 0 );
const size_t newLength = (size_t)newSize.x * (size_t)newSize.y;
data.resize( newLength );
size = newSize;
}
bool empty() const
{
return data.empty();
}
void clear()
{
data.clear();
size.x = size.y = 0;
}
Vector2i getSize() const { return size; }
// Access an item by 2D element index
T& operator[] ( const Vector2i pos )
{
return data[ getIndex( pos ) ];
}
// Access an item by 2D element index, readonly version
const T& operator[] ( const Vector2i pos ) const
{
return data[ getIndex( pos ) ];
}
// Set the complete grid to the same value
void fillGrid( const T& value )
{
std::fill( data.begin(), data.end(), value );
}
// Set a complete horizontal line to the same value
void fillScanline( int y, const T& value )
{
assert( y >= 0 && y < size.y );
auto iter = data.begin() + (size_t)y * (size_t)size.x;
std::fill( iter, iter + size.x, value );
}
};
@sdegutis

This comment has been minimized.

Copy link

sdegutis commented Jul 3, 2019

@Const-me Thanks for posting this. Do you have a quick example of how to use these APIs? Especially I'm pretty sure there's some convenience syntax to use Vector2i within the context of Grid, I just couldn't guess it or know what to call it when searching online for it.

@Const-me

This comment has been minimized.

Copy link
Owner Author

Const-me commented Jul 3, 2019

@sdegutis Something like this:

// Construct a grid holding integer in each cell. Initially will be empty, i.e. size { 0, 0 }
Grid<int> grid;

// Resize the grid to be 10 (width) x 20 (height), the initial data is undefined for integers.
// If you'll use a custom class instead of int, resize() will call default = parameterless constructors, of all 10*20=200 instances.
grid.resize( Vector2i{ 10, 20 } );

// Set all elements to 0xCC
grid.fillGrid( 0xCC );

// Set an element at x = 1 y = 11 to be 13
grid[ Vector2i{ 1, 11 } ] = 13;

// If you'll use things like grid[ Vector2i{ a, b } ] a lot, might be a good idea to expose following API in the Grid template class:
// T& getAt( int x, int y )
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.