607 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			607 lines
		
	
	
	
		
			18 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #pragma once
 | |
| #include <vector>
 | |
| #include <queue>
 | |
| #include <string>
 | |
| #include <array>
 | |
| #include <numeric>
 | |
| #include <algorithm>
 | |
| #include <fmt/core.h>
 | |
| #include "point.hpp"
 | |
| #include "rand.hpp"
 | |
| #include "dbc.hpp"
 | |
| 
 | |
| /*
 | |
|  * # What is This Shit?
 | |
|  *
 | |
|  * 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 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.
 | |
|  *
 | |
|  * It's science and shite.
 | |
|  *
 | |
|  * 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(); it++) {
 | |
|  *     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. 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.
 | |
|  *
 | |
|  * ## Algorithms I Need
 | |
|  *
 | |
|  * I'm currently looking for a few algorithms, so if you know how to do these let me know:
 | |
|  *
 | |
|  * 1. _Flood fill_  This turns out to be really hard because most algorithms require keeping track of visited cells with a queue, recursion, etc.
 | |
|  * 2. _Random rectangle fill_  I have something that mostly works but it's really only random across each y-axis, then separate y-axes are randomized.
 | |
|  * 3. _Dijkstra Map_ I have a Dijkstra algorithm but it's not in this style yet.  Look in `worldbuilder.cpp` for my current implementation.
 | |
|  * 4. _Viewport_ Currently working on this but I need to have a rectangle I can move around as a viewport.
 | |
|  *
 | |
|  *
 | |
|  * ## Usage
 | |
|  *
 | |
|  * Check the `matrix.hpp` for an example if you want to make it more conventient for your own type.
 | |
|  *
 | |
|  * ## Thanks
 | |
|  *
 | |
|  * 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 to everyone else--for helping me while I
 | |
|  * stream my development.
 | |
|  *
 | |
|  * ### SERIOUS DISCLAIMER
 | |
|  *
 | |
|  * I am horribly bad at trigonometry and graphics algorithms, so if you've got an idea to improve them
 | |
|  * or find a bug shoot me an email at help@learncodethehardway.com.
 | |
|  */
 | |
| namespace shiterator { using std::vector, std::queue, std::array; using
 | |
|   std::min, std::max, std::floor;
 | |
| 
 | |
|   template<typename T>
 | |
|   using BaseRow = vector<T>;
 | |
| 
 | |
|   template<typename T>
 | |
|   using Base = vector<BaseRow<T>>;
 | |
| 
 | |
|   template<typename T>
 | |
|   inline Base<T> make(size_t width, size_t height) {
 | |
|     Base<T> result(height, BaseRow<T>(width));
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * Just a quick thing to reset a matrix to a value.
 | |
|    */
 | |
|   template<typename MAT, typename VAL>
 | |
|   inline void assign(MAT &out, VAL new_value) {
 | |
|     for(auto &row : out) {
 | |
|       row.assign(row.size(), new_value);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /*
 | |
|    * Tells you if a coordinate is in bounds of the matrix
 | |
|    * and therefore safe to use.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   inline bool inbounds(MAT &mat, size_t x, size_t y) {
 | |
|     // since Point.x and Point.y are size_t any negatives are massive
 | |
|     return (y < mat.size()) && (x < mat[0].size());
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * Gives the width of a matrix. Assumes row major (y/x)
 | |
|    * and vector API .size().
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   inline size_t width(MAT &mat) {
 | |
|     return mat[0].size();
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * Same as shiterator::width but just the height.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   inline size_t height(MAT &mat) {
 | |
|     return mat.size();
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * These are internal calculations that help
 | |
|    * with keeping track of the next x coordinate.
 | |
|    */
 | |
|   inline size_t next_x(size_t x, size_t width) {
 | |
|     return (x + 1) * ((x + 1) < width);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * Same as next_x but updates the next y coordinate.
 | |
|    * It uses the fact that when x==0 you have a new
 | |
|    * line so increment y.
 | |
|    */
 | |
|   inline size_t next_y(size_t x, size_t y) {
 | |
|     return y + (x == 0);
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * Figures out if you're at the end of the shape,
 | |
|    * which is usually when y > height.
 | |
|    */
 | |
|   inline bool at_end(size_t y, size_t height) {
 | |
|     return y < height;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * Determines if you're at the end of a row.
 | |
|    */
 | |
|   inline bool end_row(size_t x, size_t width) {
 | |
|     return x == width - 1;
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|    * Most basic shiterator. It just goes through
 | |
|    * every cell in the matrix in linear order
 | |
|    * with not tracking of anything else.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   struct each_cell_t {
 | |
|     size_t x = ~0;
 | |
|     size_t y = ~0;
 | |
|     size_t width = 0;
 | |
|     size_t height = 0;
 | |
| 
 | |
|     each_cell_t(MAT &mat)
 | |
|     {
 | |
|       height = shiterator::height(mat);
 | |
|       width = shiterator::width(mat);
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       x = next_x(x, width);
 | |
|       y = next_y(x, y);
 | |
|       return at_end(y, height);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /*
 | |
|    * This is just each_cell_t but it sets
 | |
|    * a boolean value `bool row` so you can
 | |
|    * tell when you've reached the end of a
 | |
|    * row.  This is mostly used for printing
 | |
|    * out a matrix and similar just drawing the
 | |
|    * whole thing with its boundaries.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   struct each_row_t {
 | |
|     size_t x = ~0;
 | |
|     size_t y = ~0;
 | |
|     size_t width = 0;
 | |
|     size_t height = 0;
 | |
|     bool row = false;
 | |
| 
 | |
|     each_row_t(MAT &mat) {
 | |
|       height = shiterator::height(mat);
 | |
|       width = shiterator::width(mat);
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       x = next_x(x, width);
 | |
|       y = next_y(x, y);
 | |
|       row = end_row(x, width);
 | |
|       return at_end(y, height);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /*
 | |
|    * This is a CENTERED box, that will create
 | |
|    * a centered rectangle around a point of a
 | |
|    * certain dimension. This kind of needs a
 | |
|    * rewrite but if you want a rectangle from
 | |
|    * a upper corner then use rectangle_t type.
 | |
|    *
 | |
|    * Passing 1 parameter for the size will make
 | |
|    * a square.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   struct box_t {
 | |
|     size_t from_x;
 | |
|     size_t from_y;
 | |
|     size_t x = 0; // these are set in constructor
 | |
|     size_t y = 0; // again, no fancy ~ trick needed
 | |
|     size_t left = 0;
 | |
|     size_t top = 0;
 | |
|     size_t right = 0;
 | |
|     size_t bottom = 0;
 | |
| 
 | |
|     box_t(MAT &mat, size_t at_x, size_t at_y, size_t size) :
 | |
|       box_t(mat, at_x, at_y, size, size) {
 | |
|       }
 | |
| 
 | |
|     box_t(MAT &mat, size_t at_x, size_t at_y, size_t width, size_t height) :
 | |
|       from_x(at_x), from_y(at_y)
 | |
|     {
 | |
|       size_t h = shiterator::height(mat);
 | |
|       size_t w = shiterator::width(mat);
 | |
| 
 | |
|       // keeps it from going below zero
 | |
|       // need extra -1 to compensate for the first next()
 | |
|       left = max(from_x, width) - width;
 | |
|       x = left - 1;  // must be -1 for next()
 | |
|       // keeps it from going above width
 | |
|       right = min(from_x + width + 1, w);
 | |
| 
 | |
|       // same for these two
 | |
|       top = max(from_y, height) - height;
 | |
|       y = top - (left == 0);
 | |
|       bottom = min(from_y + height + 1, h);
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       // calc next but allow to go to 0 for next
 | |
|       x = next_x(x, right);
 | |
|       // x will go to 0, which signals new line
 | |
|       y = next_y(x, y);  // this must go here
 | |
|       // if x==0 then this moves it to min_x
 | |
|       x = max(x, left);
 | |
|       // and done
 | |
| 
 | |
|       return at_end(y, bottom);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|      * This was useful for doing quick lighting
 | |
|      * calculations, and I might need to implement
 | |
|      * it in other shiterators. It gives the distance
 | |
|      * to the center from the current x/y.
 | |
|      */
 | |
|     float distance() {
 | |
|       int dx = from_x - x;
 | |
|       int dy = from_y - y;
 | |
| 
 | |
|       return sqrt((dx * dx) + (dy * dy));
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /*
 | |
|    * Stupid simple compass shape North/South/East/West.
 | |
|    * This comes up a _ton_ when doing searching, flood
 | |
|    * algorithms, collision, etc.  Probably not the
 | |
|    * fastest way to do it but good enough.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   struct compass_t {
 | |
|     size_t x = 0; // these are set in constructor
 | |
|     size_t y = 0; // again, no fancy ~ trick needed
 | |
|     array<int, 4> x_dirs{0, 1, 0, -1};
 | |
|     array<int, 4> y_dirs{-1, 0, 1, 0};
 | |
|     size_t max_dirs=0;
 | |
|     size_t dir = ~0;
 | |
| 
 | |
|     compass_t(MAT &mat, size_t x, size_t y) :
 | |
|       x(x), y(y)
 | |
|     {
 | |
|       array<int, 4> x_in{0, 1, 0, -1};
 | |
|       array<int, 4> y_in{-1, 0, 1, 0};
 | |
| 
 | |
|       for(size_t i = 0; i < 4; i++) {
 | |
|         int nx = x + x_in[i];
 | |
|         int ny = y + y_in[i];
 | |
|         if(shiterator::inbounds(mat, nx, ny)) {
 | |
|           x_dirs[max_dirs] = nx;
 | |
|           y_dirs[max_dirs] = ny;
 | |
|           max_dirs++;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       dir++;
 | |
|       if(dir < max_dirs) {
 | |
|         x = x_dirs[dir];
 | |
|         y = y_dirs[dir];
 | |
|         return true;
 | |
|       } else {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /*
 | |
|    * Draws a line from start to end using a algorithm from
 | |
|    * https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm
 | |
|    * No idea if the one I picked is best but it's the one
 | |
|    * that works in the shiterator requirements and produced
 | |
|    * good results.
 | |
|    *
 | |
|    * _WARNING_: This one doesn't check if the start/end are
 | |
|    * within your Matrix, as it's assumed _you_ did that
 | |
|    * already.
 | |
|    */
 | |
|   struct line {
 | |
|     int x;
 | |
|     int y;
 | |
|     int x1;
 | |
|     int y1;
 | |
|     int sx;
 | |
|     int sy;
 | |
|     int dx;
 | |
|     int dy;
 | |
|     int error;
 | |
| 
 | |
|     line(Point start, Point end) :
 | |
|         x(start.x), y(start.y),
 | |
|         x1(end.x), y1(end.y)
 | |
|     {
 | |
|       dx = std::abs(x1 - x);
 | |
|       sx = x < x1 ? 1 : -1;
 | |
|       dy = std::abs(y1 - y) * -1;
 | |
|       sy = y < y1 ? 1 : -1;
 | |
|       error = dx + dy;
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       if(x != x1 || y != y1) {
 | |
|         int e2 = 2 * error;
 | |
| 
 | |
|         if(e2 >= dy) {
 | |
|           error = error + dy;
 | |
|           x = x + sx;
 | |
|         }
 | |
| 
 | |
|         if(e2 <= dx) {
 | |
|           error = error + dx;
 | |
|           y = y + sy;
 | |
|         }
 | |
|         return true;
 | |
|       } else {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /*
 | |
|    * Draws a simple circle using a fairly naive algorithm
 | |
|    * but one that actually worked.  So, so, so, so many
 | |
|    * circle drawing algorithms described online don't work
 | |
|    * or are flat wrong.  Even the very best I could find
 | |
|    * did overdrawing of multiple lines or simply got the
 | |
|    * math wrong.  Keep in mind, _I_ am bad at this trig math
 | |
|    * so if I'm finding errors in your circle drawing then
 | |
|    * you got problems.
 | |
|    *
 | |
|    * This one is real simple, and works. If you got better
 | |
|    * then take the challenge but be ready to get it wrong.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   struct circle_t {
 | |
|     float center_x;
 | |
|     float center_y;
 | |
|     float radius = 0.0f;
 | |
|     int y = 0;
 | |
|     int dx = 0;
 | |
|     int dy = 0;
 | |
|     int left = 0;
 | |
|     int right = 0;
 | |
|     int top = 0;
 | |
|     int bottom = 0;
 | |
|     int width = 0;
 | |
|     int height = 0;
 | |
| 
 | |
|     circle_t(MAT &mat, Point center, float radius) :
 | |
|       center_x(center.x), center_y(center.y), radius(radius)
 | |
|     {
 | |
|       width = shiterator::width(mat);
 | |
|       height = shiterator::height(mat);
 | |
|       top = max(int(floor(center_y - radius)), 0);
 | |
|       bottom = min(int(floor(center_y + radius)), height - 1);
 | |
| 
 | |
|       y = top;
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       y++;
 | |
|       if(y <= bottom) {
 | |
|         dy = y - center_y;
 | |
|         dx = floor(sqrt(radius * radius - dy * dy));
 | |
|         left = max(0, int(center_x) - dx);
 | |
|         right = min(width, int(center_x) + dx + 1);
 | |
|         return true;
 | |
|       } else {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /*
 | |
|    * Basic rectangle shiterator, and like box and rando_rect_t you can
 | |
|    * pass only 1 parameter for size to do a square.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   struct rectangle_t {
 | |
|     int x;
 | |
|     int y;
 | |
|     int top;
 | |
|     int left;
 | |
|     int width;
 | |
|     int height;
 | |
|     int right;
 | |
|     int bottom;
 | |
| 
 | |
|     rectangle_t(MAT &mat, size_t start_x, size_t start_y, size_t size) :
 | |
|       rectangle_t(mat, start_x, start_y, size, size) {
 | |
|       }
 | |
| 
 | |
|     rectangle_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) :
 | |
|       top(start_y),
 | |
|       left(start_x),
 | |
|       width(width),
 | |
|       height(height)
 | |
|     {
 | |
|       size_t h = shiterator::height(mat);
 | |
|       size_t w = shiterator::width(mat);
 | |
|       y = start_y - 1;
 | |
|       x = left - 1;  // must be -1 for next()
 | |
|       right = min(start_x + width, w);
 | |
| 
 | |
|       y = start_y;
 | |
|       bottom = min(start_y + height, h);
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       x = next_x(x, right);
 | |
|       y = next_y(x, y);
 | |
|       x = max(x, left);
 | |
|       return at_end(y, bottom);
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /*
 | |
|    * WIP:  This one is used to place entities randomly but
 | |
|    * could be used for effects like random destruction of floors.
 | |
|    * It simply "wraps" the rectangle_t but randomizes the x/y values
 | |
|    * using a random starting point.  This makes it random across the
 | |
|    * x-axis but only partially random across the y.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   struct rando_rect_t {
 | |
|     int x;
 | |
|     int y;
 | |
|     int x_offset;
 | |
|     int y_offset;
 | |
|     rectangle_t<MAT> it;
 | |
| 
 | |
|     rando_rect_t(MAT &mat, size_t start_x, size_t start_y, size_t size) :
 | |
|       rando_rect_t(mat, start_x, start_y, size, size) {
 | |
|       }
 | |
| 
 | |
|     rando_rect_t(MAT &mat, size_t start_x, size_t start_y, size_t width, size_t height) :
 | |
|       it{mat, start_x, start_y, width, height}
 | |
|     {
 | |
|       x_offset = Random::uniform(0, int(width));
 | |
|       y_offset = Random::uniform(0, int(height));
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       bool done = it.next();
 | |
|       x = it.left + ((it.x + x_offset) % it.width);
 | |
|       y = it.top + ((it.y + y_offset) % it.height);
 | |
|       return done;
 | |
|     }
 | |
|   };
 | |
| 
 | |
|   /*
 | |
|    * BROKEN: I'm actually not sure what I'm trying to
 | |
|    * do here yet.
 | |
|    */
 | |
|   template<typename MAT>
 | |
|   struct viewport_t {
 | |
|     Point start;
 | |
|     // this is the point in the map
 | |
|     size_t x;
 | |
|     size_t y;
 | |
|     // this is the point inside the box, start at 0
 | |
|     size_t view_x = ~0;
 | |
|     size_t view_y = ~0;
 | |
|     // viewport width/height
 | |
|     size_t width;
 | |
|     size_t height;
 | |
| 
 | |
|     viewport_t(MAT &mat, Point start, int max_x, int max_y) :
 | |
|       start(start),
 | |
|       x(start.x-1),
 | |
|       y(start.y-1)
 | |
|     {
 | |
|       width = std::min(size_t(max_x), shiterator::width(mat) - start.x);
 | |
|       height = std::min(size_t(max_y), shiterator::height(mat) - start.y);
 | |
|       fmt::println("viewport_t max_x, max_y {},{} vs matrix {},{}, x={}, y={}",
 | |
|           max_x, max_y, shiterator::width(mat), shiterator::height(mat), x, y);
 | |
|     }
 | |
| 
 | |
|     bool next() {
 | |
|       y = next_y(x, y);
 | |
|       x = next_x(x, width);
 | |
|       view_x = next_x(view_x, width);
 | |
|       view_y = next_y(view_x, view_y);
 | |
|       return at_end(y, height);
 | |
|     }
 | |
|   };
 | |
| 
 | |
| }
 | 
