|
|
|
@ -13,20 +13,92 @@ |
|
|
|
|
/*
|
|
|
|
|
* # What is This Shit? |
|
|
|
|
* |
|
|
|
|
* Announcing the Shape Iterators, or `shiterators` for short. You could also say these are Shaw's Iterators, but |
|
|
|
|
* either way they are the _shit_. Or are they shit? You decide. Maybe they're "shite"? |
|
|
|
|
* Announcing the Shape Iterators, or "shiterators" for short. The best shite |
|
|
|
|
* for C++ for-loops since that [one youtube |
|
|
|
|
* video](https://www.youtube.com/watch?v=rX0ItVEVjHc) told everyone to
|
|
|
|
|
* recreate SQL databases with structs. You could also say these are Shaw's |
|
|
|
|
* Iterators, but either way they are the _shite_. Or are they shit? You decide. |
|
|
|
|
* Maybe they're "shite"? |
|
|
|
|
* |
|
|
|
|
* A shiterator is a simple generator that converts 2D shapes into a 1D stream of x/y coordinates. You give it a matrix, some parameters like start, end, etc. and each time you call `next()` you the next viable x/y coordinate to complete the shape. |
|
|
|
|
* A shiterator is a simple generator that converts 2D shapes into a 1D stream |
|
|
|
|
* of x/y coordinates. You give it a matrix, some parameters like start, end, |
|
|
|
|
* etc. and each time you call `next()` you get the next viable x/y coordinate to |
|
|
|
|
* complete the shape. This makes them far superior to _any_ existing for-loop |
|
|
|
|
* technology because shiterators operate _intelligently_ in shapes. Other |
|
|
|
|
* [programming pundits](https://www.youtube.com/watch?v=tD5NrevFtbU) will say
|
|
|
|
|
* their 7000 line "easy to maintain" switch statements are better at drawing |
|
|
|
|
* shapes, but they're wrong. My way of making a for-loop do stuff is vastly |
|
|
|
|
* superior because it doesn't use a switch _or_ a virtual function _or_ |
|
|
|
|
* inheritance at all. That means they have to be the _fastest_. Feel free to run |
|
|
|
|
* them 1000 times and bask in the glory of 1 nanosecond difference performance. |
|
|
|
|
* |
|
|
|
|
* A shiterator tries to ensure a few things: |
|
|
|
|
* It's science and shite. |
|
|
|
|
* |
|
|
|
|
* 1. All x/y values will be within the Matrix you give it. |
|
|
|
|
* More importantly, shiterators are simple and easy to use. They're so easy to |
|
|
|
|
* use you _don't even use the 3rd part of the for-loop_. What? You read that right, |
|
|
|
|
* not only have I managed to eliminate _both_ massive horrible to maintain switches, |
|
|
|
|
* and also avoided virtual functions, but I've also _eliminated one entire part |
|
|
|
|
* of the for-loop_. This obviously makes them way faster than other inferior |
|
|
|
|
* three-clause-loop-trash. Just look at this comparison: |
|
|
|
|
* |
|
|
|
|
* ```cpp |
|
|
|
|
* for(it = trash.begin(); it != trash.end(); trash++) { |
|
|
|
|
* std::cout << it << std::endl; |
|
|
|
|
* } |
|
|
|
|
* ``` |
|
|
|
|
* |
|
|
|
|
* ```cpp |
|
|
|
|
* for(each_cell it{mat}; it.next();) { |
|
|
|
|
* std::cout << mat[it.y][it.x] << std::endl; |
|
|
|
|
* } |
|
|
|
|
* ``` |
|
|
|
|
* |
|
|
|
|
* Obviously this will outperform _any_ iterator invented in the last 30 years, but the best |
|
|
|
|
* thing about shiterators is their composability and ability to work simultaneously across |
|
|
|
|
* multiple matrices in one loop: |
|
|
|
|
* |
|
|
|
|
* ```cpp |
|
|
|
|
* for(line it{start, end}; it.next();) { |
|
|
|
|
* for(compass neighbor{walls, it.x, it.y}; neighbor.next();) { |
|
|
|
|
* if(walls[neighbor.y][neighbor.x] == 1) { |
|
|
|
|
* wall_update[it.y][it.x] = walls[it.y][it.x] + 10; |
|
|
|
|
* } |
|
|
|
|
* } |
|
|
|
|
* } |
|
|
|
|
* ``` |
|
|
|
|
* |
|
|
|
|
* This code sample (maybe, because I didn't run it) draws a line from |
|
|
|
|
* `start` to `end` then looks at each neighbor on a compass (north, south, east, west) |
|
|
|
|
* at each point to see if it's set to 1. If it is then it copies that cell over to |
|
|
|
|
* another matrix with +10. Why would you need this? Your Wizard just shot a fireball |
|
|
|
|
* down a corridor and you need to see if anything in the path is within 1 square of it. |
|
|
|
|
* |
|
|
|
|
* You _also_ don't even need to use a for-loop. Yes, you can harken back to the old |
|
|
|
|
* days when we did everything RAW inside a Duff's Device between a while-loop for |
|
|
|
|
* that PERFORMANCE because who cares about maintenance? You're a game developer! Tests? |
|
|
|
|
* Don't need a test if it runs fine on Sony Playstation only. Maintenance? You're moving |
|
|
|
|
* on to the next project in two weeks anyway right?! Use that while-loop and a shiterator |
|
|
|
|
* to really help that next guy: |
|
|
|
|
* |
|
|
|
|
* ```cpp |
|
|
|
|
* box it{walls, center_x, center_y, 20}; |
|
|
|
|
* while(it.next()) { |
|
|
|
|
* walls[it.y][it.x] = 1; |
|
|
|
|
* } |
|
|
|
|
* ``` |
|
|
|
|
* |
|
|
|
|
* ## Shiterator "Guarantees" |
|
|
|
|
* |
|
|
|
|
* Just like Rust [guarantees no memory leaks](https://github.com/pop-os/cosmic-comp/issues/1133),
|
|
|
|
|
* a shiterator tries to ensure a few things, if it can: |
|
|
|
|
* |
|
|
|
|
* 1. All x/y values will be within the Matrix you give it. The `line` shiterator doesn't though. |
|
|
|
|
* 2. They try to not store anything and only calculate the math necessary to linearlize the shape. |
|
|
|
|
* 3. You can store them and incrementally call next to get the next value. |
|
|
|
|
* 4. You should be able to compose them together on the same Matrix or different matrices of the same dimensions. |
|
|
|
|
* 5. Most of them will only require 1 for-loop, the few that require 2 only do this so you can draw the inside of a shape. `circle` is like this. |
|
|
|
|
* 6. They don't assume any particular classes or require subclassing. As long as the type given enables `mat[y][x]` (row major) access then it'll work. |
|
|
|
|
* 7. The matrix given to a shiterator isn't actually attached to it, so you can use one matrix to setup an iterator, then apply the x/y values to any other matrix of the same dimensions. |
|
|
|
|
* 7. The matrix given to a shiterator isn't actually attached to it, so you can use one matrix to setup an iterator, then apply the x/y values to any other matrix of the same dimensions. Great for smart copying and transforming. |
|
|
|
|
* 8. More importantly, shiterators _do not return any values from the matrix_. They only do the math for coordinates and leave it to you to work your matrix. |
|
|
|
|
* |
|
|
|
|
* These shiterators are used all over the game to do map rendering, randomization, drawing, nearly everything that involves a shape. |
|
|
|
@ -47,11 +119,11 @@ |
|
|
|
|
* |
|
|
|
|
* ## Thanks |
|
|
|
|
* |
|
|
|
|
* Special thanks for Amit and hirdrac for their help with the math and for |
|
|
|
|
* Special thanks to Amit and hirdrac for their help with the math and for |
|
|
|
|
* giving me the initial idea. hirdrac doesn't want to be held responsible for |
|
|
|
|
* this travesty but he showed me that you can do iteration and _not_ use the |
|
|
|
|
* weird C++ iterators. Amit did a lot to show me how to do these calculations |
|
|
|
|
* without branching. Thanks to you both and everyone helping me while I |
|
|
|
|
* without branching. Thanks to you both--and to everyone else--for helping me while I |
|
|
|
|
* stream my development. |
|
|
|
|
*/ |
|
|
|
|
namespace shiterator { using std::vector, std::queue, std::array; using |
|
|
|
|