1#include "internal/shared_contours.h"
3#include "internal/bezier.h"
10#include <unordered_map>
14constexpr int SMOOTHING_ITERATIONS {5};
15constexpr int32_t OUTSIDE = std::numeric_limits<int32_t>::min();
19void smooth_edge(std::vector<Point>& p,
int w,
int h,
int iters) {
20 const int n =
static_cast<int>(p.size());
23 auto on_border = [&](
const Point& q) {
24 return q.x <= 0.0f || q.y <= 0.0f || q.x >= w || q.y >= h;
26 for (
int it = 0; it < iters; ++it) {
27 std::vector<Point> q = p;
28 for (
int i = 1; i < n - 1; ++i) {
31 const Point& a = p[i - 1];
32 const Point& b = p[i];
33 const Point& c = p[i + 1];
34 q[i] = {0.25f * a.x + 0.5f * b.x + 0.25f * c.x, 0.25f * a.y + 0.5f * b.y + 0.25f * c.y};
43std::vector<QuadBezier> fit_edge(
const std::vector<Point>& corners,
int w,
int h,
float eps) {
44 std::vector<Point> pts = corners;
45 smooth_edge(pts, w, h, SMOOTHING_ITERATIONS);
48 std::vector<std::vector<Point>> chain {pts};
49 std::vector<std::vector<QuadBezier>> res;
50 fit_curve_reduction(chain, res, eps);
51 return res.empty() ? std::vector<QuadBezier> {} : res[0];
54void reverse_curve(std::vector<QuadBezier>& c) {
55 std::reverse(c.begin(), c.end());
57 std::swap(q.p0, q.p2);
60std::unordered_map<int32_t, std::vector<std::vector<QuadBezier>>>
61build_shared_loops(
const std::vector<int32_t>& labels,
int w,
int h,
float eps) {
63 auto L = [&](
int x,
int y) -> int32_t {
64 if (x < 0 || x >= w || y < 0 || y >= h)
66 return labels[
static_cast<size_t>(y) * w + x];
68 auto cidx = [&](
int cx,
int cy) {
71 auto cx_of = [&](
int idx) {
74 auto cy_of = [&](
int idx) {
77 auto pt_of = [&](
int idx) {
78 return Point {
static_cast<float>(cx_of(idx)),
static_cast<float>(cy_of(idx))};
82 std::unordered_map<int, std::vector<int>> adj;
83 auto add_crack = [&](
int a,
int b) {
87 for (
int cy = 0; cy <= h; ++cy)
88 for (
int cx = 0; cx <= w; ++cx) {
89 if (cy < h && L(cx - 1, cy) != L(cx, cy))
90 add_crack(cidx(cx, cy), cidx(cx, cy + 1));
91 if (cx < w && L(cx, cy - 1) != L(cx, cy))
92 add_crack(cidx(cx, cy), cidx(cx + 1, cy));
98 auto is_junction = [&](
int idx) {
99 auto it = adj.find(idx);
100 return it == adj.end() ? false : it->second.size() != 2;
107 std::vector<int> path;
108 std::vector<QuadBezier> curve;
110 std::vector<Edge> edges;
111 std::map<std::pair<int, int>,
int> crack_edge;
112 auto ckey = [](
int a,
int b) {
113 return std::make_pair(std::min(a, b), std::max(a, b));
116 auto store_edge = [&](std::vector<int> seq,
bool closed) {
117 std::vector<int> cseq = seq;
119 cseq.push_back(seq.front());
122 e.path = std::move(seq);
125 std::vector<Point> corners;
126 corners.reserve(cseq.size());
128 corners.push_back(pt_of(c));
129 e.curve = fit_edge(corners, w, h, eps);
130 int id =
static_cast<int>(edges.size());
131 for (
size_t i = 0; i + 1 < cseq.size(); ++i)
132 crack_edge[ckey(cseq[i], cseq[i + 1])] = id;
133 edges.push_back(std::move(e));
136 auto walk_edge = [&](
int start,
int first_next) {
137 std::vector<int> seq {start};
138 int prev = start, cur = first_next;
141 if (is_junction(cur))
144 for (
int nb : adj[cur])
149 if (nxt < 0 || cur == start)
158 for (
auto& kv : adj) {
159 if (!is_junction(kv.first))
161 for (
int nb : kv.second)
162 if (!crack_edge.count(ckey(kv.first, nb)))
163 store_edge(walk_edge(kv.first, nb), false);
166 for (
auto& kv : adj) {
167 for (
int nb : kv.second) {
168 if (crack_edge.count(ckey(kv.first, nb)))
170 std::vector<int> seq {kv.first};
171 int prev = kv.first, cur = nb;
172 const size_t cap = adj.size() + 4;
173 while (cur != kv.first && seq.size() < cap) {
176 for (
int x : adj[cur])
188 for (
size_t i = 0; i < seq.size(); ++i)
189 if (seq[i] < seq[mpos])
190 mpos =
static_cast<int>(i);
191 const int mm =
static_cast<int>(seq.size());
192 std::vector<int> rot;
194 for (
int i = 0; i < mm; ++i)
195 rot.push_back(seq[(mpos + i) % mm]);
196 store_edge(rot,
true);
201 std::unordered_map<int32_t, std::map<int, std::vector<int>>> region_dir;
202 for (
int y = 0; y < h; ++y)
203 for (
int x = 0; x < w; ++x) {
205 auto& dir = region_dir[r];
206 const int c00 = cidx(x, y), c10 = cidx(x + 1, y);
207 const int c11 = cidx(x + 1, y + 1), c01 = cidx(x, y + 1);
208 if (L(x, y - 1) != r)
209 dir[c00].push_back(c10);
210 if (L(x + 1, y) != r)
211 dir[c10].push_back(c11);
212 if (L(x, y + 1) != r)
213 dir[c11].push_back(c01);
214 if (L(x - 1, y) != r)
215 dir[c01].push_back(c00);
218 auto unit = [&](
int from,
int to,
int& dx,
int& dy) {
219 dx = cx_of(to) - cx_of(from);
220 dy = cy_of(to) - cy_of(from);
224 std::unordered_map<int32_t, std::vector<std::vector<QuadBezier>>> result;
225 for (
auto& rkv : region_dir) {
226 int32_t r = rkv.first;
229 std::map<int, std::vector<int>> dir = rkv.second;
232 auto take_from = [&](
int from,
int dx_in,
int dy_in) ->
int {
233 auto it = dir.find(from);
234 if (it == dir.end() || it->second.empty())
236 auto& outs = it->second;
237 int pref[4][2] = {{-dy_in, dx_in}, {dx_in, dy_in}, {dy_in, -dx_in}, {-dx_in, -dy_in}};
238 for (
auto& pr : pref)
239 for (size_t k = 0; k < outs.size(); ++k) {
241 unit(from, outs[k], dx, dy);
242 if (dx == pr[0] && dy == pr[1]) {
244 outs.erase(outs.begin() + k);
248 int to = outs.back();
253 std::vector<std::vector<int>> loops;
254 for (
auto& it : dir) {
255 while (!it.second.empty()) {
256 int start = it.first;
257 int nxt = take_from(start, 1, 0);
260 std::vector<int> loop {start};
262 unit(start, nxt, dx_in, dy_in);
264 while (cur != start) {
266 int nn = take_from(cur, dx_in, dy_in);
269 unit(cur, nn, dx_in, dy_in);
272 loops.push_back(std::move(loop));
276 std::vector<std::vector<QuadBezier>>& out_loops = result[r];
277 for (
auto& loop : loops) {
278 int m =
static_cast<int>(loop.size());
283 for (
int t = 0; t < m; ++t)
284 if (is_junction(loop[t])) {
289 std::rotate(loop.begin(), loop.begin() + js, loop.end());
291 std::vector<QuadBezier> curve;
295 int to = loop[(i + 1) % m];
296 auto eit = crack_edge.find(ckey(from, to));
297 if (eit == crack_edge.end()) {
301 const Edge& e = edges[eit->second];
302 std::vector<QuadBezier> seg = e.curve;
304 int mm =
static_cast<int>(e.path.size());
306 while (p < mm && e.path[p] != from)
308 bool fwd = (p < mm) && (e.path[(p + 1) % mm] == to);
315 bool fwd = (from == e.a);
320 int other = fwd ? e.b : e.a;
322 while (j < m && loop[j] != other)
327 if (curve.size() >= 2)
328 out_loops.push_back(std::move(curve));