diff --git a/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp b/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp index 0985d6e597..86319c4c5c 100644 --- a/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp +++ b/src/libslic3r/SLA/SupportIslands/UniformSupportIsland.cpp @@ -40,6 +40,7 @@ //#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGNED_TO_SVG_PATH "C:/data/temp/align/island_<>_aligned.svg" //#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH "C:/data/temp/align_once/iter_<>.svg" //#define SLA_SAMPLE_ISLAND_UTILS_DEBUG_CELL_DISTANCE_PATH "C:/data/temp/island_cell.svg" +//#define SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH "C:/data/temp/parts/part<>.svg" namespace { using namespace Slic3r; @@ -1843,14 +1844,24 @@ void merge_island_parts(IslandParts &island_parts, size_t index, size_t remove_i /// Index into island parts to merge /// Queue of future processing void merge_parts_and_fix_process(IslandParts &island_parts, - ProcessItem &item, size_t index, size_t remove_index, ProcessItems &process) { + ProcessItem &item, size_t index, size_t remove_index, ProcessItems &process, const Neighbor* neighbor) { if (remove_index == index) return; // nothing to merge, loop connect to itself - if (remove_index < index) // remove part with bigger index - std::swap(remove_index, index); // Merged parts should be the same state, it is essential for alhorithm // Only first island part changes its type, but only before first change - assert(island_parts[index].type == island_parts[remove_index].type); + IslandPart &part = island_parts[index]; + IslandPart &part_ = island_parts[remove_index]; + if (part.type != part_.type) { + part.changes.push_back(IslandPartChange{Position(neighbor, 0.), remove_index}); + const Neighbor *twin = VoronoiGraphUtils::get_twin(*neighbor); + part_.changes.push_back(IslandPartChange{Position(twin, 1.), index}); + return; + } + + // remove part with bigger index + if (remove_index < index) + std::swap(remove_index, index); + island_parts[index].sum_lengths += island_parts[remove_index].sum_lengths; merge_island_parts(island_parts, index, remove_index); @@ -2166,7 +2177,11 @@ std::pair> merge_negihbor(IslandParts &island_parts, // collect changes from neighbors for result part + indices of neighbor parts IslandPartChanges modified_changes; for (const IslandPartChange &change : changes) { - remove_indices.push_back(change.part_index); + if (auto it = std::lower_bound(remove_indices.begin(), remove_indices.end(), change.part_index); + it != remove_indices.end() && *it == change.part_index) + continue; // part already added + + VectorUtils::insert_sorted(remove_indices, change.part_index, std::less{}); // iterate neighbor changes and collect only changes to other neighbors for (const IslandPartChange &n_change : island_parts[change.part_index].changes) { if (n_change.part_index == index) @@ -2183,10 +2198,7 @@ std::pair> merge_negihbor(IslandParts &island_parts, } } - // Modified index is smallest from index or remove_indices - std::sort(remove_indices.begin(), remove_indices.end()); - remove_indices.erase( // Remove duplicit inidices - std::unique(remove_indices.begin(), remove_indices.end()), remove_indices.end()); + // Set modified index to smallest from index or remove_indices(are sorted) size_t modified_index = index; if (remove_indices.front() < index) { std::swap(remove_indices.front(), modified_index); @@ -2330,6 +2342,163 @@ std::pair convert_island_parts_to_thin_thick( return result; } +#ifdef SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH +void draw(const IslandParts &parts, const ProcessItems &queue, const ProcessItem& current, const Lines& lines) { + + static int counter = 0; + std::string svg_path = replace_first(SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH, "<>", std::to_string(counter++)); + SVG svg(svg_path.c_str(), LineUtils::create_bounding_box(lines)); + LineUtils::draw(svg, lines, "black", 0.); + + const char *thin_color = "blue"; + const char *thick_color = "green"; + const char *middle_color = "orange"; + coord_t edge_width = 3e4; + + using Neighbor = VoronoiGraph::Node::Neighbor; + std::vector queue_done; + auto get_neighbor = [](const VoronoiGraph::Node *from, const VoronoiGraph::Node* to)->const Neighbor* { + if (from == nullptr || to == nullptr) + return nullptr; + for (const Neighbor &n : from->neighbors) + if (n.node == to) + return &n; + return nullptr; + }; + for (const ProcessItem& it : queue) { + const Neighbor *n = get_neighbor(it.prev_node, it.node); + queue_done.push_back(n); + queue_done.push_back(VoronoiGraphUtils::get_twin(*n)); + } + + if (const Neighbor *n = get_neighbor(current.prev_node, current.node); + n != nullptr) { + queue_done.push_back(n); + queue_done.push_back(VoronoiGraphUtils::get_twin(*n)); + } + for (const IslandPart &part : parts){ + for (const IslandPartChange &change : part.changes) { // add changes + const Neighbor *n = change.position.neighbor; + queue_done.push_back(n); + queue_done.push_back(VoronoiGraphUtils::get_twin(*n)); + } + } + auto neighbor_sort = [](const Neighbor *a, const Neighbor *b) {return a < b;}; + std::sort(queue_done.begin(), queue_done.end(), neighbor_sort); + queue_done.erase(std::unique(queue_done.begin(), queue_done.end()), queue_done.end()); + + for (const IslandPart &part: parts){ + size_t index = &part - &parts.front(); + Positions ends; + const char *color = + part.type == IslandPartType::thin ? thin_color : + part.type == IslandPartType::thick ? thick_color : middle_color; + for (const IslandPartChange &change: part.changes){ + if (change.part_index > index) + continue; + + Point p = VoronoiGraphUtils::create_edge_point(change.position); + const auto edge = change.position.neighbor->edge; + const VD::vertex_type *v0 = edge->vertex0(); + const VD::vertex_type *v1 = edge->vertex1(); + Vec2d dir(v1->x() - v0->x(), v1->y() - v0->y()); + dir.normalize(); + dir.x() *= 0.6 / SCALING_FACTOR; + dir.y() *= 1 / SCALING_FACTOR; + Point dir_ = dir.cast(); + + auto type = parts[change.part_index].type; + const char *neighbor_color = + type == IslandPartType::thin ? thin_color : + type == IslandPartType::thick ? thick_color : middle_color; + svg.draw(p.cast(), "gray"); + VoronoiGraphUtils::draw(svg, *edge, lines, "gray", edge_width); + Point letter_offset(-.75/SCALING_FACTOR, -.7/SCALING_FACTOR); + svg.draw_text(letter_offset + p + dir_, std::to_string(change.part_index).c_str(), neighbor_color); + svg.draw_text(letter_offset + p - dir_, std::to_string(index).c_str(), color); + ends.push_back(change.position); + } + + if (part.changes.empty()) { + assert(parts.size() == 1); + continue; // only first part till first change + } + + const Neighbor *start = VoronoiGraphUtils::get_twin(*part.changes.front().position.neighbor); + auto is_start = [start](const IslandPartChange &change) { + return change.position.neighbor == start; + }; + if (std::find_if(part.changes.begin(), part.changes.end(), is_start) != part.changes.end()) + continue; // start is also end; + + std::vector done = queue_done; // copy queue + std::function draw_neighbor; // recursive function for draw + draw_neighbor = [&draw_neighbor, &done, &svg, &lines, color, neighbor_sort, edge_width, index] + (const Neighbor *neighbor) { + VectorUtils::insert_sorted(done, neighbor, neighbor_sort); + const Neighbor *twin = VoronoiGraphUtils::get_twin(*neighbor); + VectorUtils::insert_sorted(done, twin, neighbor_sort); + for (const Neighbor &n: neighbor->node->neighbors){ + if (&n == twin) + continue; + + if (auto it = std::lower_bound(done.begin(), done.end(), &n, neighbor_sort); + it != done.end() && *it == &n) + continue; // already done + + VoronoiGraphUtils::draw(svg, *n.edge, lines, color, edge_width); + auto v0 = n.edge->vertex0(); + auto v1 = n.edge->vertex1(); + Point p(v0->x() / 2 + v1->x() / 2, v0->y() / 2 + v1->y() / 2); + svg.draw_text(p, std::to_string(index).c_str(), color, 1); + draw_neighbor(&n);//recursive call + } + }; + draw_neighbor(start); + } + + for (const ProcessItem &item : queue) { + Point from = VoronoiGraphUtils::to_point(item.prev_node->vertex); + Point to = VoronoiGraphUtils::to_point(item.node->vertex); + svg.draw(Line(from, to), "darkgray"); + svg.draw(to, "darkgray"); + svg.draw_text(from / 2 + to / 2, std::to_string(item.i).c_str(), "darkgray"); + } + + if (current.prev_node == nullptr){ + if (current.node == nullptr) + return; + Point p = VoronoiGraphUtils::to_point(current.node->vertex); + svg.draw(p, "red"); + svg.draw_text(p, std::to_string(current.i).c_str(), "red"); + } else { + Point from = VoronoiGraphUtils::to_point(current.prev_node->vertex); + Point to = VoronoiGraphUtils::to_point(current.node->vertex); + svg.draw(Line(from, to), "red"); + svg.draw(to, "red"); + svg.draw_text(from / 2 + to / 2, std::to_string(current.i).c_str(), "red"); + } +} +#endif // SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH + +#ifndef NDEBUG +bool exist_twin_change_in_part(const IslandParts &parts){ + auto sort_by_neighbor = [](const IslandPartChange &c1, const IslandPartChange &c2) { + return c1.position.neighbor < c2.position.neighbor; + }; + auto is_same_neighbor = [](const IslandPartChange &c1, const IslandPartChange &c2) { + return c1.position.neighbor == c2.position.neighbor; + }; + for(const IslandPart &part: parts){ + IslandPartChanges changes = part.changes; // copy + std::sort(changes.begin(), changes.end(), sort_by_neighbor); + if (std::unique(changes.begin(), changes.end(), is_same_neighbor) != changes.end()) + return true; + } + return false; +} +#endif // DEBUG + /// /// Separate thin(narrow) and thick(wide) part of island /// @@ -2357,28 +2526,31 @@ std::pair separate_thin_thick( ProcessItem item = {/*prev_node*/ nullptr, start_node, 0}; // current processing item ProcessItems process; // queue of nodes to process do { // iterate over all nodes in graph and collect interfaces into island_parts - assert(item.node != nullptr); +//#ifdef SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH +// draw(island_parts, process, item, lines); +//#endif // SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH ProcessItem next_item = {nullptr, nullptr, std::numeric_limits::max()}; for (const Neighbor &neighbor: item.node->neighbors) { if (neighbor.node == item.prev_node) continue; // already done - if (next_item.node != nullptr) // already prepared item is stored into queue - process.push_back(next_item); - - size_t next_part_index = detect_interface(island_parts, item.i, &neighbor, lines, config); - next_item = ProcessItem{item.node, neighbor.node, next_part_index}; + if (next_item.node != nullptr) { // already prepared item is stored into queue + process.push_back(next_item); + next_item.node = nullptr; + } // exist loop back? - auto is_oposit_item = [&next_item](const ProcessItem &p) { - return p.node == next_item.prev_node && p.prev_node == next_item.node;}; + auto is_oposit_item = [prev_node = item.node, node = neighbor.node](const ProcessItem &p) { + return p.node == prev_node && p.prev_node == node;}; if (auto process_it = std::find_if(process.begin(), process.end(), is_oposit_item); process_it != process.end()) { // solve loop back - merge_parts_and_fix_process(island_parts, item, process_it->i, next_item.i, process); + merge_parts_and_fix_process(island_parts, item, item.i, process_it->i, process, &neighbor); // branch is already processed process.erase(process_it); - next_item.node = nullptr; // do not use item as next one continue; } + + size_t next_part_index = detect_interface(island_parts, item.i, &neighbor, lines, config); + next_item = ProcessItem{item.node, neighbor.node, next_part_index}; } // Select next node to process if (next_item.node != nullptr) { @@ -2390,13 +2562,19 @@ std::pair separate_thin_thick( process.pop_back(); } } while (item.node != nullptr); // loop should end by break with empty process +#ifdef SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH + draw(island_parts, process, item, lines); +#endif // SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH merge_middle_parts_into_biggest_neighbor(island_parts); if (island_parts.size() != 1) merge_same_neighbor_type_parts(island_parts); if (island_parts.size() != 1) merge_short_parts(island_parts, config.min_part_length); - + assert(!exist_twin_change_in_part(island_parts)); +#ifdef SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH + draw(island_parts, {}, {}, lines); +#endif // SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH return convert_island_parts_to_thin_thick(island_parts, path); } @@ -2663,6 +2841,9 @@ bool is_uniform_support_island_visualization_disabled() { #ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGNED_TO_SVG_PATH return false; #endif +#ifdef SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH + return false; +#endif return true; } diff --git a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp index 5f4de94734..814127a164 100644 --- a/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp +++ b/src/libslic3r/SLA/SupportIslands/VoronoiGraphUtils.cpp @@ -1320,7 +1320,7 @@ void VoronoiGraphUtils::draw(SVG & svg, std::stringstream ss; ss << prefix << std::hex << reinterpret_cast(addr); std::string s = ss.str(); - svg.draw_text(p, s.c_str(), color, 6); + svg.draw_text(p, s.c_str(), color, 1); } }; @@ -1329,25 +1329,56 @@ void VoronoiGraphUtils::draw(SVG & svg, "yellowgreen", // on way to thin (max is above thin) "limegreen", // between (inside histerezis) "forestgreen", // on way to thick (min is belove thick) - "darkgreen" // thick (min+max above thick) + "darkgreen", // thick (min+max above thick) + "brown" // cross histereze }; - auto get_color = [&](const VoronoiGraph::Node::Neighbor &n) { - if (n.min_width() > config.thin_max_width){ + std::vector color_description{ + "thin (min+max belowe thin " + std::to_string(config.thick_min_width) + ")", + "on way to thin (max is above thin)", + "between (inside histerezis)", + "on way to thick (min is belove thick)", + "thick (min+max above thick " + std::to_string(config.thin_max_width) + ")", + "cross histereze" + }; + Point legend_position = lines.front().a; + for (const Line& l: lines){ + if (legend_position.x() < l.a.x()) + legend_position.x() = l.a.x(); + if (legend_position.y() < l.a.y()) + legend_position.y() = l.a.y(); + } + for (size_t i = 0; i < 6; i++) { + Point p(legend_position.x(), legend_position.y() - static_cast(i * 1.3 / SCALING_FACTOR)); + std::string text = color_description[i] + " (" + skeleton_colors[i] + ")"; + svg.draw_text(p, text.c_str(), skeleton_colors[i], 8); + } + + auto get_color = [&skeleton_colors, + min_limit = config.thick_min_width, + max_limit = config.thin_max_width] + (const VoronoiGraph::Node::Neighbor &n) { + assert(n.min_width() <= n.max_width()); + coord_t min_width = n.min_width(); + coord_t max_width = n.max_width(); + if (min_width >= max_limit){ return skeleton_colors[4]; - } else if (n.max_width() < config.thick_min_width){ + } else if (max_width <= min_limit){ return skeleton_colors[0]; - } else if (n.min_width() < config.thin_max_width && - n.max_width() > config.thick_min_width){ + } else if (min_width >= min_limit && + max_width <= max_limit){ return skeleton_colors[2]; - } else if (n.min_width() < config.thick_min_width){ + } else if (min_width <= min_limit && + max_width >= max_limit){ + return skeleton_colors[5]; + } else if (min_width <= min_limit){ return skeleton_colors[1]; - } else if (n.max_width() > config.thin_max_width) { + } else if (max_width >= max_limit) { return skeleton_colors[3]; } assert(false); return "gray"; }; - + for (const auto &[key, value] : graph.data) { Point p(key->x(), key->y()); svg.draw(p, "lightgray", width); @@ -1357,17 +1388,16 @@ void VoronoiGraphUtils::draw(SVG & svg, Point to = to_point(n.edge->vertex1()); bool is_second = n.edge->vertex0() > n.edge->vertex1(); Point center = (from + to) / 2; - Point p = center + ((is_second) ? Point(0., -2e6) : - Point(0., 2e6)); + Point p = center + ((is_second) ? Point(0., -2e5) : Point(0., 2e5)); print_address(p, "neighbor ptr ", (void *) &n, "gray"); if (is_second) continue; const char *color = get_color(n); if (pointer_caption) { std::string width_str = "width min=" + std::to_string(n.min_width()) + " max=" + std::to_string(n.max_width()); - svg.draw_text(center + Point(-6e6, 0.), width_str.c_str(), color, 6); + svg.draw_text(center, width_str.c_str(), color, 3); } - draw(svg, *n.edge, lines, color, width); + draw(svg, *n.edge, lines, color, 2*width); } } } diff --git a/src/libslic3r/SLA/SupportPointGenerator.cpp b/src/libslic3r/SLA/SupportPointGenerator.cpp index 47760dc00c..019eef8fce 100644 --- a/src/libslic3r/SLA/SupportPointGenerator.cpp +++ b/src/libslic3r/SLA/SupportPointGenerator.cpp @@ -719,12 +719,12 @@ std::optional create_small_part( /// Recursive fast check function without storing any already searched data /// NOTE: Small part with holes could slow down checking /// - /// Index of layer with part - /// Index of part in layer.parts + /// Index of layer and part /// Recursion protection - std::function check_parts; + /// To prevent cycling calls + std::function check_parts; check_parts = [&range_bb, &check_parts, &layers, &island, radius_in_mm] - (const LayerPartIndex& check, size_t allowed_depth) -> bool { + (const LayerPartIndex& check, size_t allowed_depth, const LayerPartIndex& prev_check) -> bool { const Layer &check_layer = layers[check.layer_index]; const LayerPart &check_part = check_layer.parts[check.part_index]; for (const PartLink &link : check_part.next_parts) @@ -733,6 +733,9 @@ std::optional create_small_part( !range_bb.contains(link->shape_extent.max)) return false; // part is too large + if ((check_layer.print_z - layers[island.layer_index].print_z) > radius_in_mm) + return false; // parts is large in Z direction + --allowed_depth; if (allowed_depth == 0) return true; // break recursion @@ -741,7 +744,10 @@ std::optional create_small_part( size_t next_layer_i = check.layer_index + 1; for (const PartLink& link : check_part.next_parts) { size_t next_part_i = link - layers[next_layer_i].parts.cbegin(); - if (!check_parts({next_layer_i, next_part_i}, allowed_depth)) + if (next_layer_i == prev_check.layer_index && + next_part_i == prev_check.part_index) + continue; // checked in lambda caller contex + if (!check_parts({next_layer_i, next_part_i}, allowed_depth, check)) return false; } @@ -753,12 +759,7 @@ std::optional create_small_part( // Investigate only the first island(lower index into parts). if (check.part_index < island.part_index) return false; // part is already checked - } - - - if (float max_z = radius_in_mm + layers[island.layer_index].print_z; - check_layer.print_z > max_z) - return false; // too far in Z + } // NOTE: multiple investigation same part seems more relevant // instead of store and search for already checked parts @@ -774,20 +775,28 @@ std::optional create_small_part( for (const PartLink &link : check_part.prev_parts) { size_t prev_layer_i = check.layer_index - 1; // exist only when exist prev parts - cant be negative size_t prev_part_i = link - layers[prev_layer_i].parts.cbegin(); + + if (prev_layer_i == prev_check.layer_index && + prev_part_i == prev_check.part_index) + continue; // checked in lambda caller contex + // Improve: check only parts which are not already checked - if (!check_parts({prev_layer_i, prev_part_i}, allowed_depth)) + if (!check_parts({prev_layer_i, prev_part_i}, allowed_depth, check)) return false; } return true; }; - float layer_height = layers[1].print_z - layers[0].print_z; + float layer_height = (island.layer_index == 0) ? + layers[1].print_z - layers[0].print_z: + layers[island.layer_index].print_z - layers[island.layer_index-1].print_z; + assert(layer_height > 0.f); - float safe_multiplicator = 1.3f; // 30% more layers than radius for zigzag(up & down layer) search - size_t allowed_depth = static_cast(std::ceil(radius_in_mm / layer_height * safe_multiplicator)); + float safe_multiplicator = 1.4f; // 40% more layers than radius for zigzag(up & down layer) search + size_t allowed_depth = static_cast(std::ceil((radius_in_mm / layer_height + 1) * safe_multiplicator)); // Check Bounding boxes and do not create any data - FAST CHECK // NOTE: it could check model parts multiple times those there is allowed_depth - if (!check_parts(island, allowed_depth)) + if (!check_parts(island, allowed_depth, island)) return {}; SmallPart collected; // sorted by layer_i, part_i diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 92a1b772a0..860450d0f8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -885,8 +885,21 @@ void GLGizmoSlaSupports::draw_island_config() { ImGui::SameLine(); ImGui::Text("head radius %.2f mm", unscale(sample_config.head_radius)); - bool exist_change = false; + // copied from SLAPrint::Steps::support_points() + const SLAPrintObject *po = m_c->selection_info()->print_object(); + const SLAPrintObjectConfig &cfg = po->config(); + float head_diameter = (cfg.support_tree_type == sla::SupportTreeType::Branching) ? + float(cfg.branchingsupport_head_front_diameter): + float(cfg.support_head_front_diameter); // SupportTreeType::Organic + std::string button_title = "apply " + std::to_string(head_diameter); + ImGui::SameLine(); + if (ImGui::Button(button_title.c_str())) { + float density_relative = float(cfg.support_points_density_relative / 100.f); + sample_config = sla::SampleConfigFactory::apply_density( + sla::SampleConfigFactory::create(head_diameter), density_relative); + } + bool exist_change = false; if (float max_for_one = unscale(sample_config.max_length_for_one_support_point); // [in mm] ImGui::InputFloat("One support", &max_for_one, .1f, 1.f, "%.2f mm")) { sample_config.max_length_for_one_support_point = scale_(max_for_one); diff --git a/tests/data/sla_islands/SPE-2714.svg b/tests/data/sla_islands/SPE-2714.svg new file mode 100644 index 0000000000..906981a044 --- /dev/null +++ b/tests/data/sla_islands/SPE-2714.svg @@ -0,0 +1,1536 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/sla_print/sla_supptgen_tests.cpp b/tests/sla_print/sla_supptgen_tests.cpp index 62a7f8572f..93996339ca 100644 --- a/tests/sla_print/sla_supptgen_tests.cpp +++ b/tests/sla_print/sla_supptgen_tests.cpp @@ -630,3 +630,29 @@ TEST_CASE("Disable visualization", "[hide]") CHECK(is_uniform_support_island_visualization_disabled()); } +TEST_CASE("SPE-2714 3DBenchy - Sample island with config", "[SupportIsland]") { + // set_logging_level(5); + SampleConfig cfg{ + /*thin_max_distance*/ 5832568, + /*thick_inner_max_distance*/ 7290710, + /*thick_outline_max_distance*/ 5468032, + /*head_radius*/ 250000, + /*minimal_distance_from_outline*/ 250000, + /*maximal_distance_from_outline*/ 1944189, + /*max_length_for_one_support_point*/ 1869413, + /*max_length_for_two_support_points*/ 7290710, + /*max_length_ratio_for_two_support_points*/ 0.250000000f, + /*thin_max_width*/ 4673532, + /*thick_min_width*/ 4019237, + /*min_part_length*/ 5832568, + /*minimal_move*/ 100000, + /*count_iteration*/ 30, + /*max_align_distance*/ 3645355, + /*simplification_tolerance*/ 50000.000000000007 + //*path*/, "C:/data/temp/islands/spe_2714_<>.svg" // define OPTION_TO_STORE_ISLAND in SampleConfig.hpp + }; + std::string dir = std::string(TEST_DATA_DIR PATH_SEPARATOR) + "sla_islands/"; + ExPolygon island = load_svg(dir + "SPE-2714.svg"); // Bad field creation + SupportIslandPoints points = test_island_sampling(island, cfg); + CHECK(points.size() > 22); // Before fix it not finished +}