Gemmi C++ API
Loading...
Searching...
No Matches
floodfill.hpp
Go to the documentation of this file.
1// Copyright 2020 Global Phasing Ltd.
2//
3// The flood fill (scanline fill) algorithm for Grid.
4// Assumes periodic boundary conditions in the grid and 6-way connectivity.
5
6#ifndef GEMMI_FLOODFILL_HPP_
7#define GEMMI_FLOODFILL_HPP_
8
9#include <cstdint> // for int8_t
10#include "grid.hpp" // for Grid
11
12namespace gemmi {
13
14// Land is either 0 (when 1=sea, 0=land) or 1 (when 0=sea, 1=land)
15template<typename T, int Land>
16struct FloodFill {
18
19 struct Line {
20 int u, v, w, ulen;
22 };
23
24 struct Result {
25 std::vector<Line> lines;
26
27 size_t point_count() const {
28 size_t n = 0;
29 for (const Line& line : lines)
30 n += line.ulen;
31 return n;
32 }
33 };
34
35 static constexpr T this_island() { return T(Land|2); }
36
37 void set_line_values(Line& line, T value) const {
38 for (int i = 0; i < std::min(line.ulen, mask.nu - line.u); ++i) {
39 assert(line.ptr[i] != value);
40 line.ptr[i] = value;
41 }
42 for (int i = -line.u; i < line.ulen - mask.nu; ++i) {
43 assert(line.ptr[i] != value);
44 line.ptr[i] = value;
45 }
46 }
47
48 void set_volume_values(Result& r, T value) const {
49 for (Line& line : r.lines)
50 set_line_values(line, value);
51 }
52
53 // Find all connected points with value Land. Change them to this_island().
54 Result find_all_connected_points(int u, int v, int w) {
55 Result r;
56 T* ptr = &mask.data[mask.index_q(u, v, w)];
57 r.lines.push_back(line_from_point(u, v, w, ptr));
58 set_line_values(r.lines.back(), this_island());
59 for (size_t i = 0; i < r.lines.size()/*increasing!*/; ++i) {
60 int u_ = r.lines[i].u;
61 int v_ = r.lines[i].v;
62 int w_ = r.lines[i].w;
63 int ustart = u_ != 0 ? u_ - 1 : mask.nu - 1;
64 int ulen = std::min(mask.nu, r.lines[i].ulen + 2);
65 // add adjacent lines
66 int vl = v_ != 0 ? v_ - 1 : mask.nv - 1;
67 int vh = v_ + 1 != mask.nv ? v_ + 1 : 0;
68 int wl = w_ != 0 ? w_ - 1 : mask.nw - 1;
69 int wh = w_ + 1 != mask.nw ? w_ + 1 : 0;
70 add_lines(ustart, vl, wl, ulen, r);
71 add_lines(ustart, vl, w_, ulen, r);
72 add_lines(ustart, vl, wh, ulen, r);
73 add_lines(ustart, v_, wl, ulen, r);
74 add_lines(ustart, v_, wh, ulen, r);
75 add_lines(ustart, vh, wl, ulen, r);
76 add_lines(ustart, vh, w_, ulen, r);
77 add_lines(ustart, vh, wh, ulen, r);
78 }
79 return r;
80 }
81
82 template<typename Func>
84 size_t idx = 0;
85 for (int w = 0; w != mask.nw; ++w)
86 for (int v = 0; v != mask.nv; ++v)
87 for (int u = 0; u != mask.nu; ++u, ++idx) {
88 assert(idx == mask.index_q(u, v, w));
89 if (mask.data[idx] == Land) {
90 // it temporarily marks current island as this_island()
92 func(r);
93 }
94 }
95 // set big islands (continents) back to Land
96 for (T& p : mask.data)
97 p = T((int)p & 1);
98 }
99
100private:
101 void add_lines(int u, int v, int w, int ulen, Result& r) {
102 T* ptr = &mask.data[mask.index_q(u, v, w)];
103 for (int i = 0; i < std::min(ulen, mask.nu - u); ++i)
104 if (ptr[i] == Land) {
105 r.lines.push_back(line_from_point(u + i, v, w, ptr + i));
106 set_line_values(r.lines.back(), this_island());
107 }
108 for (int i = -u; i < ulen - mask.nu; ++i)
109 if (ptr[i] == Land) {
110 r.lines.push_back(line_from_point(u + i, v, w, ptr + i));
111 set_line_values(r.lines.back(), this_island());
112 }
113 }
114
115 Line line_from_point(int u, int v, int w, T* ptr) const {
116 int len = 1;
117 while (u + len < mask.nu && ptr[len] == Land)
118 ++len;
119 if (u + len == mask.nu)
120 while (len < mask.nu && ptr[len - mask.nu] == Land)
121 ++len;
122 for (int i = 0; i > -u; --i)
123 if (ptr[i-1] != Land)
124 return {u + i, v, w, len - i, ptr + i};
125 if (ptr[mask.nu-1-u] != Land)
126 return {0, v, w, len + u, ptr - u};
127 for (int i = mask.nu - 1 - u; i > 1; --i)
128 if (ptr[i-1] != Land)
129 return {u + i, v, w, len + mask.nu - 2 - i, ptr + i};
130 return {u, v, w, mask.nu, ptr};
131 }
132};
133
135 double threshold, bool negate=false) {
136 mask.copy_metadata_from(grid);
137 mask.data.resize(grid.data.size());
138 size_t n = 0;
139 for (float d : grid.data)
140 mask.data[n++] = std::int8_t((negate ? -d : d) > threshold);
141}
142
144 const std::vector<Position>& seeds,
145 double threshold,
146 bool negate=false) {
147 grid.check_not_empty();
151 for (const Position& pos : seeds) {
152 auto point = mask.get_nearest_point(pos);
153 if (*point.value == 1)
154 flood_fill.find_all_connected_points(point.u, point.v, point.w);
155 }
156 for (std::int8_t& d : mask.data)
157 d = std::int8_t(d == flood_fill.this_island());
158 return mask;
159}
160
161} // namespace gemmi
162#endif
void mask_nodes_above_threshold(Grid< std::int8_t > &mask, const Grid< float > &grid, double threshold, bool negate=false)
Grid< std::int8_t > flood_fill_above(const Grid< float > &grid, const std::vector< Position > &seeds, double threshold, bool negate=false)
std::vector< Line > lines
Definition floodfill.hpp:25
size_t point_count() const
Definition floodfill.hpp:27
void for_each_islands(Func func)
Definition floodfill.hpp:83
Result find_all_connected_points(int u, int v, int w)
Definition floodfill.hpp:54
Grid< T > & mask
Definition floodfill.hpp:17
void set_line_values(Line &line, T value) const
Definition floodfill.hpp:37
void set_volume_values(Result &r, T value) const
Definition floodfill.hpp:48
static constexpr T this_island()
Definition floodfill.hpp:35
std::vector< T > data
Definition grid.hpp:235
size_t index_q(int u, int v, int w) const
Quick(est) index function, but works only if 0 <= u < nu, etc.
Definition grid.hpp:203
Coordinates in Angstroms - orthogonal (Cartesian) coordinates.
Definition unitcell.hpp:32