Continue cleaning code

After thin&thic must be at least 2 support points
This commit is contained in:
Filip Sykala - NTB T15p 2024-11-25 12:15:24 +01:00 committed by Lukas Matena
parent b84eb1bc66
commit 3daed2f02d
9 changed files with 378 additions and 446 deletions

View File

@ -16,7 +16,6 @@ struct SampleConfig
// Every point on island has at least one support point in maximum distance
// MUST be bigger than zero
coord_t max_distance = static_cast<coord_t>(scale_(5.));
coord_t half_distance = static_cast<coord_t>(scale_(2.5)); // has to be half od max_distance
// Support point head radius
// MUST be bigger than zero
@ -31,25 +30,18 @@ struct SampleConfig
// Must be bigger than minimal_distance_from_outline
coord_t maximal_distance_from_outline = static_cast<coord_t>(scale_(1.));// [nano meter]
// When angle on outline is smaller than max_interesting_angle
// than create unmovable support point.
// Should be in range from Pi/2 to Pi
double max_interesting_angle = 2. / 3. * M_PI; // [radians]
// Distinguish when to add support point on VD outline point(center line sample)
// MUST be bigger than minimal_distance_from_outline
coord_t minimal_support_distance = 0;
// minimal length of side branch to be sampled
// it is used for sampling in center only
coord_t min_side_branch_length = 0;
// Maximal length of longest path in voronoi diagram to be island
// supported only by one single support point this point will be in center of path.
coord_t max_length_for_one_support_point = static_cast<coord_t>(scale_(1.));
// Maximal length of island supported by 2 points
coord_t max_length_for_two_support_points = static_cast<coord_t>(scale_(1.));
// Maximal ratio of path length for island supported by 2 points
// Used only in case when maximal_distance_from_outline is bigger than
// current island longest_path * this ratio
// Note: Preven for tiny island to contain overlapped support points
// must be smaller than 0.5 and bigger than zero
float max_length_ratio_for_two_support_points = 0.25; // |--25%--Sup----50%----Sup--25%--|
// Maximal width of line island supported in the middle of line
// Must be greater or equal to min_width_for_outline_support
@ -71,7 +63,7 @@ struct SampleConfig
// Sample outline of Field by this value
// Less than max_distance
coord_t outline_sample_distance = 2;
coord_t outline_sample_distance = static_cast<coord_t>(scale_(5 *3/4.));
// Maximal distance over Voronoi diagram edges to find closest point during aligning Support point
coord_t max_align_distance = 0; // [scaled mm -> nanometers]

View File

@ -62,15 +62,10 @@ SampleConfig SampleConfigFactory::create(float support_head_diameter_in_mm)
// TODO: find valid params !!!!
SampleConfig result;
result.max_distance = max_distance;
result.half_distance = result.max_distance / 2;
result.head_radius = head_diameter / 2;
result.minimal_distance_from_outline = result.head_radius;
result.maximal_distance_from_outline = result.max_distance/3;
assert(result.minimal_distance_from_outline < result.maximal_distance_from_outline);
result.minimal_support_distance = result.minimal_distance_from_outline +
result.half_distance;
result.min_side_branch_length = 2 * result.minimal_distance_from_outline + result.max_distance/4;
result.max_length_for_one_support_point =
max_distance / 3 +
2 * result.minimal_distance_from_outline +

View File

@ -10,28 +10,12 @@ SupportIslandPoint::SupportIslandPoint(Slic3r::Point point, Type type)
bool SupportIslandPoint::can_move(const Type &type)
{
// use shorter list
/*
static const std::set<Type> can_move({
Type::center_line,
Type::center_circle,
Type::center_circle_end,
Type::center_circle_end2,
Type::outline,
Type::inner
});
return can_move.find(type) != can_move.end();
/*/ // switch comment center
static const std::set<Type> cant_move({
Type::one_bb_center_point,
Type::one_center_point,
Type::two_points,
Type::center_line_end,
Type::center_line_end2,
Type::center_line_end3,
Type::center_line_start
});
return cant_move.find(type) == cant_move.end();
//*/
}
bool SupportIslandPoint::can_move() const { return can_move(type); }
@ -45,26 +29,20 @@ coord_t SupportIslandPoint::move(const Point &destination)
std::string SupportIslandPoint::to_string(const Type &type)
{
static const std::string undefined = "UNDEFINED";
static std::map<Type, std::string> type_to_string=
{{Type::one_center_point, "one_center_point"},
{Type::two_points,"two_points"},
{Type::center_line, "center_line"},
{Type::center_line1, "center_line1"},
{Type::center_line2, "center_line2"},
{Type::center_line3, "center_line3"},
{Type::center_line_end, "center_line_end"},
{Type::center_line_end2, "center_line_end2"},
{Type::center_line_end3, "center_line_end3"},
{Type::center_line_start, "center_line_start"},
{Type::center_circle, "center_circle"},
{Type::center_circle_end, "center_circle_end"},
{Type::center_circle_end2, "center_circle_end2"},
{Type::outline, "outline"},
{Type::inner, "inner"},
{Type::undefined, "undefined"}};
{{Type::one_center_point, "one_center_point"},
{Type::two_points, "two_points"},
{Type::two_points_backup, "two_points_backup"},
{Type::one_bb_center_point,"one_bb_center_point"},
{Type::thin_part, "thin_part"},
{Type::thin_part_change, "thin_part_change"},
{Type::thin_part_loop, "thin_part_loop"},
{Type::thick_part_outline, "thick_part_outline"},
{Type::thick_part_inner, "thick_part_inner"},
{Type::undefined, "undefined"}};
auto it = type_to_string.find(type);
if (it == type_to_string.end()) return undefined;
if (it == type_to_string.end())
return to_string(Type::undefined);
return it->second;
}

View File

@ -16,25 +16,15 @@ class SupportIslandPoint
{
public:
enum class Type: unsigned char {
one_center_point,
two_points,
center_line,
center_line1, // sample line in center
center_line2, // rest of neighbor edge before position of Field start
center_line3, // end of loop, next neighbors are already sampled
center_line_end, // end of branch
center_line_end2, // start of main path(only one per VD)
center_line_end3, // end in continous sampling
center_line_start, // first sample
center_circle,
center_circle_end, // circle finish by one point (one end per circle -
// need allign)
center_circle_end2, // circle finish by multi points (one end per
// circle - need allign)
outline, // keep position align with island outline
inner, // point inside wide part, without restriction on move
one_bb_center_point, // for island smaller than head radius
one_bb_center_point, // for island smaller than head radius
one_center_point, // small enough to support only by one support point
two_points, // island stretched between two points
two_points_backup, // same as before but forced after divide to thin&thick with small amoutn of points
thin_part, // point on thin part of island lay on VD
thin_part_change, // on the first edge -> together with change to thick part of island
thin_part_loop, // on the last edge -> loop into itself part of island
thick_part_outline, // keep position align with island outline
thick_part_inner, // point inside wide part, without restriction on move
undefined
};
@ -128,7 +118,7 @@ public:
public:
SupportCenterIslandPoint(VoronoiGraph::Position position,
const SampleConfig *configuration,
Type type = Type::center_line);
Type type = Type::thin_part);
bool can_move() const override{ return true; }
coord_t move(const Point &destination) override;
@ -166,7 +156,7 @@ public:
public:
SupportOutlineIslandPoint(Position position,
std::shared_ptr<Restriction> restriction,
Type type = Type::outline);
Type type = Type::thick_part_outline);
// return true
bool can_move() const override;
@ -217,7 +207,7 @@ public:
{
assert(lines.size() == lengths.size());
}
virtual ~Restriction() = default;
virtual std::optional<size_t> next_index(size_t index) const = 0;
virtual std::optional<size_t> prev_index(size_t index) const = 0;
};
@ -299,7 +289,7 @@ class SupportIslandInnerPoint: public SupportIslandPoint
public:
SupportIslandInnerPoint(Point point,
std::shared_ptr<ExPolygon> inner,
Type type = Type::inner);
Type type = Type::thick_part_inner);
bool can_move() const override { return true; };

View File

@ -1,9 +1,11 @@
#include "SampleIslandUtils.hpp"
#include "UniformSupportIsland.hpp"
#include <cmath>
#include <optional>
#include <vector>
#include <optional>
#include <cassert>
#include <memory>
#include <libslic3r/ClipperUtils.hpp> // allign
#include <libslic3r/Geometry.hpp>
@ -28,22 +30,19 @@
// comment definition of NDEBUG to enable assert()
//#define NDEBUG
#define SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH "C:/data/temp/Field_<<COUNTER>>.svg"
//#define SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH "C:/data/temp/Field_<<COUNTER>>.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_DEBUG_CELL_DISTANCE_PATH "C:/data/temp/island_cell.svg"
#include <cassert>
namespace {
using namespace Slic3r;
using namespace Slic3r::sla;
namespace {
// TODO: Move to string utils
/// <summary>
/// Replace first occurence of string
/// TODO: Generalize and Move into string utils
/// </summary>
/// <param name="s"></param>
/// <param name="toReplace"></param>
@ -62,7 +61,6 @@ std::string replace_first(
/// <summary>
/// IMPROVE: use Slic3r::BoundingBox
///
/// Search for reference to an Expolygon with biggest contour
/// </summary>
/// <param name="expolygons">Input</param>
@ -140,7 +138,7 @@ ExPolygon get_simplified(const ExPolygon &island, const SampleConfig &config) {
/// <summary>
/// Transform support point to slicer points
/// </summary>
Slic3r::Points to_points(const SupportIslandPoints &support_points){
Points to_points(const SupportIslandPoints &support_points){
Points result;
result.reserve(support_points.size());
std::transform(support_points.begin(), support_points.end(), std::back_inserter(result),
@ -178,95 +176,8 @@ SVG draw_island_graph(const std::string &path, const ExPolygon &island,
/// Term criteria for align: Minimal sample move and Maximal count of iteration</param>
void align_samples(SupportIslandPoints &samples, const ExPolygon &island, const SampleConfig &config);
/// <summary>
/// Decide how to sample path
/// </summary>
/// <param name="path">Path inside voronoi diagram with side branches and circles</param>
/// <param name="lines">Source lines for VG --> outline of island.</param>
/// <param name="config">Definition how to sample</param>
/// <returns>Support points for island</returns>
static SupportIslandPoints sample_expath(const VoronoiGraph::ExPath &path, const Lines &lines, const SampleConfig &config);
void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radius, bool write_type = true);
} // namespace
SupportIslandPoints SampleIslandUtils::uniform_cover_island(
const ExPolygon &island, const SampleConfig &config
) {
ExPolygon simplified_island = get_simplified(island, config);
#ifdef OPTION_TO_STORE_ISLAND
std::string path;
if (!config.path.empty()) {
static int counter = 0;
path = replace_first(config.path, "<<order>>", std::to_string(++counter));
draw_island(path, island, simplified_island);
// need to save svg in case of infinite loop so no store SVG into variable
}
#endif // OPTION_TO_STORE_ISLAND
// When island is smaller than minimal-head diameter,
// it will be supported whole by support poin in center
if (Point center; get_center(simplified_island.contour.points, config.head_radius, center)) {
SupportIslandPoints result;
result.push_back(std::make_unique<SupportIslandNoMovePoint>(
center, SupportIslandInnerPoint::Type::one_bb_center_point));
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()){ // add center support point into image
SVG svg = draw_island(path, island, simplified_island);
svg.draw(center, "black", config.head_radius);
svg.draw_text(center, "Center of bounding box", "black");
}
return result;
#endif // OPTION_TO_STORE_ISLAND
}
Slic3r::Geometry::VoronoiDiagram vd;
Lines lines = to_lines(simplified_island);
vd.construct_voronoi(lines.begin(), lines.end());
Slic3r::Voronoi::annotate_inside_outside(vd, lines);
VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines);
VoronoiGraph::ExPath longest_path;
const VoronoiGraph::Node *start_node = VoronoiGraphUtils::getFirstContourNode(skeleton);
// every island has to have a point on contour
assert(start_node != nullptr);
longest_path = VoronoiGraphUtils::create_longest_path(start_node);
#ifdef OPTION_TO_STORE_ISLAND // add voronoi diagram with longest path into image
if (!path.empty()) draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config);
#endif // OPTION_TO_STORE_ISLAND
SupportIslandPoints samples = sample_expath(longest_path, lines, config);
#ifdef OPTION_TO_STORE_ISLAND
Points samples_before_align = ::to_points(samples);
if (!path.empty()) {
SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config);
draw(svg, samples, config.head_radius);
}
#endif // OPTION_TO_STORE_ISLAND
// allign samples
align_samples(samples, island, config);
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()) {
SVG svg = draw_island(path, island, simplified_island);
coord_t width = config.head_radius / 5;
VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "darkorange");
VoronoiGraphUtils::draw(svg, skeleton, lines, config, false /*print Pointer address*/);
Lines align_moves;
align_moves.reserve(samples.size());
for (size_t i = 0; i < samples.size(); ++i)
align_moves.push_back(Line(samples[i]->point, samples_before_align[i]));
svg.draw(align_moves, "lightgray", width);
draw(svg, samples, config.head_radius);
}
#endif // OPTION_TO_STORE_ISLAND
return samples;
}
namespace {
/// <summary>
/// Create unique static support point
/// </summary>
@ -384,26 +295,20 @@ SupportIslandPointPtr create_middle_path_point(
if (!position_opt.has_value()) return nullptr;
return create_no_move_point(*position_opt, type);
}
} // namespace
#ifndef NDEBUG
namespace {
bool is_points_in_distance(const Slic3r::Point & p,
const Slic3r::Points &points,
bool is_points_in_distance(const Point & p,
const Points &points,
double max_distance)
{
for (const auto &p2 : points) {
double d = (p - p2).cast<double>().norm();
if (d > max_distance)
return false;
}
return true;
return std::all_of(points.begin(), points.end(),
[p, max_distance](const Point &point) {
double d = (p - point).cast<double>().norm();
return d <= max_distance;
});
}
} // namespace
#endif // NDEBUG
// align
namespace {
/// <summary>
/// once align
/// </summary>
@ -422,7 +327,7 @@ coord_t align_once(
// https://stackoverflow.com/questions/23823345/how-to-construct-a-voronoi-diagram-inside-a-polygon
// IMPROVE1: add accessor to point coordinate do not copy points
// IMPROVE2: add filter for create cell polygon only for moveable samples
Slic3r::Points points = to_points(samples);
Points points = to_points(samples);
assert(!has_duplicate_points(points));
Polygons cell_polygons = create_voronoi_cells_cgal(points, config.max_distance);
@ -463,7 +368,7 @@ coord_t align_once(
continue; // do not align point with invalid cell
// IMPROVE: add intersection polygon with expolygon
Polygons intersections = Slic3r::intersection(cell_polygon, island);
Polygons intersections = intersection(cell_polygon, island);
const Polygon *island_cell = nullptr;
if (intersections.size() == 1) {
island_cell = &intersections.front();
@ -559,12 +464,9 @@ void align_samples(SupportIslandPoints &samples, const ExPolygon &island, const
}
} // namespace
/// <summary>
/// Separation of thin and thick part of island
/// </summary>
namespace {
using VD = Slic3r::Geometry::VoronoiDiagram;
using Position = VoronoiGraph::Position;
@ -610,8 +512,11 @@ using ThickParts = std::vector<ThickPart>;
/// <param name="part">One thin part of island</param>
/// <param name="results">[OUTPUT]Set of support points</param>
/// <param name="config">Define density of support points</param>
void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &results, const SampleConfig &config) {
struct SupportIn {
void create_supports_for_thin_part(
const ThinPart &part, SupportIslandPoints &results, const SampleConfig &config
) {
struct SupportIn
{
// want to create support in
coord_t support_in; // [nano meters]
// Neighbor to continue is not sampled yet
@ -620,12 +525,13 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re
using SupportIns = std::vector<SupportIn>;
coord_t support_distance = config.max_distance;
coord_t half_support_distance = support_distance/2;
coord_t half_support_distance = support_distance / 2;
// Current neighbor
SupportIn curr{half_support_distance + part.center.calc_distance(), part.center.neighbor};
const Neighbor *twin_start = VoronoiGraphUtils::get_twin(*curr.neighbor);
coord_t twin_support_in = static_cast<coord_t>(twin_start->length()) - curr.support_in + support_distance;
coord_t twin_support_in = static_cast<coord_t>(twin_start->length()) - curr.support_in +
support_distance;
// Process queue
SupportIns process;
@ -634,24 +540,23 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re
// Loop over thin part of island to create support points on the voronoi skeleton.
while (curr.neighbor != nullptr || !process.empty()) {
if (curr.neighbor == nullptr) { // need to pop next one from process
curr = process.back(); // copy
curr = process.back(); // copy
process.pop_back();
}
auto part_end_it = std::lower_bound(part.ends.begin(), part.ends.end(), curr.neighbor,
[](const Position &end, const Neighbor *n) { return end.neighbor < n; });
[](const Position &end, const Neighbor *n) { return end.neighbor < n; });
bool is_end_neighbor = part_end_it != part.ends.end() &&
curr.neighbor == part_end_it->neighbor;
curr.neighbor == part_end_it->neighbor;
// add support on current neighbor
coord_t edge_length = (is_end_neighbor) ?
part_end_it->calc_distance() :
static_cast<coord_t>(curr.neighbor->length());
coord_t edge_length = (is_end_neighbor) ? part_end_it->calc_distance() :
static_cast<coord_t>(curr.neighbor->length());
while (edge_length >= curr.support_in) {
double ratio = curr.support_in / curr.neighbor->length();
VoronoiGraph::Position position(curr.neighbor, ratio);
results.push_back(std::make_unique<SupportCenterIslandPoint>(
position, &config, SupportIslandPoint::Type::center_line1));
position, &config, SupportIslandPoint::Type::thin_part_change));
curr.support_in += support_distance;
}
curr.support_in -= edge_length;
@ -660,7 +565,7 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re
// on the current neighbor lay part end(transition into neighbor Thick part)
if (curr.support_in < half_support_distance)
results.push_back(std::make_unique<SupportCenterIslandPoint>(
*part_end_it, &config, SupportIslandPoint::Type::center_line1));
*part_end_it, &config, SupportIslandPoint::Type::thin_part));
curr.neighbor = nullptr;
continue;
}
@ -675,12 +580,12 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re
// detect loop on island part
const Neighbor *twin = VoronoiGraphUtils::get_twin(*curr.neighbor);
if (auto process_it = std::find_if(process.begin(), process.end(),
[twin](const SupportIn &p) {return p.neighbor == twin; });
[twin](const SupportIn &p) { return p.neighbor == twin; });
process_it != process.end()) { // self loop detected
if (curr.support_in < half_support_distance){
if (curr.support_in < half_support_distance) {
Position position{curr.neighbor, 1.}; // fine tune position by alignment
results.push_back(std::make_unique<SupportCenterIslandPoint>(
position, &config, SupportIslandPoint::Type::center_line1));
position, &config, SupportIslandPoint::Type::thin_part_loop));
}
process.erase(process_it);
curr.neighbor = nullptr;
@ -769,27 +674,32 @@ outline_offset(const Slic3r::ExPolygon &island, float offset_delta)
const Line &island_line = island_lines[island_line_index];
Vec2d dir1 = LineUtils::direction(island_line).cast<double>();
dir1.normalize();
size_t majorit_axis = (fabs(dir1.x()) > fabs(dir1.y())) ? 0 : 1;
coord_t start1 = island_line.a[majorit_axis];
coord_t end1 = island_line.b[majorit_axis];
if (start1 > end1) std::swap(start1, end1);
for (size_t offset_line_index = 0; offset_line_index < offset_lines.size(); ++offset_line_index) {
const Line &offset_line = offset_lines[offset_line_index];
// check that line overlap its interval
coord_t start2 = offset_line.a[majorit_axis];
coord_t end2 = offset_line.b[majorit_axis];
if (start2 > end2) std::swap(start2, end2);
if (start1 > end2 || start2 > end1) continue; // not overlaped intervals
Vec2d dir2 = LineUtils::direction(offset_line).cast<double>();
dir2.normalize();
double angle = acos(dir1.dot(dir2));
// not similar direction
if (fabs(angle) > angle_tolerace) continue;
double angle = acos(dir1.dot(dir2));
if (fabs(angle) > angle_tolerace) continue; // not similar direction
// Improve: use only one side of offest !!
Point offset_middle = LineUtils::middle(offset_line);
Point island_middle = LineUtils::middle(island_line);
Point diff_middle = offset_middle - island_middle;
if (fabs(diff_middle.x()) > 2 * offset_delta ||
fabs(diff_middle.y()) > 2 * offset_delta)
continue;
double distance = island_line.perp_distance_to(offset_middle);
if (fabs(distance - offset_delta) > distance_tolerance)
continue;
continue; // only parallel line with big distance
// found offseted line
// found first offseted line
converter[island_line_index] = offset_line_index;
break;
}
@ -856,9 +766,12 @@ std::vector<size_t> get_line_indices(const Neighbor* input, const Positions& end
/// <summary>
/// Fix expolygon with hole bigger than contour
/// NOTE: when change contour and index it is neccesary also fix source indices
/// </summary>
/// <param name="shape">In/Out expolygon</param>
bool set_biggest_hole_as_contour(ExPolygon& shape){
/// <param name="shape">[In/Out] expolygon</param>
/// <param name="ids">[OUT] source indices of island contour line creating field</param>
/// <returns>True when contour is changed</returns>
bool set_biggest_hole_as_contour(ExPolygon &shape, std::vector<size_t> &ids) {
Point contour_size = BoundingBox(shape.contour.points).size();
Polygons &holes = shape.holes;
size_t contour_index = holes.size();
@ -873,9 +786,28 @@ bool set_biggest_hole_as_contour(ExPolygon& shape){
return false; // contour is set correctly
// some hole is bigger than contour and become contour
// swap source indices
size_t contour_count = shape.contour.size();
size_t hole_index_offset = contour_count;
for (size_t i = 0; i < contour_index; i++)
hole_index_offset += shape.holes[i].size();
size_t hole_index_end = hole_index_offset + shape.holes[contour_index].size();
// swap contour with hole
Polygon tmp = holes[contour_index]; // copy
std::swap(tmp, shape.contour);
holes[contour_index] = std::move(tmp);
// Temp copy of the old hole(newly contour) indices
std::vector<size_t> contour_indices(ids.begin() + hole_index_offset,
ids.begin() + hole_index_end); // copy
ids.erase(ids.begin() + hole_index_offset, // remove copied contour
ids.begin() + hole_index_end);
ids.insert(ids.begin() + hole_index_offset, // insert old contour(newly hole)
ids.begin(), ids.begin() + contour_count);
ids.erase(ids.begin(), ids.begin() + contour_count); // remove old contour
ids.insert(ids.begin(), contour_indices.begin(), contour_indices.end());
return true;
}
@ -891,9 +823,9 @@ struct Field
// same size as polygon.points.size()
// indexes to source island lines
// in case (index > lines.size()) it means fill the gap from tiny part of island
std::vector<size_t> source_indexes;
std::vector<size_t> source_indices;
// value for source index of change from wide to tiny part of island
size_t source_indexe_for_change;
size_t source_index_for_change;
// inner part of field
ExPolygon inner;
@ -901,7 +833,44 @@ struct Field
std::map<size_t, size_t> field_2_inner;
};
void draw(SVG &svg, const Field &field, bool draw_border_line_indexes = false, bool draw_field_source_indexes = true);
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH
void draw(SVG &svg, const Field &field, bool draw_border_line_indexes = false, bool draw_field_source_indexes = true) {
const char *field_color = "red";
const char *border_line_color = "blue";
const char *inner_line_color = "green";
const char *source_index_text_color = "blue";
svg.draw(field.border, field_color);
Lines border_lines = to_lines(field.border);
LineUtils::draw(svg, border_lines, border_line_color, 0., draw_border_line_indexes);
if (draw_field_source_indexes)
for (auto &line : border_lines) {
size_t index = &line - &border_lines.front();
// start of holes
if (index >= field.source_indices.size())
break;
Point middle_point = LineUtils::middle(line);
std::string text = std::to_string(field.source_indices[index]);
auto item = field.field_2_inner.find(index);
if (item != field.field_2_inner.end()) {
text += " inner " + std::to_string(item->second);
}
svg.draw_text(middle_point, text.c_str(), source_index_text_color);
}
if (field.inner.empty())
return;
// draw inner
Lines inner_lines = to_lines(field.inner);
LineUtils::draw(svg, inner_lines, inner_line_color, 0., draw_border_line_indexes);
if (draw_field_source_indexes)
for (auto &line : inner_lines) {
size_t index = &line - &inner_lines.front();
Point middle_point = LineUtils::middle(line);
std::string text = std::to_string(index);
svg.draw_text(middle_point, text.c_str(), inner_line_color);
}
}
#endif // SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH
// IMPROVE do not use pointers on node but pointers on Neighbor
Field create_thick_field(const ThickPart& part, const Lines &lines, const SampleConfig &config)
@ -943,8 +912,8 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample
std::map<size_t, size_t> b_connection =
LineUtils::create_line_connection_over_b(lines);
std::vector<size_t> source_indexes;
auto inser_point_b = [&lines, &b_connection, &source_indexes]
std::vector<size_t> source_indices;
auto inser_point_b = [&lines, &b_connection, &source_indices]
(size_t &index, Points &points, std::set<size_t> &done)
{
const Line &line = lines[index];
@ -953,10 +922,10 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample
assert(connection_item != b_connection.end());
done.insert(index);
index = connection_item->second;
source_indexes.push_back(index);
source_indices.push_back(index);
};
size_t source_indexe_for_change = lines.size();
size_t source_index_for_change = lines.size();
/// <summary>
/// Insert change into
@ -965,7 +934,7 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample
/// <param name="lines">island(ExPolygon) converted to lines</param>
/// <param name="index"></param> ...
/// <returns>False when change lead to close loop(into first change) otherwise True</returns>
auto insert_changes = [&wide_tiny_changes, &lines, &source_indexes, source_indexe_for_change]
auto insert_changes = [&wide_tiny_changes, &lines, &source_indices, source_index_for_change]
(size_t &index, Points &points, std::set<size_t> &done, size_t input_index)->bool {
auto change_item = wide_tiny_changes.find(index);
while (change_item != wide_tiny_changes.end()) {
@ -996,15 +965,15 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample
if (points.empty() ||
!PointUtils::is_equal(points.back(), change.new_b)) {
points.push_back(change.new_b);
source_indexes.push_back(source_indexe_for_change);
source_indices.push_back(source_index_for_change);
} else {
source_indexes.back() = source_indexe_for_change;
source_indices.back() = source_index_for_change;
}
// prevent double points
if (!PointUtils::is_equal(lines[change.next_line_index].b,
change.next_new_a)) {
points.push_back(change.next_new_a);
source_indexes.push_back(change.next_line_index);
source_indices.push_back(change.next_line_index);
}
done.insert(index);
@ -1052,33 +1021,33 @@ Field create_thick_field(const ThickPart& part, const Lines &lines, const Sample
size_t input_index = std::min(input_index1, input_index2); // Why select min index?
size_t outline_index = input_index;
// Done indexes is used to detect holes in field
std::set<size_t> done_indexes; // IMPROVE: use vector(size of lines count) with bools
std::set<size_t> done_indices; // IMPROVE: use vector(size of lines count) with bools
do {
if (!insert_changes(outline_index, points, done_indexes, input_index))
if (!insert_changes(outline_index, points, done_indices, input_index))
break;
inser_point_b(outline_index, points, done_indexes);
inser_point_b(outline_index, points, done_indices);
} while (outline_index != input_index);
assert(points.size() >= 3);
Field field;
field.border.contour = Polygon(points);
// finding holes(another closed polygon)
if (done_indexes.size() < field_line_indices.size()) {
if (done_indices.size() < field_line_indices.size()) {
for (const size_t &index : field_line_indices) {
if(done_indexes.find(index) != done_indexes.end()) continue;
if(done_indices.find(index) != done_indices.end()) continue;
// new hole
Points hole_points;
size_t hole_index = index;
do {
inser_point_b(hole_index, hole_points, done_indexes);
inser_point_b(hole_index, hole_points, done_indices);
} while (hole_index != index);
field.border.holes.emplace_back(hole_points);
}
// Set largest polygon as contour
set_biggest_hole_as_contour(field.border);
set_biggest_hole_as_contour(field.border, source_indices);
}
field.source_indexe_for_change = source_indexe_for_change;
field.source_indexes = std::move(source_indexes);
field.source_index_for_change = source_index_for_change;
field.source_indices = std::move(source_indices);
std::tie(field.inner, field.field_2_inner) =
outline_offset(field.border, (float)config.minimal_distance_from_outline);
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH
@ -1219,31 +1188,24 @@ Slic3r::Points sample_expolygon_with_centering(const ExPolygon &expoly, coord_t
/// <param name="field">Input field</param>
/// <param name="config">Parameters for sampling.</param>
/// <returns>support for outline</returns>
SupportIslandPoints sample_outline(
const Field &field, const SampleConfig &config)
{
const ExPolygon &border = field.border;
SupportIslandPoints sample_outline(const Field &field, const SampleConfig &config){
const ExPolygon &border = field.border;
const Polygon &contour = border.contour;
assert(field.source_indexes.size() >= contour.size());
assert(field.source_indices.size() >= contour.size());
coord_t max_align_distance = config.max_align_distance;
coord_t sample_distance = config.outline_sample_distance;
SupportIslandPoints result;
using RestrictionPtr = std::shared_ptr<SupportOutlineIslandPoint::Restriction>;
auto add_sample = [&](size_t index, const RestrictionPtr& restriction, coord_t &last_support) {
using Position = SupportOutlineIslandPoint::Position;
const double &line_length_double = restriction->lengths[index];
coord_t line_length = static_cast<coord_t>(std::round(line_length_double));
if (last_support + line_length > sample_distance) {
do {
float ratio = static_cast<float>((sample_distance - last_support) / line_length_double);
result.emplace_back(
std::make_unique<SupportOutlineIslandPoint>(
Position(index, ratio), restriction,
SupportIslandPoint::Type::outline)
);
last_support -= sample_distance;
} while (last_support + line_length > sample_distance);
while (last_support + line_length > sample_distance){
float ratio = static_cast<float>((sample_distance - last_support) / line_length_double);
SupportOutlineIslandPoint::Position position(index, ratio);
result.emplace_back(std::make_unique<SupportOutlineIslandPoint>(
position, restriction, SupportIslandPoint::Type::thick_part_outline));
last_support -= sample_distance;
}
last_support += line_length;
};
@ -1258,14 +1220,12 @@ SupportIslandPoints sample_outline(
sum_lengths += length;
lengths.push_back(length);
}
// no samples on this polygon
using Restriction = SupportOutlineIslandPoint::RestrictionCircleSequence;
auto restriction = std::make_shared<Restriction>(lines, lengths, max_align_distance);
coord_t last_support = std::min(static_cast<coord_t>(sum_lengths), sample_distance) / 2;
for (size_t index = 0; index < lines.size(); ++index) {
for (size_t index = 0; index < lines.size(); ++index)
add_sample(index, restriction, last_support);
}
};
// sample line sequence
@ -1313,13 +1273,12 @@ SupportIslandPoints sample_outline(
}
};
// convert map from field index to inner(line index)
const std::map<size_t, size_t>& field_2_inner = field.field_2_inner;
const size_t& change_index = field.source_indexe_for_change;
auto sample_polygon = [&](const Polygon &polygon,
const Polygon &inner_polygon,
size_t index_offset) {
auto sample_polygon = [&add_circle_sample, &add_lines_samples, &field]
(const Polygon &polygon, const Polygon &inner_polygon, size_t index_offset) {
const std::vector<size_t> &source_indices = field.source_indices;
const size_t& change_index = field.source_index_for_change;
const std::map<size_t, size_t> &field_2_inner = field.field_2_inner;
if (inner_polygon.empty())
return; // nothing to sample
@ -1327,8 +1286,8 @@ SupportIslandPoints sample_outline(
size_t first_change_index = polygon.size();
for (size_t polygon_index = 0; polygon_index < polygon.size(); ++polygon_index) {
size_t index = polygon_index + index_offset;
assert(index < field.source_indexes.size());
size_t source_index = field.source_indexes[index];
assert(index < source_indices.size());
size_t source_index = source_indices[index];
if (source_index == change_index) {
// found change from wide to tiny part
first_change_index = polygon_index;
@ -1353,8 +1312,8 @@ SupportIslandPoints sample_outline(
polygon_index != stop_index; ++polygon_index) {
if (polygon_index == polygon.size()) polygon_index = 0;
size_t index = polygon_index + index_offset;
assert(index < field.source_indexes.size());
size_t source_index = field.source_indexes[index];
assert(index < source_indices.size());
size_t source_index = source_indices[index];
if (source_index == change_index) {
if (inner_first == inner_invalid) continue;
// create Restriction object
@ -1419,7 +1378,7 @@ void create_supports_for_thick_part(const ThickPart &part, SupportIslandPoints &
std::transform(inner_points.begin(), inner_points.end(), std::back_inserter(results),
[&](const Point &point) {
return std::make_unique<SupportIslandInnerPoint>(
point, inner, SupportIslandPoint::Type::inner);
point, inner, SupportIslandPoint::Type::thick_part_inner);
});
}
@ -1463,14 +1422,14 @@ using IslandParts = std::vector<IslandPart>;
/// </summary>
struct ProcessItem {
// previously processed island node
const VoronoiGraph::Node *prev_node;
const VoronoiGraph::Node *prev_node = nullptr;
// current island node to investigate neighbors
const VoronoiGraph::Node *node;
const VoronoiGraph::Node *node = nullptr;
// index of island part stored in island_parts
// NOTE: Can't use reference because of vector reallocation
size_t i;
size_t i = std::numeric_limits<size_t>::max();
};
using ProcessItems = std::vector<ProcessItem>;
@ -2109,7 +2068,7 @@ std::pair<ThinParts, ThickParts> separate_thin_thick(
ProcessItems process; // queue of nodes to process
do { // iterate over all nodes in graph and collect interfaces into island_parts
assert(item.node != nullptr);
ProcessItem next_item{nullptr, nullptr, -1};
ProcessItem next_item = {nullptr, nullptr, std::numeric_limits<size_t>::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
@ -2160,130 +2119,42 @@ std::pair<ThinParts, ThickParts> separate_thin_thick(
/// <param name="max_side_distance">Maximal distance from side</param>
/// <returns>2x Static Support point(lay os sides of path)</returns>
SupportIslandPoints create_side_points(
const VoronoiGraph::Nodes &path,
const Lines& lines,
coord_t width,
coord_t max_side_distance)
const VoronoiGraph::ExPath &path, const Lines& lines, const SampleConfig &config,
SupportIslandPoint::Type type = SupportIslandPoint::Type::two_points)
{
VoronoiGraph::Nodes reverse_path = path; // copy
coord_t max_distance_by_length = static_cast<coord_t>(path.length * config.max_length_ratio_for_two_support_points);
coord_t max_distance = std::min(config.maximal_distance_from_outline, max_distance_by_length);
VoronoiGraph::Nodes reverse_path = path.nodes; // copy
std::reverse(reverse_path.begin(), reverse_path.end());
coord_t side_distance1 = max_side_distance; // copy
coord_t side_distance2 = max_side_distance; // copy
auto pos1 = create_position_on_path(path, lines, width, side_distance1);
coord_t width = 2 * config.head_radius;
coord_t side_distance1 = max_distance; // copy
coord_t side_distance2 = max_distance; // copy
auto pos1 = create_position_on_path(path.nodes, lines, width, side_distance1);
auto pos2 = create_position_on_path(reverse_path, lines, width, side_distance2);
assert(pos1.has_value());
assert(pos2.has_value());
SupportIslandPoint::Type type = SupportIslandPoint::Type::two_points;
SupportIslandPoints result;
SupportIslandPoints result;
result.reserve(2);
result.push_back(create_no_move_point(*pos1, type));
result.push_back(create_no_move_point(*pos2, type));
return result;
}
SupportIslandPoints sample_expath(
const VoronoiGraph::ExPath &path,
const Lines & lines,
const SampleConfig & config)
{
// 1) One support point
if (path.length < config.max_length_for_one_support_point) {
// create only one point in center
SupportIslandPoints result;
result.push_back(create_middle_path_point(
path, SupportIslandPoint::Type::one_center_point));
return result;
}
double max_width = VoronoiGraphUtils::get_max_width(path);
if (max_width < config.max_width_for_center_support_line) {
// 2) Two support points have to stretch island even if haed is not fully under island.
if (path.length < config.max_length_for_two_support_points) {
coord_t max_distance_by_length = static_cast<coord_t>(path.length / 4);
coord_t max_distance = std::min(config.maximal_distance_from_outline, max_distance_by_length);
// Be carefull tiny island could contain overlapped support points
assert(max_distance < (static_cast<coord_t>(path.length / 2) - config.head_radius));
coord_t min_width = 2 * config.head_radius; //minimal_distance_from_outline;
return create_side_points(path.nodes, lines, min_width, config.maximal_distance_from_outline);
}
// othewise sample path
/*CenterLineConfiguration
centerLineConfiguration(path.side_branches, config);
SupportIslandPoints samples = sample_center_line(path, centerLineConfiguration);
samples.front()->type = SupportIslandPoint::Type::center_line_end2;
return samples;*/
}
// TODO: 3) Triangle of points
// eval outline and find three point create almost equilateral triangle
// 4) Thin and thick support
SupportIslandPoints result;
auto [thin, thick] = separate_thin_thick(path, lines, config);
for (const ThinPart &part : thin) create_supports_for_thin_part(part, result, config);
for (const ThickPart &part : thick) create_supports_for_thick_part(part, result, lines, config);
return result;
}
void draw(SVG &svg, const Field &field, bool draw_border_line_indexes, bool draw_field_source_indexes) {
const char *field_color = "red";
const char *border_line_color = "blue";
const char *inner_line_color = "green";
const char *source_index_text_color = "blue";
svg.draw(field.border, field_color);
Lines border_lines = to_lines(field.border);
LineUtils::draw(svg, border_lines, border_line_color, 0., draw_border_line_indexes);
if (draw_field_source_indexes)
for (auto &line : border_lines) {
size_t index = &line - &border_lines.front();
// start of holes
if (index >= field.source_indexes.size())
break;
Point middle_point = LineUtils::middle(line);
std::string text = std::to_string(field.source_indexes[index]);
auto item = field.field_2_inner.find(index);
if (item != field.field_2_inner.end()) {
text += " inner " + std::to_string(item->second);
}
svg.draw_text(middle_point, text.c_str(), source_index_text_color);
}
if (field.inner.empty())
return;
// draw inner
Lines inner_lines = to_lines(field.inner);
LineUtils::draw(svg, inner_lines, inner_line_color, 0., draw_border_line_indexes);
if (draw_field_source_indexes)
for (auto &line : inner_lines) {
size_t index = &line - &inner_lines.front();
Point middle_point = LineUtils::middle(line);
std::string text = std::to_string(index);
svg.draw_text(middle_point, text.c_str(), inner_line_color);
}
}
void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radius, bool write_type) {
const char *color = nullptr;
for (const auto &p : supportIslandPoints) {
switch (p->type) {
case SupportIslandPoint::Type::center_line1:
case SupportIslandPoint::Type::center_line2:
case SupportIslandPoint::Type::center_line3:
case SupportIslandPoint::Type::center_circle:
case SupportIslandPoint::Type::center_circle_end:
case SupportIslandPoint::Type::center_circle_end2:
case SupportIslandPoint::Type::center_line_end:
case SupportIslandPoint::Type::center_line_end2:
case SupportIslandPoint::Type::center_line_end3:
case SupportIslandPoint::Type::center_line_start: color = "lightred"; break;
case SupportIslandPoint::Type::outline: color = "lightblue"; break;
case SupportIslandPoint::Type::inner: color = "lightgreen"; break;
case SupportIslandPoint::Type::thin_part:
case SupportIslandPoint::Type::thin_part_change:
case SupportIslandPoint::Type::thin_part_loop: color = "lightred"; break;
case SupportIslandPoint::Type::thick_part_outline: color = "lightblue"; break;
case SupportIslandPoint::Type::thick_part_inner: color = "lightgreen"; break;
case SupportIslandPoint::Type::one_bb_center_point: color = "red"; break;
case SupportIslandPoint::Type::one_center_point:
case SupportIslandPoint::Type::two_points:
case SupportIslandPoint::Type::two_points_backup:
default: color = "black";
}
svg.draw(p->point, color, radius);
@ -2294,9 +2165,136 @@ void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radi
}
}
}
} // namespace
bool SampleIslandUtils::is_uniform_cover_island_visualization_disabled() {
//////////////////////////////
/// uniform support island ///
//////////////////////////////
namespace Slic3r::sla {
SupportIslandPoints uniform_support_island(const ExPolygon &island, const SampleConfig &config){
ExPolygon simplified_island = get_simplified(island, config);
#ifdef OPTION_TO_STORE_ISLAND
std::string path;
if (!config.path.empty()) {
static int counter = 0;
path = replace_first(config.path, "<<order>>", std::to_string(++counter));
draw_island(path, island, simplified_island);
// need to save svg in case of infinite loop so no store SVG into variable
}
#endif // OPTION_TO_STORE_ISLAND
// 0) When island is smaller than minimal-head diameter,
// it will be supported whole by support poin in center
if (Point center; get_center(simplified_island.contour.points, config.head_radius, center)) {
SupportIslandPoints supports;
supports.push_back(std::make_unique<SupportIslandNoMovePoint>(
center, SupportIslandInnerPoint::Type::one_bb_center_point));
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()){ // add center support point into image
SVG svg = draw_island(path, island, simplified_island);
draw(svg, supports, config.head_radius);
}
#endif // OPTION_TO_STORE_ISLAND
return supports;
}
Slic3r::Geometry::VoronoiDiagram vd;
Lines lines = to_lines(simplified_island);
vd.construct_voronoi(lines.begin(), lines.end());
Slic3r::Voronoi::annotate_inside_outside(vd, lines);
VoronoiGraph skeleton = VoronoiGraphUtils::create_skeleton(vd, lines);
VoronoiGraph::ExPath longest_path;
const VoronoiGraph::Node *start_node = VoronoiGraphUtils::getFirstContourNode(skeleton);
// every island has to have a point on contour
assert(start_node != nullptr);
longest_path = VoronoiGraphUtils::create_longest_path(start_node);
#ifdef OPTION_TO_STORE_ISLAND // add voronoi diagram with longest path into image
if (!path.empty()) draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config);
#endif // OPTION_TO_STORE_ISLAND
// 1) One support point
if (longest_path.length < config.max_length_for_one_support_point) {
// create only one point in center
SupportIslandPoints supports;
supports.push_back(create_middle_path_point(
longest_path, SupportIslandPoint::Type::one_center_point));
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()){
SVG svg = draw_island(path, island, simplified_island);
draw(svg, supports, config.head_radius);
}
#endif // OPTION_TO_STORE_ISLAND
return supports;
}
// 2) Two support points have to stretch island even if haed is not fully under island.
if (VoronoiGraphUtils::get_max_width(longest_path) < config.max_width_for_center_support_line &&
longest_path.length < config.max_length_for_two_support_points) {
SupportIslandPoints supports = create_side_points(longest_path, lines, config);
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()){
SVG svg = draw_island(path, island, simplified_island);
draw(svg, supports, config.head_radius);
}
#endif // OPTION_TO_STORE_ISLAND
return supports;
}
// TODO: 3) Triangle aligned support points
// eval outline and find three point create almost equilateral triangle to stretch island
// 4) Divide island on Thin & Thick part and support by parts
SupportIslandPoints supports;
auto [thin, thick] = separate_thin_thick(longest_path, lines, config);
assert(!thin.empty() || !thick.empty());
for (const ThinPart &part : thin) create_supports_for_thin_part(part, supports, config);
for (const ThickPart &part : thick) create_supports_for_thick_part(part, supports, lines, config);
// At least 2 support points are neccessary after thin/thick sampling heuristic
if (supports.size() <= 2){
SupportIslandInnerPoint::Type type = SupportIslandInnerPoint::Type::two_points_backup;
SupportIslandPoints two_supports = create_side_points(longest_path, lines, config, type);
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()) {
SVG svg = draw_island(path, island, simplified_island);
draw(svg, two_supports, config.head_radius);
}
#endif // OPTION_TO_STORE_ISLAND
return two_supports;
}
#ifdef OPTION_TO_STORE_ISLAND
Points supports_before_align = ::to_points(supports);
if (!path.empty()) {
SVG svg = draw_island_graph(path, island, simplified_island, skeleton, longest_path, lines, config);
draw(svg, supports, config.head_radius);
}
#endif // OPTION_TO_STORE_ISLAND
// allign samples
align_samples(supports, island, config);
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()) {
SVG svg = draw_island(path, island, simplified_island);
coord_t width = config.head_radius / 5;
VoronoiGraphUtils::draw(svg, longest_path.nodes, width, "darkorange");
VoronoiGraphUtils::draw(svg, skeleton, lines, config, false /*print Pointer address*/);
Lines align_moves;
align_moves.reserve(supports.size());
for (size_t i = 0; i < supports.size(); ++i)
align_moves.push_back(Line(supports[i]->point, supports_before_align[i]));
svg.draw(align_moves, "lightgray", width);
draw(svg, supports, config.head_radius);
}
#endif // OPTION_TO_STORE_ISLAND
return supports;
}
bool is_uniform_support_island_visualization_disabled() {
#ifndef NDEBUG
return false;
#endif
@ -2311,3 +2309,5 @@ bool SampleIslandUtils::is_uniform_cover_island_visualization_disabled() {
#endif
return true;
}
} // namespace Slic3r::sla

View File

@ -1,5 +1,5 @@
#ifndef slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
#define slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
#ifndef slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_
#define slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_
#include <libslic3r/ExPolygon.hpp>
#include "SampleConfig.hpp"
@ -8,26 +8,17 @@
namespace Slic3r::sla {
/// <summary>
/// Utils class with only static function
/// Function for sampling island by Voronoi Graph.
/// Distribute support points across island area defined by ExPolygon.
/// </summary>
class SampleIslandUtils
{
public:
SampleIslandUtils() = delete;
/// <param name="island">Shape of island</param>
/// <param name="config">Configuration of support density</param>
/// <returns>Support points laying inside of island</returns>
SupportIslandPoints uniform_support_island(const ExPolygon &island, const SampleConfig &config);
/// <summary>
/// Main entry for sample island
/// </summary>
/// <param name="island">Shape of island</param>
/// <param name="config">Configuration for sampler</param>
/// <returns>List of support points</returns>
static SupportIslandPoints uniform_cover_island(
const ExPolygon &island, const SampleConfig &config);
static bool is_uniform_cover_island_visualization_disabled();
};
/// <summary>
/// Check for tests that developer do not forget disable visualization after debuging.
/// </summary>
bool is_uniform_support_island_visualization_disabled();
} // namespace Slic3r::sla
#endif // slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
#endif // slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_

View File

@ -8,9 +8,10 @@
#include "libslic3r/Execution/ExecutionTBB.hpp" // parallel preparation of data for sampling
#include "libslic3r/Execution/Execution.hpp"
#include "libslic3r/KDTreeIndirect.hpp"
#include "libslic3r/ClipperUtils.hpp"
// SupportIslands
#include "libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp"
#include "libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp"
using namespace Slic3r;
using namespace Slic3r::sla;
@ -145,7 +146,7 @@ private:
/// <param name="cnt">Circle center point</param>
/// <param name="r2">squared value of Circle Radius (r2 = r*r)</param>
/// <returns>Intersection point</returns>
Point intersection(const Point &p1, const Point &p2, const Point &cnt, double r2) {
Point intersection_line_circle(const Point &p1, const Point &p2, const Point &cnt, double r2) {
// Vector from p1 to p2
Vec2d dp_d((p2 - p1).cast<double>());
// Vector from circle center to p1
@ -269,7 +270,7 @@ void support_part_overhangs(
/// <param name="cfg"></param>
void support_island(const LayerPart &part, NearPoints& near_points, float part_z,
const SupportPointGeneratorConfig &cfg) {
SupportIslandPoints samples = SampleIslandUtils::uniform_cover_island(*part.shape, cfg.island_configuration);
SupportIslandPoints samples = uniform_support_island(*part.shape, cfg.island_configuration);
//samples = {std::make_unique<SupportIslandPoint>(island.contour.centroid())};
for (const SupportIslandPointPtr &sample : samples)
near_points.add(LayerSupportPoint{
@ -337,7 +338,7 @@ Slic3r::Points sample(Points::const_iterator b, Points::const_iterator e, double
while (p_dist2 > dist2) { // line segment goes out of radius
if (prev_pt == nullptr)
prev_pt = &(*it);
r.push_back(intersection(*prev_pt, pt, r.back(), dist2));
r.push_back(intersection_line_circle(*prev_pt, pt, r.back(), dist2));
p_dist2 = (r.back() - pt).cast<double>().squaredNorm();
prev_pt = &r.back();
}

View File

@ -838,11 +838,9 @@ void GLGizmoSlaSupports::draw_island_config() {
if (float max_distance = unscale<float>(sample_config.max_distance); // [in mm]
ImGui::InputFloat("Max dist", &max_distance, .1f, 1.f, "%.2f mm")) {
sample_config.max_distance = scale_(max_distance);
sample_config.half_distance = sample_config.max_distance / 2;
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Every support point on island has at least one support point in maximum distance\nMUST be bigger than zero");
ImGui::SameLine(); ImGui::Text("half is %.2f", unscale<float>(sample_config.half_distance));
if (float minimal_distance_from_outline = unscale<float>(sample_config.minimal_distance_from_outline); // [in mm]
ImGui::InputFloat("from outline", &minimal_distance_from_outline, .1f, 1.f, "%.2f mm")) {
sample_config.minimal_distance_from_outline = scale_(minimal_distance_from_outline);
@ -856,20 +854,6 @@ void GLGizmoSlaSupports::draw_island_config() {
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Measured as sum of VD edge length from outline\nUsed only when there is no space for outline offset on first/last point\nMust be bigger than minimal_distance_from_outline");
ImGui::Text("max_interesting_angle is %.0f", float(sample_config.max_interesting_angle*180/M_PI));
if (ImGui::IsItemHovered()) ImGui::SetTooltip(" When angle on outline is smaller than max_interesting_angle\nthan create unmovable support point.\nShould be in range from 90 to 180");
if (float minimal_support_distance = unscale<float>(sample_config.minimal_support_distance); // [in mm]
ImGui::InputFloat("Thin dist", &minimal_support_distance, .1f, 1.f, "%.2f mm")) {
sample_config.minimal_support_distance = scale_(minimal_support_distance);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Distinguish when to add support point on Voronoi Diagram\nMUST be bigger than minimal_distance_from_outline\nSmaller -> more supports AND Larger -> less amount");
if (float min_side_branch_length = unscale<float>(sample_config.min_side_branch_length); // [in mm]
ImGui::InputFloat("min_side_branch_length", &min_side_branch_length, .1f, 1.f, "%.2f mm")) {
sample_config.min_side_branch_length = scale_(min_side_branch_length);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("minimal length of side branch to be sampled\nit is used for sampling in center only");
if (float max_for_one = unscale<float>(sample_config.max_length_for_one_support_point); // [in mm]
ImGui::InputFloat("Max len for one", &max_for_one, .1f, 1.f, "%.2f mm")) {
sample_config.max_length_for_one_support_point = scale_(max_for_one);

View File

@ -10,7 +10,7 @@
#include <libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp>
#include <libslic3r/SLA/SupportIslands/SampleConfig.hpp>
#include <libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp>
#include <libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp>
#include <libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp>
#include <libslic3r/SLA/SupportIslands/PolygonUtils.hpp>
#include "nanosvg/nanosvg.h" // load SVG file
#include "sla_test_utils.hpp"
@ -357,7 +357,10 @@ ExPolygons createTestIslands(double size)
bool useFrogLeg = false;
// need post reorganization of longest path
ExPolygons result = {
load_svg(dir + "lm_issue.svg"), // change from thick to thin and vice versa on circle
// debug
create_tiny_between_holes(3 * size, 2 / 3. * size),
// one support point
ExPolygon(PolygonUtils::create_equilateral_triangle(size)),
ExPolygon(PolygonUtils::create_square(size)),
@ -389,6 +392,7 @@ ExPolygons createTestIslands(double size)
ExPolygon(PolygonUtils::create_equilateral_triangle(scale_(18.6))),
create_cylinder_bottom_slice(),
load_svg(dir + "lm_issue.svg"), // change from thick to thin and vice versa on circle
// still problem
// three support points
@ -450,7 +454,7 @@ Points rasterize(const ExPolygon &island, double distance) {
SupportIslandPoints test_island_sampling(const ExPolygon & island,
const SampleConfig &config)
{
auto points = SampleIslandUtils::uniform_cover_island(island, config);
auto points = uniform_support_island(island, config);
Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer
bool is_ok = true;
@ -508,12 +512,9 @@ SampleConfig create_sample_config(double size) {
SampleConfig cfg;
cfg.max_distance = 3 * size + 0.1;
cfg.half_distance = cfg.max_distance/2;
cfg.head_radius = size / 4;
cfg.minimal_distance_from_outline = cfg.head_radius;
cfg.maximal_distance_from_outline = cfg.max_distance/4;
cfg.min_side_branch_length = 2 * cfg.minimal_distance_from_outline;
cfg.minimal_support_distance = cfg.minimal_distance_from_outline + cfg.half_distance;
cfg.max_length_for_one_support_point = 2*size;
cfg.max_length_for_two_support_points = 4*size;
cfg.max_width_for_center_support_line = size;
@ -590,6 +591,6 @@ TEST_CASE("Disable visualization", "[hide]")
#ifdef STORE_SAMPLE_INTO_SVG_FILES
CHECK(false);
#endif // STORE_SAMPLE_INTO_SVG_FILES
CHECK(SampleIslandUtils::is_visualization_disabled());
CHECK(is_uniform_support_island_visualization_disabled());
}