MarchingSquares.hpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. #ifndef MARCHINGSQUARES_HPP
  2. #define MARCHINGSQUARES_HPP
  3. #include <type_traits>
  4. #include <cstdint>
  5. #include <vector>
  6. #include <algorithm>
  7. #include <cassert>
  8. namespace marchsq {
  9. // Marks a square in the grid
  10. struct Coord {
  11. long r = 0, c = 0;
  12. Coord() = default;
  13. explicit Coord(long s) : r(s), c(s) {}
  14. Coord(long _r, long _c): r(_r), c(_c) {}
  15. size_t seq(const Coord &res) const { return r * res.c + c; }
  16. Coord& operator+=(const Coord& b) { r += b.r; c += b.c; return *this; }
  17. Coord operator+(const Coord& b) const { Coord a = *this; a += b; return a; }
  18. };
  19. // Closed ring of cell coordinates
  20. using Ring = std::vector<Coord>;
  21. // Specialize this struct to register a raster type for the Marching squares alg
  22. template<class T, class Enable = void> struct _RasterTraits {
  23. // The type of pixel cell in the raster
  24. using ValueType = typename T::ValueType;
  25. // Value at a given position
  26. static ValueType get(const T &raster, size_t row, size_t col);
  27. // Number of rows and cols of the raster
  28. static size_t rows(const T &raster);
  29. static size_t cols(const T &raster);
  30. };
  31. // Specialize this to use parellel loops within the algorithm
  32. template<class ExecutionPolicy, class Enable = void> struct _Loop {
  33. template<class It, class Fn> static void for_each(It from, It to, Fn &&fn)
  34. {
  35. for (auto it = from; it < to; ++it) fn(*it, size_t(it - from));
  36. }
  37. };
  38. namespace __impl {
  39. template<class T> using RasterTraits = _RasterTraits<std::decay_t<T>>;
  40. template<class T> using TRasterValue = typename RasterTraits<T>::ValueType;
  41. template<class T> size_t rows(const T &raster)
  42. {
  43. return RasterTraits<T>::rows(raster);
  44. }
  45. template<class T> size_t cols(const T &raster)
  46. {
  47. return RasterTraits<T>::cols(raster);
  48. }
  49. template<class T> TRasterValue<T> isoval(const T &rst, const Coord &crd)
  50. {
  51. return RasterTraits<T>::get(rst, crd.r, crd.c);
  52. }
  53. template<class ExecutionPolicy, class It, class Fn>
  54. void for_each(ExecutionPolicy&& policy, It from, It to, Fn &&fn)
  55. {
  56. _Loop<ExecutionPolicy>::for_each(from, to, fn);
  57. }
  58. // Type of squares (tiles) depending on which vertices are inside an ROI
  59. // The vertices would be marked a, b, c, d in counter clockwise order from the
  60. // bottom left vertex of a square.
  61. // d --- c
  62. // | |
  63. // | |
  64. // a --- b
  65. enum class SquareTag : uint8_t {
  66. // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
  67. none, a, b, ab, c, ac, bc, abc, d, ad, bd, abd, cd, acd, bcd, full
  68. };
  69. template<class E> constexpr std::underlying_type_t<E> _t(E e) noexcept
  70. {
  71. return static_cast<std::underlying_type_t<E>>(e);
  72. }
  73. enum class Dir: uint8_t { left, down, right, up, none};
  74. static const constexpr Dir NEXT_CCW[] = {
  75. /* 00 */ Dir::none, // SquareTag::none (empty square, nowhere to go)
  76. /* 01 */ Dir::left, // SquareTag::a
  77. /* 02 */ Dir::down, // SquareTag::b
  78. /* 03 */ Dir::left, // SquareTag::ab
  79. /* 04 */ Dir::right, // SquareTag::c
  80. /* 05 */ Dir::none, // SquareTag::ac (ambiguous case)
  81. /* 06 */ Dir::down, // SquareTag::bc
  82. /* 07 */ Dir::left, // SquareTag::abc
  83. /* 08 */ Dir::up, // SquareTag::d
  84. /* 09 */ Dir::up, // SquareTag::ad
  85. /* 10 */ Dir::none, // SquareTag::bd (ambiguous case)
  86. /* 11 */ Dir::up, // SquareTag::abd
  87. /* 12 */ Dir::right, // SquareTag::cd
  88. /* 13 */ Dir::right, // SquareTag::acd
  89. /* 14 */ Dir::down, // SquareTag::bcd
  90. /* 15 */ Dir::none // SquareTag::full (full covered, nowhere to go)
  91. };
  92. static const constexpr uint8_t PREV_CCW[] = {
  93. /* 00 */ 1 << _t(Dir::none),
  94. /* 01 */ 1 << _t(Dir::up),
  95. /* 02 */ 1 << _t(Dir::left),
  96. /* 03 */ 1 << _t(Dir::left),
  97. /* 04 */ 1 << _t(Dir::down),
  98. /* 05 */ 1 << _t(Dir::up) | 1 << _t(Dir::down),
  99. /* 06 */ 1 << _t(Dir::down),
  100. /* 07 */ 1 << _t(Dir::down),
  101. /* 08 */ 1 << _t(Dir::right),
  102. /* 09 */ 1 << _t(Dir::up),
  103. /* 10 */ 1 << _t(Dir::left) | 1 << _t(Dir::right),
  104. /* 11 */ 1 << _t(Dir::left),
  105. /* 12 */ 1 << _t(Dir::right),
  106. /* 13 */ 1 << _t(Dir::up),
  107. /* 14 */ 1 << _t(Dir::right),
  108. /* 15 */ 1 << _t(Dir::none)
  109. };
  110. const constexpr uint8_t DIRMASKS[] = {
  111. /*left: */ 0x01, /*down*/ 0x12, /*right */0x21, /*up*/ 0x10, /*none*/ 0x00
  112. };
  113. inline Coord step(const Coord &crd, Dir d)
  114. {
  115. uint8_t dd = DIRMASKS[uint8_t(d)];
  116. return {crd.r - 1 + (dd & 0x0f), crd.c - 1 + (dd >> 4)};
  117. }
  118. template<class Rst> class Grid {
  119. const Rst * m_rst = nullptr;
  120. Coord m_cellsize, m_res_1, m_window, m_gridsize, m_grid_1;
  121. std::vector<uint8_t> m_tags; // Assign tags to each square
  122. Coord rastercoord(const Coord &crd) const
  123. {
  124. return {(crd.r - 1) * m_window.r, (crd.c - 1) * m_window.c};
  125. }
  126. Coord bl(const Coord &crd) const { return tl(crd) + Coord{m_res_1.r, 0}; }
  127. Coord br(const Coord &crd) const { return tl(crd) + Coord{m_res_1.r, m_res_1.c}; }
  128. Coord tr(const Coord &crd) const { return tl(crd) + Coord{0, m_res_1.c}; }
  129. Coord tl(const Coord &crd) const { return rastercoord(crd); }
  130. bool is_within(const Coord &crd)
  131. {
  132. long R = rows(*m_rst), C = cols(*m_rst);
  133. return crd.r >= 0 && crd.r < R && crd.c >= 0 && crd.c < C;
  134. };
  135. // Calculate the tag for a cell (or square). The cell coordinates mark the
  136. // top left vertex of a square in the raster. v is the isovalue
  137. uint8_t get_tag_for_cell(const Coord &cell, TRasterValue<Rst> v)
  138. {
  139. Coord sqr[] = {bl(cell), br(cell), tr(cell), tl(cell)};
  140. uint8_t t = ((is_within(sqr[0]) && isoval(*m_rst, sqr[0]) >= v)) +
  141. ((is_within(sqr[1]) && isoval(*m_rst, sqr[1]) >= v) << 1) +
  142. ((is_within(sqr[2]) && isoval(*m_rst, sqr[2]) >= v) << 2) +
  143. ((is_within(sqr[3]) && isoval(*m_rst, sqr[3]) >= v) << 3);
  144. assert(t < 16);
  145. return t;
  146. }
  147. // Get a cell coordinate from a sequential index
  148. Coord coord(size_t i) const
  149. {
  150. return {long(i) / m_gridsize.c, long(i) % m_gridsize.c};
  151. }
  152. size_t seq(const Coord &crd) const { return crd.seq(m_gridsize); }
  153. bool is_visited(size_t idx, Dir d = Dir::none) const
  154. {
  155. SquareTag t = get_tag(idx);
  156. uint8_t ref = d == Dir::none ? PREV_CCW[_t(t)] : uint8_t(1 << _t(d));
  157. return t == SquareTag::full || t == SquareTag::none ||
  158. ((m_tags[idx] & 0xf0) >> 4) == ref;
  159. }
  160. void set_visited(size_t idx, Dir d = Dir::none)
  161. {
  162. m_tags[idx] |= (1 << (_t(d)) << 4);
  163. }
  164. bool is_ambiguous(size_t idx) const
  165. {
  166. SquareTag t = get_tag(idx);
  167. return t == SquareTag::ac || t == SquareTag::bd;
  168. }
  169. // Search for a new starting square
  170. size_t search_start_cell(size_t i = 0) const
  171. {
  172. // Skip ambiguous tags as starting tags due to unknown previous
  173. // direction.
  174. while ((i < m_tags.size()) && (is_visited(i) || is_ambiguous(i))) ++i;
  175. return i;
  176. }
  177. SquareTag get_tag(size_t idx) const { return SquareTag(m_tags[idx] & 0x0f); }
  178. Dir next_dir(Dir prev, SquareTag tag) const
  179. {
  180. // Treat ambiguous cases as two separate regions in one square.
  181. switch (tag) {
  182. case SquareTag::ac:
  183. switch (prev) {
  184. case Dir::down: return Dir::right;
  185. case Dir::up: return Dir::left;
  186. default: assert(false); return Dir::none;
  187. }
  188. case SquareTag::bd:
  189. switch (prev) {
  190. case Dir::right: return Dir::up;
  191. case Dir::left: return Dir::down;
  192. default: assert(false); return Dir::none;
  193. }
  194. default:
  195. return NEXT_CCW[uint8_t(tag)];
  196. }
  197. return Dir::none;
  198. }
  199. struct CellIt {
  200. Coord crd; Dir dir= Dir::none; const Rst *grid = nullptr;
  201. TRasterValue<Rst> operator*() const { return isoval(*grid, crd); }
  202. CellIt& operator++() { crd = step(crd, dir); return *this; }
  203. CellIt operator++(int) { CellIt it = *this; ++(*this); return it; }
  204. bool operator!=(const CellIt &it) { return crd.r != it.crd.r || crd.c != it.crd.c; }
  205. using value_type = TRasterValue<Rst>;
  206. using pointer = TRasterValue<Rst> *;
  207. using reference = TRasterValue<Rst> &;
  208. using difference_type = long;
  209. using iterator_category = std::forward_iterator_tag;
  210. };
  211. // Two cell iterators representing an edge of a square. This is then
  212. // used for binary search for the first active pixel on the edge.
  213. struct Edge { CellIt from, to; };
  214. Edge _edge(const Coord &ringvertex) const
  215. {
  216. size_t idx = ringvertex.r;
  217. Coord cell = coord(idx);
  218. uint8_t tg = m_tags[ringvertex.r];
  219. SquareTag t = SquareTag(tg & 0x0f);
  220. switch (t) {
  221. case SquareTag::a:
  222. case SquareTag::ab:
  223. case SquareTag::abc:
  224. return {{tl(cell), Dir::down, m_rst}, {bl(cell)}};
  225. case SquareTag::b:
  226. case SquareTag::bc:
  227. case SquareTag::bcd:
  228. return {{bl(cell), Dir::right, m_rst}, {br(cell)}};
  229. case SquareTag::c:
  230. return {{br(cell), Dir::up, m_rst}, {tr(cell)}};
  231. case SquareTag::ac:
  232. switch (Dir(ringvertex.c)) {
  233. case Dir::left: return {{tl(cell), Dir::down, m_rst}, {bl(cell)}};
  234. case Dir::right: return {{br(cell), Dir::up, m_rst}, {tr(cell)}};
  235. default: assert(false);
  236. }
  237. case SquareTag::d:
  238. case SquareTag::ad:
  239. case SquareTag::abd:
  240. return {{tr(cell), Dir::left, m_rst}, {tl(cell)}};
  241. case SquareTag::bd:
  242. switch (Dir(ringvertex.c)) {
  243. case Dir::down: return {{bl(cell), Dir::right, m_rst}, {br(cell)}};
  244. case Dir::up: return {{tr(cell), Dir::left, m_rst}, {tl(cell)}};
  245. default: assert(false);
  246. }
  247. case SquareTag::cd:
  248. case SquareTag::acd:
  249. return {{br(cell), Dir::up, m_rst}, {tr(cell)}};
  250. case SquareTag::full:
  251. case SquareTag::none: {
  252. Coord crd{tl(cell) + Coord{m_cellsize.r / 2, m_cellsize.c / 2}};
  253. return {{crd, Dir::none, m_rst}, crd};
  254. }
  255. }
  256. return {};
  257. }
  258. Edge edge(const Coord &ringvertex) const
  259. {
  260. const long R = rows(*m_rst), C = cols(*m_rst);
  261. const long R_1 = R - 1, C_1 = C - 1;
  262. Edge e = _edge(ringvertex);
  263. e.to.dir = e.from.dir;
  264. ++e.to;
  265. e.from.crd.r = std::min(e.from.crd.r, R_1);
  266. e.from.crd.r = std::max(e.from.crd.r, 0l);
  267. e.from.crd.c = std::min(e.from.crd.c, C_1);
  268. e.from.crd.c = std::max(e.from.crd.c, 0l);
  269. e.to.crd.r = std::min(e.to.crd.r, R);
  270. e.to.crd.r = std::max(e.to.crd.r, 0l);
  271. e.to.crd.c = std::min(e.to.crd.c, C);
  272. e.to.crd.c = std::max(e.to.crd.c, 0l);
  273. return e;
  274. }
  275. public:
  276. explicit Grid(const Rst &rst, const Coord &cellsz, const Coord &overlap)
  277. : m_rst{&rst}
  278. , m_cellsize{cellsz}
  279. , m_res_1{m_cellsize.r - 1, m_cellsize.c - 1}
  280. , m_window{overlap.r < cellsz.r ? cellsz.r - overlap.r : cellsz.r,
  281. overlap.c < cellsz.c ? cellsz.c - overlap.c : cellsz.c}
  282. , m_gridsize{2 + (long(rows(rst)) - overlap.r) / m_window.r,
  283. 2 + (long(cols(rst)) - overlap.c) / m_window.c}
  284. , m_tags(m_gridsize.r * m_gridsize.c, 0)
  285. {}
  286. // Go through the cells and mark them with the appropriate tag.
  287. template<class ExecutionPolicy>
  288. void tag_grid(ExecutionPolicy &&policy, TRasterValue<Rst> isoval)
  289. {
  290. // parallel for r
  291. for_each (std::forward<ExecutionPolicy>(policy),
  292. m_tags.begin(), m_tags.end(),
  293. [this, isoval](uint8_t& tag, size_t idx) {
  294. tag = get_tag_for_cell(coord(idx), isoval);
  295. });
  296. }
  297. // Scan for the rings on the tagged grid. Each ring vertex stores the
  298. // sequential index of the cell and the next direction (Dir).
  299. // This info can be used later to calculate the exact raster coordinate.
  300. std::vector<Ring> scan_rings()
  301. {
  302. std::vector<Ring> rings;
  303. size_t startidx = 0;
  304. while ((startidx = search_start_cell(startidx)) < m_tags.size()) {
  305. Ring ring;
  306. size_t idx = startidx;
  307. Dir prev = Dir::none, next = next_dir(prev, get_tag(idx));
  308. while (next != Dir::none && !is_visited(idx, prev)) {
  309. Coord ringvertex{long(idx), long(next)};
  310. ring.emplace_back(ringvertex);
  311. set_visited(idx, prev);
  312. idx = seq(step(coord(idx), next));
  313. prev = next;
  314. next = next_dir(next, get_tag(idx));
  315. }
  316. // To prevent infinite loops in case of degenerate input
  317. if (next == Dir::none) m_tags[startidx] = _t(SquareTag::none);
  318. if (ring.size() > 1) {
  319. ring.pop_back();
  320. rings.emplace_back(ring);
  321. }
  322. }
  323. return rings;
  324. }
  325. // Calculate the exact raster position from the cells which store the
  326. // sequantial index of the square and the next direction
  327. template<class ExecutionPolicy>
  328. void interpolate_rings(ExecutionPolicy && policy,
  329. std::vector<Ring> &rings,
  330. TRasterValue<Rst> isov)
  331. {
  332. for_each(std::forward<ExecutionPolicy>(policy),
  333. rings.begin(), rings.end(), [this, isov] (Ring &ring, size_t)
  334. {
  335. for (Coord &ringvertex : ring) {
  336. Edge e = edge(ringvertex);
  337. CellIt found = std::lower_bound(e.from, e.to, isov);
  338. ringvertex = found.crd;
  339. }
  340. });
  341. }
  342. };
  343. template<class Raster, class ExecutionPolicy>
  344. std::vector<marchsq::Ring> execute_with_policy(ExecutionPolicy && policy,
  345. const Raster & raster,
  346. TRasterValue<Raster> isoval,
  347. Coord windowsize = {})
  348. {
  349. if (!rows(raster) || !cols(raster)) return {};
  350. size_t ratio = cols(raster) / rows(raster);
  351. if (!windowsize.r) windowsize.r = 2;
  352. if (!windowsize.c)
  353. windowsize.c = std::max(2l, long(windowsize.r * ratio));
  354. Coord overlap{1};
  355. Grid<Raster> grid{raster, windowsize, overlap};
  356. grid.tag_grid(std::forward<ExecutionPolicy>(policy), isoval);
  357. std::vector<marchsq::Ring> rings = grid.scan_rings();
  358. grid.interpolate_rings(std::forward<ExecutionPolicy>(policy), rings, isoval);
  359. return rings;
  360. }
  361. template<class Raster>
  362. std::vector<marchsq::Ring> execute(const Raster &raster,
  363. TRasterValue<Raster> isoval,
  364. Coord windowsize = {})
  365. {
  366. return execute_with_policy(nullptr, raster, isoval, windowsize);
  367. }
  368. } // namespace __impl
  369. using __impl::execute_with_policy;
  370. using __impl::execute;
  371. } // namespace marchsq
  372. #endif // MARCHINGSQUARES_HPP