mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-31 00:51:59 +08:00
Merge branch 'fs_SPE-2716_2714'
This commit is contained in:
commit
f73d74146e
@ -40,6 +40,7 @@
|
|||||||
//#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGNED_TO_SVG_PATH "C:/data/temp/align/island_<<COUNTER>>_aligned.svg"
|
//#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGNED_TO_SVG_PATH "C:/data/temp/align/island_<<COUNTER>>_aligned.svg"
|
||||||
//#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH "C:/data/temp/align_once/iter_<<COUNTER>>.svg"
|
//#define SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH "C:/data/temp/align_once/iter_<<COUNTER>>.svg"
|
||||||
//#define SLA_SAMPLE_ISLAND_UTILS_DEBUG_CELL_DISTANCE_PATH "C:/data/temp/island_cell.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<<COUNTER>>.svg"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
@ -1843,14 +1844,24 @@ void merge_island_parts(IslandParts &island_parts, size_t index, size_t remove_i
|
|||||||
/// <param name="remove_index">Index into island parts to merge</param>
|
/// <param name="remove_index">Index into island parts to merge</param>
|
||||||
/// <param name="process">Queue of future processing</param>
|
/// <param name="process">Queue of future processing</param>
|
||||||
void merge_parts_and_fix_process(IslandParts &island_parts,
|
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) 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
|
// Merged parts should be the same state, it is essential for alhorithm
|
||||||
// Only first island part changes its type, but only before first change
|
// 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;
|
island_parts[index].sum_lengths += island_parts[remove_index].sum_lengths;
|
||||||
merge_island_parts(island_parts, index, remove_index);
|
merge_island_parts(island_parts, index, remove_index);
|
||||||
|
|
||||||
@ -2166,7 +2177,11 @@ std::pair<size_t, std::vector<size_t>> merge_negihbor(IslandParts &island_parts,
|
|||||||
// collect changes from neighbors for result part + indices of neighbor parts
|
// collect changes from neighbors for result part + indices of neighbor parts
|
||||||
IslandPartChanges modified_changes;
|
IslandPartChanges modified_changes;
|
||||||
for (const IslandPartChange &change : 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
|
// iterate neighbor changes and collect only changes to other neighbors
|
||||||
for (const IslandPartChange &n_change : island_parts[change.part_index].changes) {
|
for (const IslandPartChange &n_change : island_parts[change.part_index].changes) {
|
||||||
if (n_change.part_index == index)
|
if (n_change.part_index == index)
|
||||||
@ -2183,10 +2198,7 @@ std::pair<size_t, std::vector<size_t>> merge_negihbor(IslandParts &island_parts,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Modified index is smallest from index or remove_indices
|
// Set modified index to smallest from index or remove_indices(are sorted)
|
||||||
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());
|
|
||||||
size_t modified_index = index;
|
size_t modified_index = index;
|
||||||
if (remove_indices.front() < index) {
|
if (remove_indices.front() < index) {
|
||||||
std::swap(remove_indices.front(), modified_index);
|
std::swap(remove_indices.front(), modified_index);
|
||||||
@ -2330,6 +2342,163 @@ std::pair<ThinParts, ThickParts> convert_island_parts_to_thin_thick(
|
|||||||
return result;
|
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, "<<COUNTER>>", 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<const Neighbor *> 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<coord_t>();
|
||||||
|
|
||||||
|
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<coord_t>(), "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<const Neighbor *> done = queue_done; // copy queue
|
||||||
|
std::function<void(const Neighbor *)> 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
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Separate thin(narrow) and thick(wide) part of island
|
/// Separate thin(narrow) and thick(wide) part of island
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -2357,28 +2526,31 @@ std::pair<ThinParts, ThickParts> separate_thin_thick(
|
|||||||
ProcessItem item = {/*prev_node*/ nullptr, start_node, 0}; // current processing item
|
ProcessItem item = {/*prev_node*/ nullptr, start_node, 0}; // current processing item
|
||||||
ProcessItems process; // queue of nodes to process
|
ProcessItems process; // queue of nodes to process
|
||||||
do { // iterate over all nodes in graph and collect interfaces into island_parts
|
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<size_t>::max()};
|
ProcessItem next_item = {nullptr, nullptr, std::numeric_limits<size_t>::max()};
|
||||||
for (const Neighbor &neighbor: item.node->neighbors) {
|
for (const Neighbor &neighbor: item.node->neighbors) {
|
||||||
if (neighbor.node == item.prev_node) continue; // already done
|
if (neighbor.node == item.prev_node) continue; // already done
|
||||||
if (next_item.node != nullptr) // already prepared item is stored into queue
|
if (next_item.node != nullptr) { // already prepared item is stored into queue
|
||||||
process.push_back(next_item);
|
process.push_back(next_item);
|
||||||
|
next_item.node = nullptr;
|
||||||
size_t next_part_index = detect_interface(island_parts, item.i, &neighbor, lines, config);
|
}
|
||||||
next_item = ProcessItem{item.node, neighbor.node, next_part_index};
|
|
||||||
|
|
||||||
// exist loop back?
|
// exist loop back?
|
||||||
auto is_oposit_item = [&next_item](const ProcessItem &p) {
|
auto is_oposit_item = [prev_node = item.node, node = neighbor.node](const ProcessItem &p) {
|
||||||
return p.node == next_item.prev_node && p.prev_node == next_item.node;};
|
return p.node == prev_node && p.prev_node == node;};
|
||||||
if (auto process_it = std::find_if(process.begin(), process.end(), is_oposit_item);
|
if (auto process_it = std::find_if(process.begin(), process.end(), is_oposit_item);
|
||||||
process_it != process.end()) {
|
process_it != process.end()) {
|
||||||
// solve loop back
|
// 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
|
// branch is already processed
|
||||||
process.erase(process_it);
|
process.erase(process_it);
|
||||||
next_item.node = nullptr; // do not use item as next one
|
|
||||||
continue;
|
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
|
// Select next node to process
|
||||||
if (next_item.node != nullptr) {
|
if (next_item.node != nullptr) {
|
||||||
@ -2390,13 +2562,19 @@ std::pair<ThinParts, ThickParts> separate_thin_thick(
|
|||||||
process.pop_back();
|
process.pop_back();
|
||||||
}
|
}
|
||||||
} while (item.node != nullptr); // loop should end by break with empty process
|
} 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);
|
merge_middle_parts_into_biggest_neighbor(island_parts);
|
||||||
if (island_parts.size() != 1)
|
if (island_parts.size() != 1)
|
||||||
merge_same_neighbor_type_parts(island_parts);
|
merge_same_neighbor_type_parts(island_parts);
|
||||||
if (island_parts.size() != 1)
|
if (island_parts.size() != 1)
|
||||||
merge_short_parts(island_parts, config.min_part_length);
|
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);
|
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
|
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGNED_TO_SVG_PATH
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef SLA_SAMPLE_ISLAND_UTILS_DEBUG_PARTS_PATH
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1320,7 +1320,7 @@ void VoronoiGraphUtils::draw(SVG & svg,
|
|||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << prefix << std::hex << reinterpret_cast<intptr_t>(addr);
|
ss << prefix << std::hex << reinterpret_cast<intptr_t>(addr);
|
||||||
std::string s = ss.str();
|
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)
|
"yellowgreen", // on way to thin (max is above thin)
|
||||||
"limegreen", // between (inside histerezis)
|
"limegreen", // between (inside histerezis)
|
||||||
"forestgreen", // on way to thick (min is belove thick)
|
"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) {
|
std::vector<std::string> color_description{
|
||||||
if (n.min_width() > config.thin_max_width){
|
"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<coord_t>(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];
|
return skeleton_colors[4];
|
||||||
} else if (n.max_width() < config.thick_min_width){
|
} else if (max_width <= min_limit){
|
||||||
return skeleton_colors[0];
|
return skeleton_colors[0];
|
||||||
} else if (n.min_width() < config.thin_max_width &&
|
} else if (min_width >= min_limit &&
|
||||||
n.max_width() > config.thick_min_width){
|
max_width <= max_limit){
|
||||||
return skeleton_colors[2];
|
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];
|
return skeleton_colors[1];
|
||||||
} else if (n.max_width() > config.thin_max_width) {
|
} else if (max_width >= max_limit) {
|
||||||
return skeleton_colors[3];
|
return skeleton_colors[3];
|
||||||
}
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
return "gray";
|
return "gray";
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const auto &[key, value] : graph.data) {
|
for (const auto &[key, value] : graph.data) {
|
||||||
Point p(key->x(), key->y());
|
Point p(key->x(), key->y());
|
||||||
svg.draw(p, "lightgray", width);
|
svg.draw(p, "lightgray", width);
|
||||||
@ -1357,17 +1388,16 @@ void VoronoiGraphUtils::draw(SVG & svg,
|
|||||||
Point to = to_point(n.edge->vertex1());
|
Point to = to_point(n.edge->vertex1());
|
||||||
bool is_second = n.edge->vertex0() > n.edge->vertex1();
|
bool is_second = n.edge->vertex0() > n.edge->vertex1();
|
||||||
Point center = (from + to) / 2;
|
Point center = (from + to) / 2;
|
||||||
Point p = center + ((is_second) ? Point(0., -2e6) :
|
Point p = center + ((is_second) ? Point(0., -2e5) : Point(0., 2e5));
|
||||||
Point(0., 2e6));
|
|
||||||
print_address(p, "neighbor ptr ", (void *) &n, "gray");
|
print_address(p, "neighbor ptr ", (void *) &n, "gray");
|
||||||
if (is_second) continue;
|
if (is_second) continue;
|
||||||
const char *color = get_color(n);
|
const char *color = get_color(n);
|
||||||
if (pointer_caption) {
|
if (pointer_caption) {
|
||||||
std::string width_str = "width min=" + std::to_string(n.min_width()) +
|
std::string width_str = "width min=" + std::to_string(n.min_width()) +
|
||||||
" max=" + std::to_string(n.max_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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -719,12 +719,12 @@ std::optional<SmallPart> create_small_part(
|
|||||||
/// Recursive fast check function without storing any already searched data
|
/// Recursive fast check function without storing any already searched data
|
||||||
/// NOTE: Small part with holes could slow down checking
|
/// NOTE: Small part with holes could slow down checking
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="check_layer_i">Index of layer with part</param>
|
/// <param name="check">Index of layer and part</param>
|
||||||
/// <param name="check_part_i">Index of part in layer.parts</param>
|
|
||||||
/// <param name="allowed_depth">Recursion protection</param>
|
/// <param name="allowed_depth">Recursion protection</param>
|
||||||
std::function<bool(const LayerPartIndex &, size_t)> check_parts;
|
/// <param name="prev_check">To prevent cycling calls</param>
|
||||||
|
std::function<bool(const LayerPartIndex &, size_t, const LayerPartIndex &)> check_parts;
|
||||||
check_parts = [&range_bb, &check_parts, &layers, &island, radius_in_mm]
|
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 Layer &check_layer = layers[check.layer_index];
|
||||||
const LayerPart &check_part = check_layer.parts[check.part_index];
|
const LayerPart &check_part = check_layer.parts[check.part_index];
|
||||||
for (const PartLink &link : check_part.next_parts)
|
for (const PartLink &link : check_part.next_parts)
|
||||||
@ -733,6 +733,9 @@ std::optional<SmallPart> create_small_part(
|
|||||||
!range_bb.contains(link->shape_extent.max))
|
!range_bb.contains(link->shape_extent.max))
|
||||||
return false; // part is too large
|
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;
|
--allowed_depth;
|
||||||
if (allowed_depth == 0)
|
if (allowed_depth == 0)
|
||||||
return true; // break recursion
|
return true; // break recursion
|
||||||
@ -741,7 +744,10 @@ std::optional<SmallPart> create_small_part(
|
|||||||
size_t next_layer_i = check.layer_index + 1;
|
size_t next_layer_i = check.layer_index + 1;
|
||||||
for (const PartLink& link : check_part.next_parts) {
|
for (const PartLink& link : check_part.next_parts) {
|
||||||
size_t next_part_i = link - layers[next_layer_i].parts.cbegin();
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,12 +759,7 @@ std::optional<SmallPart> create_small_part(
|
|||||||
// Investigate only the first island(lower index into parts).
|
// Investigate only the first island(lower index into parts).
|
||||||
if (check.part_index < island.part_index)
|
if (check.part_index < island.part_index)
|
||||||
return false; // part is already checked
|
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
|
// NOTE: multiple investigation same part seems more relevant
|
||||||
// instead of store and search for already checked parts
|
// instead of store and search for already checked parts
|
||||||
@ -774,20 +775,28 @@ std::optional<SmallPart> create_small_part(
|
|||||||
for (const PartLink &link : check_part.prev_parts) {
|
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_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();
|
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
|
// 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 false;
|
||||||
}
|
}
|
||||||
return true;
|
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);
|
assert(layer_height > 0.f);
|
||||||
float safe_multiplicator = 1.3f; // 30% more layers than radius for zigzag(up & down layer) search
|
float safe_multiplicator = 1.4f; // 40% more layers than radius for zigzag(up & down layer) search
|
||||||
size_t allowed_depth = static_cast<size_t>(std::ceil(radius_in_mm / layer_height * safe_multiplicator));
|
size_t allowed_depth = static_cast<size_t>(std::ceil((radius_in_mm / layer_height + 1) * safe_multiplicator));
|
||||||
// Check Bounding boxes and do not create any data - FAST CHECK
|
// Check Bounding boxes and do not create any data - FAST CHECK
|
||||||
// NOTE: it could check model parts multiple times those there is allowed_depth
|
// 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 {};
|
return {};
|
||||||
|
|
||||||
SmallPart collected; // sorted by layer_i, part_i
|
SmallPart collected; // sorted by layer_i, part_i
|
||||||
|
@ -885,8 +885,21 @@ void GLGizmoSlaSupports::draw_island_config() {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("head radius %.2f mm", unscale<float>(sample_config.head_radius));
|
ImGui::Text("head radius %.2f mm", unscale<float>(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<float>(sample_config.max_length_for_one_support_point); // [in mm]
|
if (float max_for_one = unscale<float>(sample_config.max_length_for_one_support_point); // [in mm]
|
||||||
ImGui::InputFloat("One support", &max_for_one, .1f, 1.f, "%.2f 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);
|
sample_config.max_length_for_one_support_point = scale_(max_for_one);
|
||||||
|
1536
tests/data/sla_islands/SPE-2714.svg
Normal file
1536
tests/data/sla_islands/SPE-2714.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 232 KiB |
@ -630,3 +630,29 @@ TEST_CASE("Disable visualization", "[hide]")
|
|||||||
CHECK(is_uniform_support_island_visualization_disabled());
|
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_<<order>>.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
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user