Skip to content

Instantly share code, notes, and snippets.

@Const-me
Created July 3, 2019 16:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Const-me/7aee0ef4087d250cc9b82463988ecafc to your computer and use it in GitHub Desktop.
Save Const-me/7aee0ef4087d250cc9b82463988ecafc to your computer and use it in GitHub Desktop.
#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
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
Copy link
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