FIX: Multicolor slicing error when contours self-intersect
github: 4138 Change-Id: I08375e2cf66d4fa4c7322f5aa1b8e86a7c49bf2d (cherry picked from commit b2a1f816605615cbd2e47c62a00d3b87998e3213)
This commit is contained in:
parent
5a850305c6
commit
0b0e03df1f
|
@ -31,6 +31,454 @@
|
|||
//#define MM_SEGMENTATION_DEBUG_TOP_BOTTOM
|
||||
|
||||
namespace Slic3r {
|
||||
using boost::polygon::voronoi_diagram;
|
||||
|
||||
static inline Point mk_point(const Voronoi::VD::vertex_type *point) { return {coord_t(point->x()), coord_t(point->y())}; }
|
||||
|
||||
static inline Point mk_point(const Voronoi::Internal::point_type &point) { return {coord_t(point.x()), coord_t(point.y())}; }
|
||||
|
||||
static inline Point mk_point(const voronoi_diagram<double>::vertex_type &point) { return {coord_t(point.x()), coord_t(point.y())}; }
|
||||
|
||||
static inline Point mk_point(const Vec2d &point) { return {coord_t(std::round(point.x())), coord_t(std::round(point.y()))}; }
|
||||
|
||||
static inline Vec2d mk_vec2(const voronoi_diagram<double>::vertex_type *point) { return {point->x(), point->y()}; }
|
||||
|
||||
static bool vertex_equal_to_point(const Voronoi::VD::vertex_type &vertex, const Vec2d &ipt)
|
||||
{
|
||||
// Convert ipt to doubles, force the 80bit FPU temporary to 64bit and then compare.
|
||||
// This should work with any settings of math compiler switches and the C++ compiler
|
||||
// shall understand the memcpies as type punning and it shall optimize them out.
|
||||
using ulp_cmp_type = boost::polygon::detail::ulp_comparison<double>;
|
||||
ulp_cmp_type ulp_cmp;
|
||||
static constexpr int ULPS = boost::polygon::voronoi_diagram_traits<double>::vertex_equality_predicate_type::ULPS;
|
||||
return ulp_cmp(vertex.x(), ipt.x(), ULPS) == ulp_cmp_type::EQUAL && ulp_cmp(vertex.y(), ipt.y(), ULPS) == ulp_cmp_type::EQUAL;
|
||||
}
|
||||
|
||||
static inline bool vertex_equal_to_point(const Voronoi::VD::vertex_type *vertex, const Vec2d &ipt) { return vertex_equal_to_point(*vertex, ipt); }
|
||||
|
||||
struct MMU_Graph
|
||||
{
|
||||
enum class ARC_TYPE { BORDER, NON_BORDER };
|
||||
|
||||
struct Arc
|
||||
{
|
||||
size_t from_idx;
|
||||
size_t to_idx;
|
||||
int color;
|
||||
ARC_TYPE type;
|
||||
|
||||
bool operator==(const Arc &rhs) const { return (from_idx == rhs.from_idx) && (to_idx == rhs.to_idx) && (color == rhs.color) && (type == rhs.type); }
|
||||
bool operator!=(const Arc &rhs) const { return !operator==(rhs); }
|
||||
};
|
||||
|
||||
struct Node
|
||||
{
|
||||
Vec2d point;
|
||||
std::list<size_t> arc_idxs;
|
||||
|
||||
void remove_edge(const size_t to_idx, MMU_Graph &graph)
|
||||
{
|
||||
for (auto arc_it = this->arc_idxs.begin(); arc_it != this->arc_idxs.end(); ++arc_it) {
|
||||
MMU_Graph::Arc &arc = graph.arcs[*arc_it];
|
||||
if (arc.to_idx == to_idx) {
|
||||
assert(arc.type != ARC_TYPE::BORDER);
|
||||
this->arc_idxs.erase(arc_it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<MMU_Graph::Node> nodes;
|
||||
std::vector<MMU_Graph::Arc> arcs;
|
||||
size_t all_border_points{};
|
||||
|
||||
std::vector<size_t> polygon_idx_offset;
|
||||
std::vector<size_t> polygon_sizes;
|
||||
|
||||
void remove_edge(const size_t from_idx, const size_t to_idx)
|
||||
{
|
||||
nodes[from_idx].remove_edge(to_idx, *this);
|
||||
nodes[to_idx].remove_edge(from_idx, *this);
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t get_global_index(const size_t poly_idx, const size_t point_idx) const { return polygon_idx_offset[poly_idx] + point_idx; }
|
||||
|
||||
void append_edge(const size_t &from_idx, const size_t &to_idx, int color = -1, ARC_TYPE type = ARC_TYPE::NON_BORDER)
|
||||
{
|
||||
// Don't append duplicate edges between the same nodes.
|
||||
for (const size_t &arc_idx : this->nodes[from_idx].arc_idxs)
|
||||
if (arcs[arc_idx].to_idx == to_idx) return;
|
||||
for (const size_t &arc_idx : this->nodes[to_idx].arc_idxs)
|
||||
if (arcs[arc_idx].to_idx == from_idx) return;
|
||||
|
||||
this->nodes[from_idx].arc_idxs.push_back(this->arcs.size());
|
||||
this->arcs.push_back({from_idx, to_idx, color, type});
|
||||
|
||||
// Always insert only one directed arc for the input polygons.
|
||||
// Two directed arcs in both directions are inserted if arcs aren't between points of the input polygons.
|
||||
if (type == ARC_TYPE::NON_BORDER) {
|
||||
this->nodes[to_idx].arc_idxs.push_back(this->arcs.size());
|
||||
this->arcs.push_back({to_idx, from_idx, color, type});
|
||||
}
|
||||
}
|
||||
|
||||
// It assumes that between points of the input polygons is always only one directed arc,
|
||||
// with the same direction as lines of the input polygon.
|
||||
[[nodiscard]] MMU_Graph::Arc get_border_arc(size_t idx) const
|
||||
{
|
||||
assert(idx < this->all_border_points);
|
||||
return this->arcs[idx];
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t nodes_count() const { return this->nodes.size(); }
|
||||
|
||||
void remove_nodes_with_one_arc()
|
||||
{
|
||||
std::queue<size_t> update_queue;
|
||||
for (const MMU_Graph::Node &node : this->nodes) {
|
||||
size_t node_idx = &node - &this->nodes.front();
|
||||
// Skip nodes that represent points of input polygons.
|
||||
if (node.arc_idxs.size() == 1 && node_idx >= this->all_border_points) update_queue.emplace(&node - &this->nodes.front());
|
||||
}
|
||||
|
||||
while (!update_queue.empty()) {
|
||||
size_t node_from_idx = update_queue.front();
|
||||
MMU_Graph::Node &node_from = this->nodes[update_queue.front()];
|
||||
update_queue.pop();
|
||||
if (node_from.arc_idxs.empty()) continue;
|
||||
|
||||
assert(node_from.arc_idxs.size() == 1);
|
||||
size_t node_to_idx = arcs[node_from.arc_idxs.front()].to_idx;
|
||||
MMU_Graph::Node &node_to = this->nodes[node_to_idx];
|
||||
this->remove_edge(node_from_idx, node_to_idx);
|
||||
if (node_to.arc_idxs.size() == 1 && node_to_idx >= this->all_border_points) update_queue.emplace(node_to_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void add_contours(const std::vector<std::vector<ColoredLine>> &color_poly)
|
||||
{
|
||||
this->all_border_points = nodes.size();
|
||||
this->polygon_sizes = std::vector<size_t>(color_poly.size());
|
||||
for (size_t polygon_idx = 0; polygon_idx < color_poly.size(); ++polygon_idx) this->polygon_sizes[polygon_idx] = color_poly[polygon_idx].size();
|
||||
this->polygon_idx_offset = std::vector<size_t>(color_poly.size());
|
||||
this->polygon_idx_offset[0] = 0;
|
||||
for (size_t polygon_idx = 1; polygon_idx < color_poly.size(); ++polygon_idx) {
|
||||
this->polygon_idx_offset[polygon_idx] = this->polygon_idx_offset[polygon_idx - 1] + color_poly[polygon_idx - 1].size();
|
||||
}
|
||||
|
||||
size_t poly_idx = 0;
|
||||
for (const std::vector<ColoredLine> &color_lines : color_poly) {
|
||||
size_t line_idx = 0;
|
||||
for (const ColoredLine &color_line : color_lines) {
|
||||
size_t from_idx = this->get_global_index(poly_idx, line_idx);
|
||||
size_t to_idx = this->get_global_index(poly_idx, (line_idx + 1) % color_lines.size());
|
||||
this->append_edge(from_idx, to_idx, color_line.color, ARC_TYPE::BORDER);
|
||||
++line_idx;
|
||||
}
|
||||
++poly_idx;
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes 0..all_border_points are only one with are on countour. Other vertexis are consider as not on coouter. So we check if base on attach index
|
||||
inline bool is_vertex_on_contour(const Voronoi::VD::vertex_type *vertex) const
|
||||
{
|
||||
assert(vertex != nullptr);
|
||||
return vertex->color() < this->all_border_points;
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_edge_attach_to_contour(const voronoi_diagram<double>::const_edge_iterator &edge_iterator) const
|
||||
{
|
||||
return this->is_vertex_on_contour(edge_iterator->vertex0()) || this->is_vertex_on_contour(edge_iterator->vertex1());
|
||||
}
|
||||
|
||||
[[nodiscard]] inline bool is_edge_connecting_two_contour_vertices(const voronoi_diagram<double>::const_edge_iterator &edge_iterator) const
|
||||
{
|
||||
return this->is_vertex_on_contour(edge_iterator->vertex0()) && this->is_vertex_on_contour(edge_iterator->vertex1());
|
||||
}
|
||||
|
||||
// All Voronoi vertices are post-processes to merge very close vertices to single. Witch eliminates issues with intersection edges.
|
||||
// Also, Voronoi vertices outside of the bounding of input polygons are throw away by marking them.
|
||||
void append_voronoi_vertices(const Geometry::VoronoiDiagram &vd, const Polygons &color_poly_tmp, BoundingBox bbox)
|
||||
{
|
||||
bbox.offset(SCALED_EPSILON);
|
||||
|
||||
struct CPoint
|
||||
{
|
||||
CPoint() = delete;
|
||||
CPoint(const Vec2d &point, size_t contour_idx, size_t point_idx) : m_point_double(point), m_point(mk_point(point)), m_point_idx(point_idx), m_contour_idx(contour_idx)
|
||||
{}
|
||||
CPoint(const Vec2d &point, size_t point_idx) : m_point_double(point), m_point(mk_point(point)), m_point_idx(point_idx), m_contour_idx(0) {}
|
||||
const Vec2d m_point_double;
|
||||
const Point m_point;
|
||||
size_t m_point_idx;
|
||||
size_t m_contour_idx;
|
||||
|
||||
[[nodiscard]] const Vec2d &point_double() const { return m_point_double; }
|
||||
[[nodiscard]] const Point &point() const { return m_point; }
|
||||
bool operator==(const CPoint &rhs) const
|
||||
{
|
||||
return this->m_point_double == rhs.m_point_double && this->m_contour_idx == rhs.m_contour_idx && this->m_point_idx == rhs.m_point_idx;
|
||||
}
|
||||
};
|
||||
struct CPointAccessor
|
||||
{
|
||||
const Point *operator()(const CPoint &pt) const { return &pt.point(); }
|
||||
};
|
||||
typedef ClosestPointInRadiusLookup<CPoint, CPointAccessor> CPointLookupType;
|
||||
|
||||
CPointLookupType closest_voronoi_point(coord_t(SCALED_EPSILON));
|
||||
CPointLookupType closest_contour_point(3 * coord_t(SCALED_EPSILON));
|
||||
for (const Polygon &polygon : color_poly_tmp)
|
||||
for (const Point &pt : polygon.points) closest_contour_point.insert(CPoint(Vec2d(pt.x(), pt.y()), &polygon - &color_poly_tmp.front(), &pt - &polygon.points.front()));
|
||||
|
||||
for (const voronoi_diagram<double>::vertex_type &vertex : vd.vertices()) {
|
||||
vertex.color(-1);
|
||||
Vec2d vertex_point_double = Vec2d(vertex.x(), vertex.y());
|
||||
Point vertex_point = mk_point(vertex);
|
||||
|
||||
const Vec2d &first_point_double = this->nodes[this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx].point;
|
||||
const Vec2d &second_point_double = this->nodes[this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx].point;
|
||||
|
||||
if (vertex_equal_to_point(&vertex, first_point_double)) {
|
||||
assert(vertex.color() != vertex.incident_edge()->cell()->source_index());
|
||||
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
|
||||
vertex.color(this->get_border_arc(vertex.incident_edge()->cell()->source_index()).from_idx);
|
||||
} else if (vertex_equal_to_point(&vertex, second_point_double)) {
|
||||
assert(vertex.color() != vertex.incident_edge()->cell()->source_index());
|
||||
assert(vertex.color() != vertex.incident_edge()->twin()->cell()->source_index());
|
||||
vertex.color(this->get_border_arc(vertex.incident_edge()->twin()->cell()->source_index()).from_idx);
|
||||
} else if (bbox.contains(vertex_point)) {
|
||||
if (auto [contour_pt, c_dist_sqr] = closest_contour_point.find(vertex_point); contour_pt != nullptr && c_dist_sqr < Slic3r::sqr(3 * SCALED_EPSILON)) {
|
||||
vertex.color(this->get_global_index(contour_pt->m_contour_idx, contour_pt->m_point_idx));
|
||||
} else if (auto [voronoi_pt, v_dist_sqr] = closest_voronoi_point.find(vertex_point); voronoi_pt == nullptr || v_dist_sqr >= Slic3r::sqr(SCALED_EPSILON / 10.0)) {
|
||||
closest_voronoi_point.insert(CPoint(vertex_point_double, this->nodes_count()));
|
||||
vertex.color(this->nodes_count());
|
||||
this->nodes.push_back({vertex_point_double});
|
||||
} else {
|
||||
// Boost Voronoi diagram generator sometimes creates two very closed points instead of one point.
|
||||
// For the example points (146872.99999999997, -146872.99999999997) and (146873, -146873), this example also included in Voronoi generator test cases.
|
||||
std::vector<std::pair<const CPoint *, double>> all_closes_c_points = closest_voronoi_point.find_all(vertex_point);
|
||||
int merge_to_point = -1;
|
||||
for (const std::pair<const CPoint *, double> &c_point : all_closes_c_points)
|
||||
if ((vertex_point_double - c_point.first->point_double()).squaredNorm() <= Slic3r::sqr(EPSILON)) {
|
||||
merge_to_point = int(c_point.first->m_point_idx);
|
||||
break;
|
||||
}
|
||||
|
||||
if (merge_to_point != -1) {
|
||||
vertex.color(merge_to_point);
|
||||
} else {
|
||||
closest_voronoi_point.insert(CPoint(vertex_point_double, this->nodes_count()));
|
||||
vertex.color(this->nodes_count());
|
||||
this->nodes.push_back({vertex_point_double});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void garbage_collect()
|
||||
{
|
||||
std::vector<int> nodes_map(this->nodes.size(), -1);
|
||||
int nodes_count = 0;
|
||||
size_t arcs_count = 0;
|
||||
for (const MMU_Graph::Node &node : this->nodes)
|
||||
if (size_t node_idx = &node - &this->nodes.front(); !node.arc_idxs.empty()) {
|
||||
nodes_map[node_idx] = nodes_count++;
|
||||
arcs_count += node.arc_idxs.size();
|
||||
}
|
||||
|
||||
std::vector<MMU_Graph::Node> new_nodes;
|
||||
std::vector<MMU_Graph::Arc> new_arcs;
|
||||
new_nodes.reserve(nodes_count);
|
||||
new_arcs.reserve(arcs_count);
|
||||
for (const MMU_Graph::Node &node : this->nodes)
|
||||
if (size_t node_idx = &node - &this->nodes.front(); nodes_map[node_idx] >= 0) {
|
||||
new_nodes.push_back({node.point});
|
||||
for (const size_t &arc_idx : node.arc_idxs) {
|
||||
const Arc &arc = this->arcs[arc_idx];
|
||||
new_nodes.back().arc_idxs.emplace_back(new_arcs.size());
|
||||
new_arcs.push_back({size_t(nodes_map[arc.from_idx]), size_t(nodes_map[arc.to_idx]), arc.color, arc.type});
|
||||
}
|
||||
}
|
||||
|
||||
this->nodes = std::move(new_nodes);
|
||||
this->arcs = std::move(new_arcs);
|
||||
}
|
||||
};
|
||||
|
||||
static Polygon colored_points_to_polygon(const std::vector<ColoredLine> &lines)
|
||||
{
|
||||
Polygon out;
|
||||
out.points.reserve(lines.size());
|
||||
for (const ColoredLine &l : lines) out.points.emplace_back(l.line.a);
|
||||
return out;
|
||||
}
|
||||
|
||||
static Polygons colored_points_to_polygon(const std::vector<std::vector<ColoredLine>> &lines)
|
||||
{
|
||||
Polygons out;
|
||||
out.reserve(lines.size());
|
||||
for (const std::vector<ColoredLine> &l : lines) out.emplace_back(colored_points_to_polygon(l));
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::vector<std::vector<const MMU_Graph::Arc *>> get_all_next_arcs(
|
||||
const MMU_Graph &graph, std::vector<bool> &used_arcs, const Linef &process_line, const MMU_Graph::Arc &original_arc, const int color)
|
||||
{
|
||||
std::vector<std::vector<const MMU_Graph::Arc *>> all_next_arcs;
|
||||
for (const size_t &arc_idx : graph.nodes[original_arc.to_idx].arc_idxs) {
|
||||
std::vector<const MMU_Graph::Arc *> next_continue_arc;
|
||||
|
||||
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
|
||||
if (graph.nodes[arc.to_idx].point == process_line.a || used_arcs[arc_idx]) continue;
|
||||
|
||||
if (original_arc.type == MMU_Graph::ARC_TYPE::BORDER && original_arc.color != color) continue;
|
||||
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::BORDER && arc.color != color) continue;
|
||||
|
||||
Vec2d arc_line = graph.nodes[arc.to_idx].point - graph.nodes[arc.from_idx].point;
|
||||
next_continue_arc.emplace_back(&arc);
|
||||
all_next_arcs.emplace_back(next_continue_arc);
|
||||
}
|
||||
return all_next_arcs;
|
||||
}
|
||||
|
||||
static std::vector<const MMU_Graph::Arc *> get_next_arc(
|
||||
const MMU_Graph &graph, std::vector<bool> &used_arcs, const Linef &process_line, const MMU_Graph::Arc &original_arc, const int color)
|
||||
{
|
||||
std::vector<const MMU_Graph::Arc *> res;
|
||||
|
||||
std::vector<std::vector<const MMU_Graph::Arc *>> all_next_arcs = get_all_next_arcs(graph, used_arcs, process_line, original_arc, color);
|
||||
if (all_next_arcs.empty()) {
|
||||
res.emplace_back(&original_arc);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::vector<const MMU_Graph::Arc *>, double>> sorted_arcs;
|
||||
for (auto next_arc : all_next_arcs) {
|
||||
if (next_arc.empty()) continue;
|
||||
|
||||
Vec2d process_line_vec_n = (process_line.a - process_line.b).normalized();
|
||||
Vec2d neighbour_line_vec_n = (graph.nodes[next_arc.back()->to_idx].point - graph.nodes[next_arc.back()->from_idx].point).normalized();
|
||||
|
||||
double angle = ::acos(std::clamp(neighbour_line_vec_n.dot(process_line_vec_n), -1.0, 1.0));
|
||||
if (Slic3r::cross2(neighbour_line_vec_n, process_line_vec_n) < 0.0) angle = 2.0 * (double) PI - angle;
|
||||
|
||||
sorted_arcs.emplace_back(next_arc, angle);
|
||||
}
|
||||
|
||||
std::sort(sorted_arcs.begin(), sorted_arcs.end(),
|
||||
[](std::pair<std::vector<const MMU_Graph::Arc *>, double> &l, std::pair<std::vector<const MMU_Graph::Arc *>, double> &r) -> bool { return l.second < r.second; });
|
||||
|
||||
// Try to return left most edge witch is unused
|
||||
for (auto &sorted_arc : sorted_arcs) {
|
||||
if (size_t arc_idx = sorted_arc.first.back() - &graph.arcs.front(); !used_arcs[arc_idx]) return sorted_arc.first;
|
||||
}
|
||||
|
||||
if (sorted_arcs.empty()) {
|
||||
res.emplace_back(&original_arc);
|
||||
return res;
|
||||
}
|
||||
|
||||
return sorted_arcs.front().first;
|
||||
}
|
||||
|
||||
static bool is_profile_self_interaction(Polygon poly)
|
||||
{
|
||||
auto lines = poly.lines();
|
||||
Point intersection;
|
||||
for (int i = 0; i < lines.size(); ++i) {
|
||||
for (int j = i + 2; j < std::min(lines.size(), lines.size() + i - 1); ++j) {
|
||||
if (lines[i].intersection(lines[j], &intersection)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline Polygon to_polygon(const std::vector<std::pair<size_t, Linef>> &id_to_lines)
|
||||
{
|
||||
std::vector<Linef> lines;
|
||||
for (auto id_to_line : id_to_lines) lines.emplace_back(id_to_line.second);
|
||||
|
||||
Polygon poly_out;
|
||||
poly_out.points.reserve(lines.size());
|
||||
for (const Linef &line : lines) poly_out.points.emplace_back(mk_point(line.a));
|
||||
return poly_out;
|
||||
}
|
||||
|
||||
static std::vector<ExPolygons> extract_colored_segments(const MMU_Graph &graph, const size_t num_extruders)
|
||||
{
|
||||
std::vector<bool> used_arcs(graph.arcs.size(), false);
|
||||
|
||||
auto all_arc_used = [&used_arcs](const MMU_Graph::Node &node) -> bool {
|
||||
return std::all_of(node.arc_idxs.cbegin(), node.arc_idxs.cend(), [&used_arcs](const size_t &arc_idx) -> bool { return used_arcs[arc_idx]; });
|
||||
};
|
||||
|
||||
std::vector<ExPolygons> expolygons_segments(num_extruders + 1);
|
||||
for (size_t node_idx = 0; node_idx < graph.all_border_points; ++node_idx) {
|
||||
const MMU_Graph::Node &node = graph.nodes[node_idx];
|
||||
|
||||
for (const size_t &arc_idx : node.arc_idxs) {
|
||||
const MMU_Graph::Arc &arc = graph.arcs[arc_idx];
|
||||
if (arc.type == MMU_Graph::ARC_TYPE::NON_BORDER || used_arcs[arc_idx]) continue;
|
||||
|
||||
Linef process_line(graph.nodes[arc.from_idx].point, graph.nodes[arc.to_idx].point);
|
||||
used_arcs[arc_idx] = true;
|
||||
|
||||
std::vector<std::pair<size_t, Linef>> arc_id_to_face_lines;
|
||||
arc_id_to_face_lines.emplace_back(std::make_pair(arc_idx, process_line));
|
||||
Vec2d start_p = process_line.a;
|
||||
|
||||
Linef p_vec = process_line;
|
||||
const MMU_Graph::Arc *p_arc = &arc;
|
||||
bool flag = false;
|
||||
do {
|
||||
std::vector<const MMU_Graph::Arc *> nexts = get_next_arc(graph, used_arcs, p_vec, *p_arc, arc.color);
|
||||
for (auto next : nexts) {
|
||||
size_t next_arc_idx = next - &graph.arcs.front();
|
||||
if (used_arcs[next_arc_idx]) {
|
||||
flag = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (flag) break;
|
||||
|
||||
for (auto next : nexts) {
|
||||
size_t next_arc_idx = next - &graph.arcs.front();
|
||||
arc_id_to_face_lines.emplace_back(std::make_pair(next_arc_idx, Linef(graph.nodes[next->from_idx].point, graph.nodes[next->to_idx].point)));
|
||||
used_arcs[next_arc_idx] = true;
|
||||
}
|
||||
|
||||
p_vec = Linef(graph.nodes[nexts.back()->from_idx].point, graph.nodes[nexts.back()->to_idx].point);
|
||||
p_arc = nexts.back();
|
||||
|
||||
} while (graph.nodes[p_arc->to_idx].point != start_p || !all_arc_used(graph.nodes[p_arc->to_idx]));
|
||||
|
||||
if (Polygon poly = to_polygon(arc_id_to_face_lines); poly.is_counter_clockwise() && poly.is_valid()) {
|
||||
expolygons_segments[arc.color].emplace_back(std::move(poly));
|
||||
} else {
|
||||
while (arc_id_to_face_lines.size() > 1) {
|
||||
auto id_to_line = arc_id_to_face_lines.back();
|
||||
used_arcs[id_to_line.first] = false;
|
||||
arc_id_to_face_lines.pop_back();
|
||||
Linef add_line(arc_id_to_face_lines.back().second.b, arc_id_to_face_lines.front().second.a);
|
||||
arc_id_to_face_lines.emplace_back(std::make_pair(-1, add_line));
|
||||
Polygon poly = to_polygon(arc_id_to_face_lines);
|
||||
if (!is_profile_self_interaction(poly) && poly.is_counter_clockwise() && poly.is_valid()) {
|
||||
expolygons_segments[arc.color].emplace_back(std::move(poly));
|
||||
break;
|
||||
}
|
||||
arc_id_to_face_lines.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return expolygons_segments;
|
||||
}
|
||||
|
||||
bool is_equal(float left, float right, float eps = 1e-3) {
|
||||
return abs(left - right) <= eps;
|
||||
}
|
||||
|
@ -693,6 +1141,7 @@ static void remove_multiple_edges_in_vertex(const VD::vertex_type &vertex) {
|
|||
}
|
||||
}
|
||||
|
||||
#if (0)
|
||||
// Returns list of ExPolygons for each extruder + 1 for default unpainted regions.
|
||||
// It iterates through all nodes on the border between two different colors, and from this point,
|
||||
// start selection always left most edges for every node to construct CCW polygons.
|
||||
|
@ -828,6 +1277,7 @@ static std::vector<ExPolygons> extract_colored_segments(const std::vector<Colore
|
|||
|
||||
return segmented_expolygons_per_extruder;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cut_segmented_layers(const std::vector<ExPolygons> &input_expolygons,
|
||||
std::vector<std::vector<ExPolygons>> &segmented_regions,
|
||||
|
@ -1158,6 +1608,358 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
|
|||
return triangles_by_color_merged;
|
||||
}
|
||||
|
||||
// For every ColoredLine in lines_colored_out, assign the index of the polygon to which belongs and also the index of this line inside of the polygon.
|
||||
static inline void init_polygon_indices(const MMU_Graph &graph, const std::vector<std::vector<ColoredLine>> &color_poly, std::vector<ColoredLine> &lines_colored_out)
|
||||
{
|
||||
size_t poly_idx = 0;
|
||||
for (const std::vector<ColoredLine> &color_lines : color_poly) {
|
||||
size_t line_idx = 0;
|
||||
for (size_t color_line_idx = 0; color_line_idx < color_lines.size(); ++color_line_idx) {
|
||||
size_t from_idx = graph.get_global_index(poly_idx, line_idx);
|
||||
lines_colored_out[from_idx].poly_idx = int(poly_idx);
|
||||
lines_colored_out[from_idx].local_line_idx = int(line_idx);
|
||||
++line_idx;
|
||||
}
|
||||
++poly_idx;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool line_intersection_with_epsilon(const Line &line_to_extend, const Line &other, Point *intersection)
|
||||
{
|
||||
Line extended_line = line_to_extend;
|
||||
extended_line.extend(15 * SCALED_EPSILON);
|
||||
return extended_line.intersection(other, intersection);
|
||||
}
|
||||
|
||||
static inline void mark_processed(const voronoi_diagram<double>::const_edge_iterator &edge_iterator)
|
||||
{
|
||||
edge_iterator->color(true);
|
||||
edge_iterator->twin()->color(true);
|
||||
}
|
||||
|
||||
static inline bool is_point_closer_to_beginning_of_line(const Line &line, const Point &p)
|
||||
{
|
||||
return (p - line.a).cast<double>().squaredNorm() < (p - line.b).cast<double>().squaredNorm();
|
||||
}
|
||||
|
||||
static inline Line clip_finite_voronoi_edge(const Voronoi::VD::edge_type &edge, const BoundingBoxf &bbox)
|
||||
{
|
||||
assert(edge.is_finite());
|
||||
Vec2d v0 = mk_vec2(edge.vertex0());
|
||||
Vec2d v1 = mk_vec2(edge.vertex1());
|
||||
bool contains_v0 = bbox.contains(v0);
|
||||
bool contains_v1 = bbox.contains(v1);
|
||||
if ((contains_v0 && contains_v1) || (!contains_v0 && !contains_v1)) return {mk_point(edge.vertex0()), mk_point(edge.vertex1())};
|
||||
|
||||
Vec2d vector = (v1 - v0).normalized() * bbox.size().norm();
|
||||
if (!contains_v0)
|
||||
v0 = (v1 - vector);
|
||||
else
|
||||
v1 = (v0 + vector);
|
||||
|
||||
return {v0.cast<coord_t>(), v1.cast<coord_t>()};
|
||||
}
|
||||
|
||||
static inline bool has_same_color(const ColoredLine &cl1, const ColoredLine &cl2) { return cl1.color == cl2.color; }
|
||||
|
||||
static MMU_Graph build_graph(size_t layer_idx, const std::vector<std::vector<ColoredLine>> &color_poly)
|
||||
{
|
||||
const Polygons color_poly_tmp = colored_points_to_polygon(color_poly);
|
||||
const Points points = to_points(color_poly_tmp);
|
||||
const Lines lines = to_lines(color_poly_tmp);
|
||||
|
||||
// The algorithm adds edges to the graph that are between two different colors.
|
||||
// If a polygon is colored entirely with one color, we need to add at least one edge from that polygon artificially.
|
||||
// Adding this edge is necessary for cases where the expolygon has an outer contour colored whole with one color
|
||||
// and a hole colored with a different color. If an edge wasn't added to the graph,
|
||||
// the entire expolygon would be colored with single random color instead of two different.
|
||||
std::vector<bool> force_edge_adding(color_poly.size());
|
||||
|
||||
// For each polygon, check if it is all colored with the same color. If it is, we need to force adding one edge to it.
|
||||
for (const std::vector<ColoredLine> &c_poly : color_poly) {
|
||||
bool force_edge = true;
|
||||
for (const ColoredLine &c_line : c_poly)
|
||||
if (c_line.color != c_poly.front().color) {
|
||||
force_edge = false;
|
||||
break;
|
||||
}
|
||||
force_edge_adding[&c_poly - &color_poly.front()] = force_edge;
|
||||
}
|
||||
|
||||
ColoredLines lines_colored = to_lines(color_poly);
|
||||
const ColoredLines colored_lines = lines_colored;
|
||||
|
||||
Voronoi::VD vd;
|
||||
vd.construct_voronoi(colored_lines.begin(), colored_lines.end());
|
||||
// boost::polygon::construct_voronoi(lines_colored.begin(), lines_colored.end(), &vd);
|
||||
MMU_Graph graph;
|
||||
graph.nodes.reserve(points.size() + vd.vertices().size());
|
||||
for (const Point &point : points) graph.nodes.push_back({Vec2d(double(point.x()), double(point.y()))});
|
||||
|
||||
graph.add_contours(color_poly);
|
||||
init_polygon_indices(graph, color_poly, lines_colored);
|
||||
|
||||
assert(graph.nodes.size() == lines_colored.size());
|
||||
BoundingBox bbox = get_extents(color_poly_tmp);
|
||||
graph.append_voronoi_vertices(vd, color_poly_tmp, bbox);
|
||||
|
||||
auto get_prev_contour_line = [&lines_colored, &color_poly, &graph](const voronoi_diagram<double>::const_edge_iterator &edge_it) -> ColoredLine {
|
||||
size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx;
|
||||
size_t contour_line_size = color_poly[lines_colored[edge_it->cell()->source_index()].poly_idx].size();
|
||||
size_t contour_prev_idx = graph.get_global_index(lines_colored[edge_it->cell()->source_index()].poly_idx,
|
||||
(contour_line_local_idx > 0) ? contour_line_local_idx - 1 : contour_line_size - 1);
|
||||
return lines_colored[contour_prev_idx];
|
||||
};
|
||||
|
||||
auto get_next_contour_line = [&lines_colored, &color_poly, &graph](const voronoi_diagram<double>::const_edge_iterator &edge_it) -> ColoredLine {
|
||||
size_t contour_line_local_idx = lines_colored[edge_it->cell()->source_index()].local_line_idx;
|
||||
size_t contour_line_size = color_poly[lines_colored[edge_it->cell()->source_index()].poly_idx].size();
|
||||
size_t contour_next_idx = graph.get_global_index(lines_colored[edge_it->cell()->source_index()].poly_idx, (contour_line_local_idx + 1) % contour_line_size);
|
||||
return lines_colored[contour_next_idx];
|
||||
};
|
||||
|
||||
bbox.offset(scale_(10.));
|
||||
const BoundingBoxf bbox_clip(bbox.min.cast<double>(), bbox.max.cast<double>());
|
||||
const double bbox_dim_max = double(std::max(bbox.size().x(), bbox.size().y()));
|
||||
|
||||
// Make a copy of the input segments with the double type.
|
||||
std::vector<Voronoi::Internal::segment_type> segments;
|
||||
for (const Line &line : lines)
|
||||
segments.emplace_back(Voronoi::Internal::point_type(double(line.a(0)), double(line.a(1))), Voronoi::Internal::point_type(double(line.b(0)), double(line.b(1))));
|
||||
|
||||
for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) {
|
||||
// Skip second half-edge
|
||||
if (edge_it->cell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) continue;
|
||||
|
||||
if (edge_it->is_infinite() && (edge_it->vertex0() != nullptr || edge_it->vertex1() != nullptr)) {
|
||||
// Infinite edge is leading through a point on the counter, but there are no Voronoi vertices.
|
||||
// So we could fix this case by computing the intersection between the contour line and infinity edge.
|
||||
std::vector<Voronoi::Internal::point_type> samples;
|
||||
Voronoi::Internal::clip_infinite_edge(points, segments, *edge_it, bbox_dim_max, &samples);
|
||||
if (samples.empty()) continue;
|
||||
|
||||
const Line edge_line(mk_point(samples[0]), mk_point(samples[1]));
|
||||
const ColoredLine &contour_line = lines_colored[edge_it->cell()->source_index()];
|
||||
Point contour_intersection;
|
||||
|
||||
if (line_intersection_with_epsilon(contour_line.line, edge_line, &contour_intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_it->cell()->source_index());
|
||||
const size_t from_idx = (edge_it->vertex1() != nullptr) ? edge_it->vertex1()->color() : edge_it->vertex0()->color();
|
||||
size_t to_idx = ((contour_line.line.a - contour_intersection).cast<double>().squaredNorm() <
|
||||
(contour_line.line.b - contour_intersection).cast<double>().squaredNorm()) ?
|
||||
graph_arc.from_idx :
|
||||
graph_arc.to_idx;
|
||||
if (from_idx != to_idx && from_idx < graph.nodes_count() && to_idx < graph.nodes_count()) {
|
||||
graph.append_edge(from_idx, to_idx);
|
||||
mark_processed(edge_it);
|
||||
}
|
||||
}
|
||||
} else if (edge_it->is_finite()) {
|
||||
// Both points are on contour, so skip them. In cases of duplicate Voronoi vertices, skip edges between the same two points.
|
||||
if (graph.is_edge_connecting_two_contour_vertices(edge_it) || (edge_it->vertex0()->color() == edge_it->vertex1()->color())) continue;
|
||||
|
||||
const Line edge_line = clip_finite_voronoi_edge(*edge_it, bbox_clip);
|
||||
const Line contour_line = lines_colored[edge_it->cell()->source_index()].line;
|
||||
const ColoredLine colored_line = lines_colored[edge_it->cell()->source_index()];
|
||||
const ColoredLine contour_line_prev = get_prev_contour_line(edge_it);
|
||||
const ColoredLine contour_line_next = get_next_contour_line(edge_it);
|
||||
|
||||
if (edge_it->vertex0()->color() >= graph.nodes_count() || edge_it->vertex1()->color() >= graph.nodes_count()) {
|
||||
enum class Vertex { VERTEX0, VERTEX1 };
|
||||
auto append_edge_if_intersects_with_contour = [&graph, &lines_colored, &edge_line,
|
||||
&contour_line](const voronoi_diagram<double>::const_edge_iterator &edge_iterator, const Vertex vertex) {
|
||||
Point intersection;
|
||||
Line contour_line_twin = lines_colored[edge_iterator->twin()->cell()->source_index()].line;
|
||||
if (line_intersection_with_epsilon(contour_line_twin, edge_line, &intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_iterator->twin()->cell()->source_index());
|
||||
const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line_twin, intersection) ? graph_arc.from_idx : graph_arc.to_idx;
|
||||
graph.append_edge(vertex == Vertex::VERTEX0 ? edge_iterator->vertex0()->color() : edge_iterator->vertex1()->color(), to_idx_l);
|
||||
} else if (line_intersection_with_epsilon(contour_line, edge_line, &intersection)) {
|
||||
const MMU_Graph::Arc &graph_arc = graph.get_border_arc(edge_iterator->cell()->source_index());
|
||||
const size_t to_idx_l = is_point_closer_to_beginning_of_line(contour_line, intersection) ? graph_arc.from_idx : graph_arc.to_idx;
|
||||
graph.append_edge(vertex == Vertex::VERTEX0 ? edge_iterator->vertex0()->color() : edge_iterator->vertex1()->color(), to_idx_l);
|
||||
}
|
||||
mark_processed(edge_iterator);
|
||||
};
|
||||
|
||||
if (edge_it->vertex0()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex0()))
|
||||
append_edge_if_intersects_with_contour(edge_it, Vertex::VERTEX0);
|
||||
|
||||
if (edge_it->vertex1()->color() < graph.nodes_count() && !graph.is_vertex_on_contour(edge_it->vertex1()))
|
||||
append_edge_if_intersects_with_contour(edge_it, Vertex::VERTEX1);
|
||||
} else if (graph.is_edge_attach_to_contour(edge_it)) {
|
||||
mark_processed(edge_it);
|
||||
// Skip edges witch connection two points on a contour
|
||||
if (graph.is_edge_connecting_two_contour_vertices(edge_it)) continue;
|
||||
|
||||
const size_t from_idx = edge_it->vertex0()->color();
|
||||
const size_t to_idx = edge_it->vertex1()->color();
|
||||
if (graph.is_vertex_on_contour(edge_it->vertex0())) {
|
||||
if (is_point_closer_to_beginning_of_line(contour_line, edge_line.a)) {
|
||||
if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) &&
|
||||
points_inside(contour_line_prev.line, contour_line, edge_line.b)) {
|
||||
graph.append_edge(from_idx, to_idx);
|
||||
force_edge_adding[colored_line.poly_idx] = false;
|
||||
}
|
||||
} else {
|
||||
if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) &&
|
||||
points_inside(contour_line, contour_line_next.line, edge_line.b)) {
|
||||
graph.append_edge(from_idx, to_idx);
|
||||
force_edge_adding[colored_line.poly_idx] = false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(graph.is_vertex_on_contour(edge_it->vertex1()));
|
||||
if (is_point_closer_to_beginning_of_line(contour_line, edge_line.b)) {
|
||||
if ((!has_same_color(contour_line_prev, colored_line) || force_edge_adding[colored_line.poly_idx]) &&
|
||||
points_inside(contour_line_prev.line, contour_line, edge_line.a)) {
|
||||
graph.append_edge(from_idx, to_idx);
|
||||
force_edge_adding[colored_line.poly_idx] = false;
|
||||
}
|
||||
} else {
|
||||
if ((!has_same_color(contour_line_next, colored_line) || force_edge_adding[colored_line.poly_idx]) &&
|
||||
points_inside(contour_line, contour_line_next.line, edge_line.a)) {
|
||||
graph.append_edge(from_idx, to_idx);
|
||||
force_edge_adding[colored_line.poly_idx] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (Point intersection; line_intersection_with_epsilon(contour_line, edge_line, &intersection)) {
|
||||
mark_processed(edge_it);
|
||||
Vec2d real_v0_double = graph.nodes[edge_it->vertex0()->color()].point;
|
||||
Vec2d real_v1_double = graph.nodes[edge_it->vertex1()->color()].point;
|
||||
Point real_v0 = Point(coord_t(real_v0_double.x()), coord_t(real_v0_double.y()));
|
||||
Point real_v1 = Point(coord_t(real_v1_double.x()), coord_t(real_v1_double.y()));
|
||||
|
||||
if (is_point_closer_to_beginning_of_line(contour_line, intersection)) {
|
||||
Line first_part(intersection, real_v0);
|
||||
Line second_part(intersection, real_v1);
|
||||
|
||||
if (!has_same_color(contour_line_prev, colored_line)) {
|
||||
if (points_inside(contour_line_prev.line, contour_line, first_part.b))
|
||||
graph.append_edge(edge_it->vertex0()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
|
||||
|
||||
if (points_inside(contour_line_prev.line, contour_line, second_part.b))
|
||||
graph.append_edge(edge_it->vertex1()->color(), graph.get_border_arc(edge_it->cell()->source_index()).from_idx);
|
||||
}
|
||||
} else {
|
||||
const size_t int_point_idx = graph.get_border_arc(edge_it->cell()->source_index()).to_idx;
|
||||
const Vec2d int_point_double = graph.nodes[int_point_idx].point;
|
||||
const Point int_point = Point(coord_t(int_point_double.x()), coord_t(int_point_double.y()));
|
||||
|
||||
const Line first_part(int_point, real_v0);
|
||||
const Line second_part(int_point, real_v1);
|
||||
|
||||
if (!has_same_color(contour_line_next, colored_line)) {
|
||||
if (points_inside(contour_line, contour_line_next.line, first_part.b)) graph.append_edge(edge_it->vertex0()->color(), int_point_idx);
|
||||
|
||||
if (points_inside(contour_line, contour_line_next.line, second_part.b)) graph.append_edge(edge_it->vertex1()->color(), int_point_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto edge_it = vd.edges().begin(); edge_it != vd.edges().end(); ++edge_it) {
|
||||
// Skip second half-edge and processed edges
|
||||
if (edge_it->cell()->source_index() > edge_it->twin()->cell()->source_index() || edge_it->color()) continue;
|
||||
|
||||
if (edge_it->is_finite() && !bool(edge_it->color()) && edge_it->vertex0()->color() < graph.nodes_count() && edge_it->vertex1()->color() < graph.nodes_count()) {
|
||||
// Skip cases, when the edge is between two same vertices, which is in cases two near vertices were merged together.
|
||||
if (edge_it->vertex0()->color() == edge_it->vertex1()->color()) continue;
|
||||
|
||||
size_t from_idx = edge_it->vertex0()->color();
|
||||
size_t to_idx = edge_it->vertex1()->color();
|
||||
graph.append_edge(from_idx, to_idx);
|
||||
}
|
||||
mark_processed(edge_it);
|
||||
}
|
||||
|
||||
graph.remove_nodes_with_one_arc();
|
||||
return graph;
|
||||
}
|
||||
|
||||
static std::vector<std::vector<std::pair<size_t, size_t>>> get_all_segments(const std::vector<std::vector<ColoredLine>> &color_poly)
|
||||
{
|
||||
std::vector<std::vector<std::pair<size_t, size_t>>> all_segments(color_poly.size());
|
||||
for (size_t poly_idx = 0; poly_idx < color_poly.size(); ++poly_idx) {
|
||||
const std::vector<ColoredLine> &c_polygon = color_poly[poly_idx];
|
||||
all_segments[poly_idx] = get_segments(c_polygon);
|
||||
}
|
||||
return all_segments;
|
||||
}
|
||||
|
||||
static inline double compute_edge_length(const MMU_Graph &graph, const size_t start_idx, const size_t &start_arc_idx)
|
||||
{
|
||||
assert(start_arc_idx < graph.arcs.size());
|
||||
std::vector<bool> used_arcs(graph.arcs.size(), false);
|
||||
|
||||
used_arcs[start_arc_idx] = true;
|
||||
const MMU_Graph::Arc *arc = &graph.arcs[start_arc_idx];
|
||||
size_t idx = start_idx;
|
||||
double line_total_length = (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).norm();
|
||||
while (graph.nodes[arc->to_idx].arc_idxs.size() == 2) {
|
||||
bool found = false;
|
||||
for (const size_t &arc_idx : graph.nodes[arc->to_idx].arc_idxs) {
|
||||
if (const MMU_Graph::Arc &arc_n = graph.arcs[arc_idx]; arc_n.type == MMU_Graph::ARC_TYPE::NON_BORDER && !used_arcs[arc_idx] && arc_n.to_idx != idx) {
|
||||
Linef first_line(graph.nodes[idx].point, graph.nodes[arc->to_idx].point);
|
||||
Linef second_line(graph.nodes[arc->to_idx].point, graph.nodes[arc_n.to_idx].point);
|
||||
|
||||
Vec2d first_line_vec = (first_line.a - first_line.b);
|
||||
Vec2d second_line_vec = (second_line.b - second_line.a);
|
||||
Vec2d first_line_vec_n = first_line_vec.normalized();
|
||||
Vec2d second_line_vec_n = second_line_vec.normalized();
|
||||
double angle = ::acos(std::clamp(first_line_vec_n.dot(second_line_vec_n), -1.0, 1.0));
|
||||
if (Slic3r::cross2(first_line_vec_n, second_line_vec_n) < 0.0) angle = 2.0 * (double) PI - angle;
|
||||
|
||||
if (std::abs(angle - PI) >= (PI / 12)) continue;
|
||||
|
||||
idx = arc->to_idx;
|
||||
arc = &arc_n;
|
||||
|
||||
line_total_length += (graph.nodes[arc->to_idx].point - graph.nodes[idx].point).norm();
|
||||
used_arcs[arc_idx] = true;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) break;
|
||||
}
|
||||
|
||||
return line_total_length;
|
||||
}
|
||||
|
||||
static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vector<std::vector<ColoredLine>> &color_poly)
|
||||
{
|
||||
std::vector<std::vector<std::pair<size_t, size_t>>> colored_segments = get_all_segments(color_poly);
|
||||
for (const std::vector<std::pair<size_t, size_t>> &colored_segment_p : colored_segments) {
|
||||
size_t poly_idx = &colored_segment_p - &colored_segments.front();
|
||||
for (const std::pair<size_t, size_t> &colored_segment : colored_segment_p) {
|
||||
size_t first_idx = graph.get_global_index(poly_idx, colored_segment.first);
|
||||
size_t second_idx = graph.get_global_index(poly_idx, (colored_segment.second + 1) % graph.polygon_sizes[poly_idx]);
|
||||
Linef seg_line(graph.nodes[first_idx].point, graph.nodes[second_idx].point);
|
||||
|
||||
if (graph.nodes[first_idx].arc_idxs.size() >= 3) {
|
||||
std::vector<std::pair<MMU_Graph::Arc *, double>> arc_to_check;
|
||||
for (const size_t &arc_idx : graph.nodes[first_idx].arc_idxs) {
|
||||
MMU_Graph::Arc &n_arc = graph.arcs[arc_idx];
|
||||
if (n_arc.type == MMU_Graph::ARC_TYPE::NON_BORDER) {
|
||||
double total_len = compute_edge_length(graph, first_idx, arc_idx);
|
||||
arc_to_check.emplace_back(&n_arc, total_len);
|
||||
}
|
||||
}
|
||||
std::sort(arc_to_check.begin(), arc_to_check.end(),
|
||||
[](std::pair<MMU_Graph::Arc *, double> &l, std::pair<MMU_Graph::Arc *, double> &r) -> bool { return l.second > r.second; });
|
||||
|
||||
while (arc_to_check.size() > 1) {
|
||||
graph.remove_edge(first_idx, arc_to_check.back().first->to_idx);
|
||||
arc_to_check.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::vector<std::vector<ExPolygons>> merge_segmented_layers(
|
||||
const std::vector<std::vector<ExPolygons>> &segmented_regions,
|
||||
std::vector<std::vector<ExPolygons>> &&top_and_bottom_layers,
|
||||
|
@ -1476,7 +2278,11 @@ std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(con
|
|||
// If the whole layer is painted using the same color, it is not needed to construct a Voronoi diagram for the segmentation of this layer.
|
||||
segmented_regions[layer_idx][size_t(color_poly.front().front().color)] = input_expolygons[layer_idx];
|
||||
} else {
|
||||
segmented_regions[layer_idx] = extract_colored_segments(color_poly, num_extruders, layer_idx);
|
||||
MMU_Graph graph = build_graph(layer_idx, color_poly);
|
||||
remove_multiple_edges_in_vertices(graph, color_poly);
|
||||
graph.remove_nodes_with_one_arc();
|
||||
segmented_regions[layer_idx] = extract_colored_segments(graph, num_extruders);
|
||||
//segmented_regions[layer_idx] = extract_colored_segments(color_poly, num_extruders, layer_idx);
|
||||
}
|
||||
|
||||
#ifdef MM_SEGMENTATION_DEBUG_REGIONS
|
||||
|
|
Loading…
Reference in New Issue