Fix for creating wide tiny change.

+ unify runtime by selecting position with smallest source index
This commit is contained in:
Filip Sykala - NTB T15p 2025-02-19 00:43:00 +01:00 committed by Lukas Matena
parent ac2f6304a0
commit cb20051e03
3 changed files with 89 additions and 63 deletions

View File

@ -39,6 +39,8 @@ std::string SupportIslandPoint::to_string(const Type &type)
{Type::thin_part_loop, "thin_part_loop"}, {Type::thin_part_loop, "thin_part_loop"},
{Type::thick_part_outline, "thick_part_outline"}, {Type::thick_part_outline, "thick_part_outline"},
{Type::thick_part_inner, "thick_part_inner"}, {Type::thick_part_inner, "thick_part_inner"},
{Type::bad_shape_for_vd, "bad_shape_for_vd"},
{Type::permanent, "permanent"},
{Type::undefined, "undefined"}}; {Type::undefined, "undefined"}};
auto it = type_to_string.find(type); auto it = type_to_string.find(type);
if (it == type_to_string.end()) if (it == type_to_string.end())

View File

@ -431,10 +431,10 @@ coord_t align_once(
SupportIslandPointPtr &support = supports[i]; SupportIslandPointPtr &support = supports[i];
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH #ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH
if (!sample->can_move()) { // draww freezed support points if (!support->can_move()) { // draww freezed support points
svg.draw(sample->point, color_static_point, config.head_radius); svg.draw(support->point, color_static_point, config.head_radius);
svg.draw_text(sample->point + Point(config.head_radius, 0), svg.draw_text(support->point + Point(config.head_radius, 0),
SupportIslandPoint::to_string(sample->type).c_str(), color_static_point.c_str()); SupportIslandPoint::to_string(support->type).c_str(), color_static_point.c_str());
} }
#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH #endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH
if (!support->can_move()) if (!support->can_move())
@ -488,8 +488,8 @@ coord_t align_once(
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH #ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH
svg.draw(cell_polygon, color_point_cell); svg.draw(cell_polygon, color_point_cell);
svg.draw(*island_cell, color_island_cell_intersection); svg.draw(*island_cell, color_island_cell_intersection);
svg.draw(Line(sample->point, island_cell_center), color_wanted_point, config.head_radius / 5); svg.draw(Line(support->point, island_cell_center), color_wanted_point, config.head_radius / 5);
svg.draw(sample->point, color_old_point, config.head_radius); svg.draw(support->point, color_old_point, config.head_radius);
svg.draw(island_cell_center, color_wanted_point, config.head_radius); // wanted position svg.draw(island_cell_center, color_wanted_point, config.head_radius); // wanted position
#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH #endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH
@ -499,9 +499,10 @@ coord_t align_once(
max_move = act_move; max_move = act_move;
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH #ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH
svg.draw(sample->point, color_new_point, config.head_radius); svg.draw(support->point, color_new_point, config.head_radius);
svg.draw_text(sample->point + Point(config.head_radius, 0), svg.draw_text(support->point + Point(config.head_radius, 0),
SupportIslandPoint::to_string(sample->type).c_str(), color_new_point.c_str()); SupportIslandPoint::to_string(support->type).c_str(), color_new_point.c_str()
);
#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH #endif // SLA_SAMPLE_ISLAND_UTILS_STORE_ALIGN_ONCE_TO_SVG_PATH
} }
@ -640,7 +641,7 @@ using ThinParts = std::vector<ThinPart>;
/// </summary> /// </summary>
struct ThickPart struct ThickPart
{ {
// neighbor from thick part (twin of first end) // neighbor from thick part (twin of end with smallest source line index)
// edge from thin to thick, start.node is inside of thick part // edge from thin to thick, start.node is inside of thick part
const Neighbor* start; const Neighbor* start;
@ -1099,22 +1100,18 @@ void draw(SVG &svg, const Field &field, const ExPolygon& border, bool draw_borde
} }
#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH || SLA_SAMPLE_ISLAND_UTILS_STORE_PENINSULA_FIELD_TO_SVG_PATH #endif // SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH || SLA_SAMPLE_ISLAND_UTILS_STORE_PENINSULA_FIELD_TO_SVG_PATH
// IMPROVE do not use pointers on node but pointers on Neighbor std::map<size_t, WideTinyChanges> create_wide_tiny_changes(const Positions& part_ends, const Lines &lines) {
Field create_thick_field(const ThickPart& part, const Lines &lines, const SampleConfig &config)
{
// store shortening of outline segments
// line index, vector<next line index + 2x shortening points>
std::map<size_t, WideTinyChanges> wide_tiny_changes; std::map<size_t, WideTinyChanges> wide_tiny_changes;
for (const Position &position : part.ends) { // part_ends are already oriented
for (const Position &position : part_ends) {
Point p1, p2; Point p1, p2;
std::tie(p1, p2) = VoronoiGraphUtils::point_on_lines(position, lines); std::tie(p2, p1) = VoronoiGraphUtils::point_on_lines(position, lines);
const VD::edge_type *edge = position.neighbor->edge; const VD::edge_type *edge = position.neighbor->edge;
size_t i1 = edge->cell()->source_index(); size_t i1 = edge->twin()->cell()->source_index();
size_t i2 = edge->twin()->cell()->source_index(); size_t i2 = edge->cell()->source_index();
// function to add sorted change from wide to tiny // add sorted change from wide to tiny
// stored uder line index or line shorten in point b // stored uder line index or line shorten in point b
auto add = [&wide_tiny_changes, &lines](const Point &p1, const Point &p2, size_t i1, size_t i2) {
WideTinyChange change(p1, p2, i2); WideTinyChange change(p1, p2, i2);
auto item = wide_tiny_changes.find(i1); auto item = wide_tiny_changes.find(i1);
if (item == wide_tiny_changes.end()) { if (item == wide_tiny_changes.end()) {
@ -1123,21 +1120,19 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample
WideTinyChange::SortFromAToB pred(lines[i1]); WideTinyChange::SortFromAToB pred(lines[i1]);
VectorUtils::insert_sorted(item->second, change, pred); VectorUtils::insert_sorted(item->second, change, pred);
} }
}; }
return wide_tiny_changes;
}
const Line &l1 = lines[i1]; // IMPROVE do not use pointers on node but pointers on Neighbor
if (VoronoiGraphUtils::is_opposit_direction(edge, l1)) { Field create_thick_field(const ThickPart& part, const Lines &lines, const SampleConfig &config)
// line1 is shorten on side line1.a --> line2 is shorten on side line2.b {
add(p2, p1, i2, i1); // store shortening of outline segments
} else { // line index, vector<next line index + 2x shortening points>
// line1 is shorten on side line1.b std::map<size_t, WideTinyChanges> wide_tiny_changes = create_wide_tiny_changes(part.ends, lines);
add(p1, p2, i1, i2);
}
}
// connection of line on island // connection of line on island
std::map<size_t, size_t> b_connection = std::map<size_t, size_t> b_connection = LineUtils::create_line_connection_over_b(lines);
LineUtils::create_line_connection_over_b(lines);
std::vector<size_t> source_indices; std::vector<size_t> source_indices;
auto inser_point_b = [&lines, &b_connection, &source_indices] auto inser_point_b = [&lines, &b_connection, &source_indices]
@ -2240,6 +2235,27 @@ ThinPart create_only_thin_part(const VoronoiGraph::ExPath &path) {
return ThinPart{*path_center_opt, /*ends*/ {}}; return ThinPart{*path_center_opt, /*ends*/ {}};
} }
const VoronoiGraph::Node::Neighbor *get_smallest_source_index(const Positions& positions){
// do not call with empty positions
assert(!positions.empty());
if (positions.size() == 1)
return positions.front().neighbor;
const VoronoiGraph::Node::Neighbor *smallest = nullptr;
size_t smallest_index = std::numeric_limits<size_t>::max();
for (const Position &position : positions) {
const VD::edge_type *e = position.neighbor->edge;
size_t min_index = std::min(
e->cell()->source_index(),
e->twin()->cell()->source_index());
if (smallest_index > min_index) {
smallest_index = min_index;
smallest = position.neighbor;
}
}
return smallest;
}
std::pair<ThinParts, ThickParts> convert_island_parts_to_thin_thick( std::pair<ThinParts, ThickParts> convert_island_parts_to_thin_thick(
const IslandParts& island_parts, const VoronoiGraph::ExPath &path) const IslandParts& island_parts, const VoronoiGraph::ExPath &path)
{ {
@ -2273,8 +2289,9 @@ std::pair<ThinParts, ThickParts> convert_island_parts_to_thin_thick(
thin_parts.push_back(ThinPart{center, std::move(ends)}); thin_parts.push_back(ThinPart{center, std::move(ends)});
} else { } else {
assert(i.type == IslandPartType::thick); assert(i.type == IslandPartType::thick);
//const Neighbor* start = polygon_index.changes.front().position.neighbor; const Neighbor *start = VoronoiGraphUtils::get_twin(*get_smallest_source_index(ends));
const Neighbor *start = VoronoiGraphUtils::get_twin(*ends.front().neighbor); // NOTE: VD could contain different order of edges each run.
// To unify behavior as a start index is selected edge with smallest index of source line
thick_parts.push_back(ThickPart {start, std::move(ends)}); thick_parts.push_back(ThickPart {start, std::move(ends)});
} }
} }

View File

@ -19,6 +19,7 @@ using namespace Slic3r;
using namespace Slic3r::sla; using namespace Slic3r::sla;
//#define STORE_SAMPLE_INTO_SVG_FILES "C:/data/temp/test_islands/sample_" //#define STORE_SAMPLE_INTO_SVG_FILES "C:/data/temp/test_islands/sample_"
//#define STORE_ISLAND_ISSUES "C:/data/temp/issues/"
TEST_CASE("Overhanging point should be supported", "[SupGen]") { TEST_CASE("Overhanging point should be supported", "[SupGen]") {
@ -69,9 +70,6 @@ TEST_CASE("Overhanging horizontal surface should be supported", "[SupGen]") {
mesh.translate(0., 0., 5.); // lift up mesh.translate(0., 0., 5.); // lift up
// mesh.WriteOBJFile("Cuboid.obj"); // mesh.WriteOBJFile("Cuboid.obj");
sla::SupportPoints pts = calc_support_pts(mesh); sla::SupportPoints pts = calc_support_pts(mesh);
double mm2 = width * depth;
REQUIRE(!pts.empty()); REQUIRE(!pts.empty());
} }
@ -325,7 +323,7 @@ ExPolygon load_svg(const std::string& svg_filepath) {
auto to_polygon = [](NSVGpath *path) { auto to_polygon = [](NSVGpath *path) {
Polygon r; Polygon r;
r.points.reserve(path->npts); r.points.reserve(path->npts);
for (size_t i = 0; i < path->npts; i++) for (int i = 0; i < path->npts; i++)
r.points.push_back(Point(path->pts[2 * i], path->pts[2 * i + 1])); r.points.push_back(Point(path->pts[2 * i], path->pts[2 * i + 1]));
return r; return r;
}; };
@ -455,10 +453,9 @@ SupportIslandPoints test_island_sampling(const ExPolygon & island,
auto points = uniform_support_island(island, {}, config); auto points = uniform_support_island(island, {}, config);
Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer
bool is_ok = true; bool is_island_supported = true; // Check rasterized island points that exist support point in max_distance
double max_distance = config.thick_inner_max_distance; double max_distance = config.thick_inner_max_distance;
std::vector<double> point_distances(chck_points.size(), std::vector<double> point_distances(chck_points.size(), {max_distance + 1});
{max_distance + 1});
for (size_t index = 0; index < chck_points.size(); ++index) { for (size_t index = 0; index < chck_points.size(); ++index) {
const Point &chck_point = chck_points[index]; const Point &chck_point = chck_points[index];
double &min_distance = point_distances[index]; double &min_distance = point_distances[index];
@ -473,20 +470,26 @@ SupportIslandPoints test_island_sampling(const ExPolygon & island,
if (min_distance > distance) { if (min_distance > distance) {
min_distance = distance; min_distance = distance;
exist_close_support_point = true; exist_close_support_point = true;
};
} }
} }
if (!exist_close_support_point) is_ok = false; }
if (!exist_close_support_point) is_island_supported = false;
} }
if (!is_ok) { // visualize bool is_all_points_inside_island = true;
for (const auto &point : points)
if (!island.contains(point->point))
is_all_points_inside_island = false;
#ifdef STORE_ISLAND_ISSUES
if (!is_island_supported || !is_all_points_inside_island) { // visualize
static int counter = 0; static int counter = 0;
BoundingBox bb; BoundingBox bb;
for (const Point &pt : island.contour.points) bb.merge(pt); for (const Point &pt : island.contour.points) bb.merge(pt);
SVG svg("Error" + std::to_string(++counter) + ".svg", bb); SVG svg(STORE_ISLAND_ISSUES + std::string("Error") + std::to_string(++counter) + ".svg", bb);
svg.draw(island, "blue", 0.5f); svg.draw(island, "blue", 0.5f);
for (auto& p : points) for (auto& p : points)
svg.draw(p->point, "lightgreen", config.head_radius); svg.draw(p->point, island.contains(p->point) ? "lightgreen" : "red", config.head_radius);
for (size_t index = 0; index < chck_points.size(); ++index) { for (size_t index = 0; index < chck_points.size(); ++index) {
const Point &chck_point = chck_points[index]; const Point &chck_point = chck_points[index];
double distance = point_distances[index]; double distance = point_distances[index];
@ -495,12 +498,12 @@ SupportIslandPoints test_island_sampling(const ExPolygon & island,
svg.draw(chck_point, color, config.head_radius / 4); svg.draw(chck_point, color, config.head_radius / 4);
} }
} }
CHECK(!points.empty()); #endif // STORE_ISLAND_ISSUES
// TODO: solve issue
//CHECK(is_ok); CHECK(!points.empty());
CHECK(is_all_points_inside_island);
// CHECK(is_island_supported); // TODO: skip special cases with one point and 2 points
// all points must be inside of island
for (const auto &point : points) { CHECK(island.contains(point->point)); }
return points; return points;
} }
@ -557,6 +560,7 @@ void store_sample(const SupportIslandPoints &samples, const ExPolygon &island) {
/// </summary> /// </summary>
TEST_CASE("Uniform sample test islands", "[SupGen], [VoronoiSkeleton]") TEST_CASE("Uniform sample test islands", "[SupGen], [VoronoiSkeleton]")
{ {
//set_logging_level(5);
float head_diameter = .4f; float head_diameter = .4f;
SampleConfig cfg = SampleConfigFactory::create(head_diameter); SampleConfig cfg = SampleConfigFactory::create(head_diameter);
//cfg.path = "C:/data/temp/islands/<<order>>.svg"; //cfg.path = "C:/data/temp/islands/<<order>>.svg";
@ -589,6 +593,9 @@ TEST_CASE("Disable visualization", "[hide]")
#ifdef STORE_SAMPLE_INTO_SVG_FILES #ifdef STORE_SAMPLE_INTO_SVG_FILES
CHECK(false); CHECK(false);
#endif // STORE_SAMPLE_INTO_SVG_FILES #endif // STORE_SAMPLE_INTO_SVG_FILES
#ifdef STORE_ISLAND_ISSUES
CHECK(false);
#endif // STORE_ISLAND_ISSUES
CHECK(is_uniform_support_island_visualization_disabled()); CHECK(is_uniform_support_island_visualization_disabled());
} }