cudaslicer/src/vectorizer.h
2026-09-25 18:09:19 +03:00

289 lines
10 KiB
C++

#ifndef VECTORIZER_H
#define VECTORIZER_H
#include "gpu_offset.h"
#include <vector>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cstdint>
inline int get_h_edge_id(int x, int y, int stride_w) {
return 2 * (y * stride_w + x) + 0;
}
inline int get_v_edge_id(int x, int y, int stride_w) {
return 2 * (y * stride_w + x) + 1;
}
inline GpuPoint2D get_edge_point(int edge_id, int stride_w, float min_x, float min_y, float pitch_mm) {
int type = edge_id & 1;
int cell = edge_id >> 1;
int y = cell / stride_w;
int x = cell % stride_w;
if (type == 0) {
return GpuPoint2D{ min_x + (x + 0.5f) * pitch_mm, min_y + (float)y * pitch_mm };
} else {
return GpuPoint2D{ min_x + (float)x * pitch_mm, min_y + (y + 0.5f) * pitch_mm };
}
}
static inline void vectorize_mask_layer(
const uint8_t* mask,
int width, int height,
float min_x, float min_y, float pitch_mm,
std::vector<int>& next_edge_buf,
std::vector<int>& active_edges_buf,
std::vector<uint8_t>& visited_buf,
std::vector<GpuContour>& out_contours
) {
int stride_w = width + 1;
active_edges_buf.clear();
auto add_directed_edge = [&](int e0, int e1) {
next_edge_buf[e0] = e1;
visited_buf[e0] = 0;
active_edges_buf.push_back(e0);
};
for (int y = 0; y < height - 1; ++y) {
const uint8_t* row0 = mask + y * width;
const uint8_t* row1 = mask + (y + 1) * width;
for (int x = 0; x < width - 1; ++x) {
int c0 = row0[x];
int c1 = row0[x + 1];
int c2 = row1[x + 1];
int c3 = row1[x];
int case_idx = (c0) | (c1 << 1) | (c2 << 2) | (c3 << 3);
if (case_idx == 0 || case_idx == 15) continue;
int e_bottom = get_h_edge_id(x, y, stride_w);
int e_top = get_h_edge_id(x, y + 1, stride_w);
int e_left = get_v_edge_id(x, y, stride_w);
int e_right = get_v_edge_id(x + 1, y, stride_w);
switch (case_idx) {
case 1: add_directed_edge(e_left, e_bottom); break;
case 2: add_directed_edge(e_bottom, e_right); break;
case 3: add_directed_edge(e_left, e_right); break;
case 4: add_directed_edge(e_right, e_top); break;
case 5: add_directed_edge(e_left, e_top); add_directed_edge(e_bottom, e_right); break;
case 6: add_directed_edge(e_bottom, e_top); break;
case 7: add_directed_edge(e_left, e_top); break;
case 8: add_directed_edge(e_top, e_left); break;
case 9: add_directed_edge(e_top, e_bottom); break;
case 10: add_directed_edge(e_bottom, e_left); add_directed_edge(e_top, e_right); break;
case 11: add_directed_edge(e_top, e_right); break;
case 12: add_directed_edge(e_right, e_left); break;
case 13: add_directed_edge(e_right, e_bottom); break;
case 14: add_directed_edge(e_bottom, e_left); break;
}
}
}
if (active_edges_buf.empty()) return;
std::vector<GpuPoint2D> loop_pts;
loop_pts.reserve(1000);
for (int start_e : active_edges_buf) {
if (visited_buf[start_e]) continue;
loop_pts.clear();
int cur = start_e;
while (cur != -1 && !visited_buf[cur]) {
visited_buf[cur] = 1;
GpuPoint2D pt = get_edge_point(cur, stride_w, min_x, min_y, pitch_mm);
size_t n = loop_pts.size();
if (n >= 2) {
float dx1 = loop_pts[n-1].x - loop_pts[n-2].x;
float dy1 = loop_pts[n-1].y - loop_pts[n-2].y;
float dx2 = pt.x - loop_pts[n-1].x;
float dy2 = pt.y - loop_pts[n-1].y;
float cross = dx1 * dy2 - dy1 * dx2;
if (std::abs(cross) < 1e-6f) {
loop_pts[n-1] = pt; // simplify collinear vertex
} else {
loop_pts.push_back(pt);
}
} else {
loop_pts.push_back(pt);
}
cur = next_edge_buf[cur];
if (cur == start_e) break;
if (loop_pts.size() > 50000) break;
}
if (loop_pts.size() >= 3) {
size_t n = loop_pts.size();
float dx1 = loop_pts[0].x - loop_pts[n-1].x;
float dy1 = loop_pts[0].y - loop_pts[n-1].y;
float dx2 = loop_pts[1].x - loop_pts[0].x;
float dy2 = loop_pts[1].y - loop_pts[0].y;
if (std::abs(dx1 * dy2 - dy1 * dx2) < 1e-6f) {
loop_pts.erase(loop_pts.begin());
}
if (loop_pts.size() >= 3) {
GpuContour c;
c.num_points = loop_pts.size();
c.points = (GpuPoint2D*)malloc(c.num_points * sizeof(GpuPoint2D));
memcpy(c.points, loop_pts.data(), c.num_points * sizeof(GpuPoint2D));
c.is_hole = 0;
out_contours.push_back(c);
}
}
}
// Reset thread-local edge table for zero re-allocation overhead
for (int e : active_edges_buf) {
next_edge_buf[e] = -1;
visited_buf[e] = 0;
}
}
static inline void vectorize_bitpack_layer(
const unsigned int* bit_words,
int width, int height, int row_stride_words,
float min_x, float min_y, float pitch_mm,
std::vector<int>& next_edge_buf,
std::vector<int>& active_edges_buf,
std::vector<uint8_t>& visited_buf,
std::vector<GpuContour>& out_contours
) {
int stride_w = width + 1;
active_edges_buf.clear();
auto add_directed_edge = [&](int e0, int e1) {
next_edge_buf[e0] = e1;
visited_buf[e0] = 0;
active_edges_buf.push_back(e0);
};
for (int y = 0; y < height - 1; ++y) {
const unsigned int* row0 = bit_words + (size_t)y * row_stride_words;
const unsigned int* row1 = bit_words + (size_t)(y + 1) * row_stride_words;
for (int w = 0; w < row_stride_words; ++w) {
unsigned int w0 = row0[w];
unsigned int w1 = row1[w];
unsigned int w0_next = (w + 1 < row_stride_words) ? row0[w + 1] : 0;
unsigned int w1_next = (w + 1 < row_stride_words) ? row1[w + 1] : 0;
if ((w0 == 0 && w1 == 0 && w0_next == 0 && w1_next == 0) ||
(w0 == 0xFFFFFFFF && w1 == 0xFFFFFFFF && (w0_next & 1) && (w1_next & 1))) {
continue;
}
int x_base = w * 32;
for (int b = 0; b < 32; ++b) {
int x = x_base + b;
if (x >= width - 1) break;
int c0 = (w0 >> b) & 1;
int c1 = (b < 31) ? ((w0 >> (b + 1)) & 1) : (w0_next & 1);
int c2 = (b < 31) ? ((w1 >> (b + 1)) & 1) : (w1_next & 1);
int c3 = (w1 >> b) & 1;
int case_idx = c0 | (c1 << 1) | (c2 << 2) | (c3 << 3);
if (case_idx == 0 || case_idx == 15) continue;
int e_bottom = get_h_edge_id(x, y, stride_w);
int e_top = get_h_edge_id(x, y + 1, stride_w);
int e_left = get_v_edge_id(x, y, stride_w);
int e_right = get_v_edge_id(x + 1, y, stride_w);
switch (case_idx) {
case 1: add_directed_edge(e_left, e_bottom); break;
case 2: add_directed_edge(e_bottom, e_right); break;
case 3: add_directed_edge(e_left, e_right); break;
case 4: add_directed_edge(e_right, e_top); break;
case 5: add_directed_edge(e_left, e_top); add_directed_edge(e_bottom, e_right); break;
case 6: add_directed_edge(e_bottom, e_top); break;
case 7: add_directed_edge(e_left, e_top); break;
case 8: add_directed_edge(e_top, e_left); break;
case 9: add_directed_edge(e_top, e_bottom); break;
case 10: add_directed_edge(e_bottom, e_left); add_directed_edge(e_top, e_right); break;
case 11: add_directed_edge(e_top, e_right); break;
case 12: add_directed_edge(e_right, e_left); break;
case 13: add_directed_edge(e_right, e_bottom); break;
case 14: add_directed_edge(e_bottom, e_left); break;
}
}
}
}
if (active_edges_buf.empty()) return;
std::vector<GpuPoint2D> loop_pts;
loop_pts.reserve(1000);
for (int start_e : active_edges_buf) {
if (visited_buf[start_e]) continue;
loop_pts.clear();
int cur = start_e;
while (cur != -1 && !visited_buf[cur]) {
visited_buf[cur] = 1;
GpuPoint2D pt = get_edge_point(cur, stride_w, min_x, min_y, pitch_mm);
size_t n = loop_pts.size();
if (n >= 2) {
float dx1 = loop_pts[n-1].x - loop_pts[n-2].x;
float dy1 = loop_pts[n-1].y - loop_pts[n-2].y;
float dx2 = pt.x - loop_pts[n-1].x;
float dy2 = pt.y - loop_pts[n-1].y;
float cross = dx1 * dy2 - dy1 * dx2;
if (std::abs(cross) < 1e-6f) {
loop_pts[n-1] = pt; // simplify collinear vertex
} else {
loop_pts.push_back(pt);
}
} else {
loop_pts.push_back(pt);
}
cur = next_edge_buf[cur];
if (cur == start_e) break;
if (loop_pts.size() > 50000) break;
}
if (loop_pts.size() >= 3) {
size_t n = loop_pts.size();
float dx1 = loop_pts[0].x - loop_pts[n-1].x;
float dy1 = loop_pts[0].y - loop_pts[n-1].y;
float dx2 = loop_pts[1].x - loop_pts[0].x;
float dy2 = loop_pts[1].y - loop_pts[0].y;
if (std::abs(dx1 * dy2 - dy1 * dx2) < 1e-6f) {
loop_pts.erase(loop_pts.begin());
}
if (loop_pts.size() >= 3) {
GpuContour c;
c.num_points = loop_pts.size();
c.points = (GpuPoint2D*)malloc(c.num_points * sizeof(GpuPoint2D));
memcpy(c.points, loop_pts.data(), c.num_points * sizeof(GpuPoint2D));
c.is_hole = 0;
out_contours.push_back(c);
}
}
}
// Reset thread-local edge table for zero re-allocation overhead
for (int e : active_edges_buf) {
next_edge_buf[e] = -1;
visited_buf[e] = 0;
}
}
#endif // VECTORIZER_H