mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-14 20:55:55 +08:00
Continue cleaning code
After thin&thic must be at least 2 support points
This commit is contained in:
parent
b84eb1bc66
commit
3daed2f02d
@ -16,7 +16,6 @@ struct SampleConfig
|
|||||||
// Every point on island has at least one support point in maximum distance
|
// Every point on island has at least one support point in maximum distance
|
||||||
// MUST be bigger than zero
|
// MUST be bigger than zero
|
||||||
coord_t max_distance = static_cast<coord_t>(scale_(5.));
|
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
|
// Support point head radius
|
||||||
// MUST be bigger than zero
|
// MUST be bigger than zero
|
||||||
@ -31,25 +30,18 @@ struct SampleConfig
|
|||||||
// Must be bigger than minimal_distance_from_outline
|
// Must be bigger than minimal_distance_from_outline
|
||||||
coord_t maximal_distance_from_outline = static_cast<coord_t>(scale_(1.));// [nano meter]
|
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
|
// 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.
|
// 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.));
|
coord_t max_length_for_one_support_point = static_cast<coord_t>(scale_(1.));
|
||||||
|
|
||||||
// Maximal length of island supported by 2 points
|
// Maximal length of island supported by 2 points
|
||||||
coord_t max_length_for_two_support_points = static_cast<coord_t>(scale_(1.));
|
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
|
// Maximal width of line island supported in the middle of line
|
||||||
// Must be greater or equal to min_width_for_outline_support
|
// Must be greater or equal to min_width_for_outline_support
|
||||||
@ -71,7 +63,7 @@ struct SampleConfig
|
|||||||
|
|
||||||
// Sample outline of Field by this value
|
// Sample outline of Field by this value
|
||||||
// Less than max_distance
|
// 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
|
// Maximal distance over Voronoi diagram edges to find closest point during aligning Support point
|
||||||
coord_t max_align_distance = 0; // [scaled mm -> nanometers]
|
coord_t max_align_distance = 0; // [scaled mm -> nanometers]
|
||||||
|
@ -62,15 +62,10 @@ SampleConfig SampleConfigFactory::create(float support_head_diameter_in_mm)
|
|||||||
// TODO: find valid params !!!!
|
// TODO: find valid params !!!!
|
||||||
SampleConfig result;
|
SampleConfig result;
|
||||||
result.max_distance = max_distance;
|
result.max_distance = max_distance;
|
||||||
result.half_distance = result.max_distance / 2;
|
|
||||||
result.head_radius = head_diameter / 2;
|
result.head_radius = head_diameter / 2;
|
||||||
result.minimal_distance_from_outline = result.head_radius;
|
result.minimal_distance_from_outline = result.head_radius;
|
||||||
result.maximal_distance_from_outline = result.max_distance/3;
|
result.maximal_distance_from_outline = result.max_distance/3;
|
||||||
assert(result.minimal_distance_from_outline < result.maximal_distance_from_outline);
|
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 =
|
result.max_length_for_one_support_point =
|
||||||
max_distance / 3 +
|
max_distance / 3 +
|
||||||
2 * result.minimal_distance_from_outline +
|
2 * result.minimal_distance_from_outline +
|
||||||
|
@ -10,28 +10,12 @@ SupportIslandPoint::SupportIslandPoint(Slic3r::Point point, Type type)
|
|||||||
|
|
||||||
bool SupportIslandPoint::can_move(const 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({
|
static const std::set<Type> cant_move({
|
||||||
|
Type::one_bb_center_point,
|
||||||
Type::one_center_point,
|
Type::one_center_point,
|
||||||
Type::two_points,
|
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();
|
return cant_move.find(type) == cant_move.end();
|
||||||
//*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SupportIslandPoint::can_move() const { return can_move(type); }
|
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)
|
std::string SupportIslandPoint::to_string(const Type &type)
|
||||||
{
|
{
|
||||||
static const std::string undefined = "UNDEFINED";
|
|
||||||
static std::map<Type, std::string> type_to_string=
|
static std::map<Type, std::string> type_to_string=
|
||||||
{{Type::one_center_point, "one_center_point"},
|
{{Type::one_center_point, "one_center_point"},
|
||||||
{Type::two_points, "two_points"},
|
{Type::two_points, "two_points"},
|
||||||
{Type::center_line, "center_line"},
|
{Type::two_points_backup, "two_points_backup"},
|
||||||
{Type::center_line1, "center_line1"},
|
{Type::one_bb_center_point,"one_bb_center_point"},
|
||||||
{Type::center_line2, "center_line2"},
|
{Type::thin_part, "thin_part"},
|
||||||
{Type::center_line3, "center_line3"},
|
{Type::thin_part_change, "thin_part_change"},
|
||||||
{Type::center_line_end, "center_line_end"},
|
{Type::thin_part_loop, "thin_part_loop"},
|
||||||
{Type::center_line_end2, "center_line_end2"},
|
{Type::thick_part_outline, "thick_part_outline"},
|
||||||
{Type::center_line_end3, "center_line_end3"},
|
{Type::thick_part_inner, "thick_part_inner"},
|
||||||
{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::undefined, "undefined"}};
|
||||||
auto it = type_to_string.find(type);
|
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;
|
return it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,25 +16,15 @@ class SupportIslandPoint
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum class Type: unsigned char {
|
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
|
undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,7 +118,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
SupportCenterIslandPoint(VoronoiGraph::Position position,
|
SupportCenterIslandPoint(VoronoiGraph::Position position,
|
||||||
const SampleConfig *configuration,
|
const SampleConfig *configuration,
|
||||||
Type type = Type::center_line);
|
Type type = Type::thin_part);
|
||||||
|
|
||||||
bool can_move() const override{ return true; }
|
bool can_move() const override{ return true; }
|
||||||
coord_t move(const Point &destination) override;
|
coord_t move(const Point &destination) override;
|
||||||
@ -166,7 +156,7 @@ public:
|
|||||||
public:
|
public:
|
||||||
SupportOutlineIslandPoint(Position position,
|
SupportOutlineIslandPoint(Position position,
|
||||||
std::shared_ptr<Restriction> restriction,
|
std::shared_ptr<Restriction> restriction,
|
||||||
Type type = Type::outline);
|
Type type = Type::thick_part_outline);
|
||||||
// return true
|
// return true
|
||||||
bool can_move() const override;
|
bool can_move() const override;
|
||||||
|
|
||||||
@ -217,7 +207,7 @@ public:
|
|||||||
{
|
{
|
||||||
assert(lines.size() == lengths.size());
|
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> next_index(size_t index) const = 0;
|
||||||
virtual std::optional<size_t> prev_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:
|
public:
|
||||||
SupportIslandInnerPoint(Point point,
|
SupportIslandInnerPoint(Point point,
|
||||||
std::shared_ptr<ExPolygon> inner,
|
std::shared_ptr<ExPolygon> inner,
|
||||||
Type type = Type::inner);
|
Type type = Type::thick_part_inner);
|
||||||
|
|
||||||
bool can_move() const override { return true; };
|
bool can_move() const override { return true; };
|
||||||
|
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#include "SampleIslandUtils.hpp"
|
#include "UniformSupportIsland.hpp"
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <cassert>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#include <libslic3r/ClipperUtils.hpp> // allign
|
#include <libslic3r/ClipperUtils.hpp> // allign
|
||||||
#include <libslic3r/Geometry.hpp>
|
#include <libslic3r/Geometry.hpp>
|
||||||
@ -28,22 +30,19 @@
|
|||||||
|
|
||||||
// comment definition of NDEBUG to enable assert()
|
// comment definition of NDEBUG to enable assert()
|
||||||
//#define NDEBUG
|
//#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_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"
|
||||||
|
|
||||||
#include <cassert>
|
namespace {
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::sla;
|
using namespace Slic3r::sla;
|
||||||
|
|
||||||
namespace {
|
|
||||||
// TODO: Move to string utils
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Replace first occurence of string
|
/// Replace first occurence of string
|
||||||
|
/// TODO: Generalize and Move into string utils
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="s"></param>
|
/// <param name="s"></param>
|
||||||
/// <param name="toReplace"></param>
|
/// <param name="toReplace"></param>
|
||||||
@ -62,7 +61,6 @@ std::string replace_first(
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// IMPROVE: use Slic3r::BoundingBox
|
/// IMPROVE: use Slic3r::BoundingBox
|
||||||
///
|
|
||||||
/// Search for reference to an Expolygon with biggest contour
|
/// Search for reference to an Expolygon with biggest contour
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="expolygons">Input</param>
|
/// <param name="expolygons">Input</param>
|
||||||
@ -140,7 +138,7 @@ ExPolygon get_simplified(const ExPolygon &island, const SampleConfig &config) {
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Transform support point to slicer points
|
/// Transform support point to slicer points
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Slic3r::Points to_points(const SupportIslandPoints &support_points){
|
Points to_points(const SupportIslandPoints &support_points){
|
||||||
Points result;
|
Points result;
|
||||||
result.reserve(support_points.size());
|
result.reserve(support_points.size());
|
||||||
std::transform(support_points.begin(), support_points.end(), std::back_inserter(result),
|
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>
|
/// Term criteria for align: Minimal sample move and Maximal count of iteration</param>
|
||||||
void align_samples(SupportIslandPoints &samples, const ExPolygon &island, const SampleConfig &config);
|
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);
|
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>
|
/// <summary>
|
||||||
/// Create unique static support point
|
/// Create unique static support point
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -384,26 +295,20 @@ SupportIslandPointPtr create_middle_path_point(
|
|||||||
if (!position_opt.has_value()) return nullptr;
|
if (!position_opt.has_value()) return nullptr;
|
||||||
return create_no_move_point(*position_opt, type);
|
return create_no_move_point(*position_opt, type);
|
||||||
}
|
}
|
||||||
} // namespace
|
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
namespace {
|
bool is_points_in_distance(const Point & p,
|
||||||
bool is_points_in_distance(const Slic3r::Point & p,
|
const Points &points,
|
||||||
const Slic3r::Points &points,
|
|
||||||
double max_distance)
|
double max_distance)
|
||||||
{
|
{
|
||||||
for (const auto &p2 : points) {
|
return std::all_of(points.begin(), points.end(),
|
||||||
double d = (p - p2).cast<double>().norm();
|
[p, max_distance](const Point &point) {
|
||||||
if (d > max_distance)
|
double d = (p - point).cast<double>().norm();
|
||||||
return false;
|
return d <= max_distance;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
|
|
||||||
// align
|
|
||||||
namespace {
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// once align
|
/// once align
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -422,7 +327,7 @@ coord_t align_once(
|
|||||||
// https://stackoverflow.com/questions/23823345/how-to-construct-a-voronoi-diagram-inside-a-polygon
|
// https://stackoverflow.com/questions/23823345/how-to-construct-a-voronoi-diagram-inside-a-polygon
|
||||||
// IMPROVE1: add accessor to point coordinate do not copy points
|
// IMPROVE1: add accessor to point coordinate do not copy points
|
||||||
// IMPROVE2: add filter for create cell polygon only for moveable samples
|
// 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));
|
assert(!has_duplicate_points(points));
|
||||||
Polygons cell_polygons = create_voronoi_cells_cgal(points, config.max_distance);
|
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
|
continue; // do not align point with invalid cell
|
||||||
|
|
||||||
// IMPROVE: add intersection polygon with expolygon
|
// IMPROVE: add intersection polygon with expolygon
|
||||||
Polygons intersections = Slic3r::intersection(cell_polygon, island);
|
Polygons intersections = intersection(cell_polygon, island);
|
||||||
const Polygon *island_cell = nullptr;
|
const Polygon *island_cell = nullptr;
|
||||||
if (intersections.size() == 1) {
|
if (intersections.size() == 1) {
|
||||||
island_cell = &intersections.front();
|
island_cell = &intersections.front();
|
||||||
@ -559,12 +464,9 @@ void align_samples(SupportIslandPoints &samples, const ExPolygon &island, const
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Separation of thin and thick part of island
|
/// Separation of thin and thick part of island
|
||||||
/// </summary>
|
/// </summary>
|
||||||
namespace {
|
|
||||||
|
|
||||||
using VD = Slic3r::Geometry::VoronoiDiagram;
|
using VD = Slic3r::Geometry::VoronoiDiagram;
|
||||||
using Position = VoronoiGraph::Position;
|
using Position = VoronoiGraph::Position;
|
||||||
@ -610,8 +512,11 @@ using ThickParts = std::vector<ThickPart>;
|
|||||||
/// <param name="part">One thin part of island</param>
|
/// <param name="part">One thin part of island</param>
|
||||||
/// <param name="results">[OUTPUT]Set of support points</param>
|
/// <param name="results">[OUTPUT]Set of support points</param>
|
||||||
/// <param name="config">Define density 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) {
|
void create_supports_for_thin_part(
|
||||||
struct SupportIn {
|
const ThinPart &part, SupportIslandPoints &results, const SampleConfig &config
|
||||||
|
) {
|
||||||
|
struct SupportIn
|
||||||
|
{
|
||||||
// want to create support in
|
// want to create support in
|
||||||
coord_t support_in; // [nano meters]
|
coord_t support_in; // [nano meters]
|
||||||
// Neighbor to continue is not sampled yet
|
// Neighbor to continue is not sampled yet
|
||||||
@ -625,7 +530,8 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re
|
|||||||
// Current neighbor
|
// Current neighbor
|
||||||
SupportIn curr{half_support_distance + part.center.calc_distance(), part.center.neighbor};
|
SupportIn curr{half_support_distance + part.center.calc_distance(), part.center.neighbor};
|
||||||
const Neighbor *twin_start = VoronoiGraphUtils::get_twin(*curr.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
|
// Process queue
|
||||||
SupportIns process;
|
SupportIns process;
|
||||||
@ -644,14 +550,13 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re
|
|||||||
curr.neighbor == part_end_it->neighbor;
|
curr.neighbor == part_end_it->neighbor;
|
||||||
|
|
||||||
// add support on current neighbor
|
// add support on current neighbor
|
||||||
coord_t edge_length = (is_end_neighbor) ?
|
coord_t edge_length = (is_end_neighbor) ? part_end_it->calc_distance() :
|
||||||
part_end_it->calc_distance() :
|
|
||||||
static_cast<coord_t>(curr.neighbor->length());
|
static_cast<coord_t>(curr.neighbor->length());
|
||||||
while (edge_length >= curr.support_in) {
|
while (edge_length >= curr.support_in) {
|
||||||
double ratio = curr.support_in / curr.neighbor->length();
|
double ratio = curr.support_in / curr.neighbor->length();
|
||||||
VoronoiGraph::Position position(curr.neighbor, ratio);
|
VoronoiGraph::Position position(curr.neighbor, ratio);
|
||||||
results.push_back(std::make_unique<SupportCenterIslandPoint>(
|
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 += support_distance;
|
||||||
}
|
}
|
||||||
curr.support_in -= edge_length;
|
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)
|
// on the current neighbor lay part end(transition into neighbor Thick part)
|
||||||
if (curr.support_in < half_support_distance)
|
if (curr.support_in < half_support_distance)
|
||||||
results.push_back(std::make_unique<SupportCenterIslandPoint>(
|
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;
|
curr.neighbor = nullptr;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -680,7 +585,7 @@ void create_supports_for_thin_part(const ThinPart &part, SupportIslandPoints &re
|
|||||||
if (curr.support_in < half_support_distance) {
|
if (curr.support_in < half_support_distance) {
|
||||||
Position position{curr.neighbor, 1.}; // fine tune position by alignment
|
Position position{curr.neighbor, 1.}; // fine tune position by alignment
|
||||||
results.push_back(std::make_unique<SupportCenterIslandPoint>(
|
results.push_back(std::make_unique<SupportCenterIslandPoint>(
|
||||||
position, &config, SupportIslandPoint::Type::center_line1));
|
position, &config, SupportIslandPoint::Type::thin_part_loop));
|
||||||
}
|
}
|
||||||
process.erase(process_it);
|
process.erase(process_it);
|
||||||
curr.neighbor = nullptr;
|
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];
|
const Line &island_line = island_lines[island_line_index];
|
||||||
Vec2d dir1 = LineUtils::direction(island_line).cast<double>();
|
Vec2d dir1 = LineUtils::direction(island_line).cast<double>();
|
||||||
dir1.normalize();
|
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) {
|
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];
|
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>();
|
Vec2d dir2 = LineUtils::direction(offset_line).cast<double>();
|
||||||
dir2.normalize();
|
dir2.normalize();
|
||||||
double angle = acos(dir1.dot(dir2));
|
double angle = acos(dir1.dot(dir2));
|
||||||
// not similar direction
|
if (fabs(angle) > angle_tolerace) continue; // not similar direction
|
||||||
|
|
||||||
if (fabs(angle) > angle_tolerace) continue;
|
|
||||||
|
|
||||||
|
// Improve: use only one side of offest !!
|
||||||
Point offset_middle = LineUtils::middle(offset_line);
|
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);
|
double distance = island_line.perp_distance_to(offset_middle);
|
||||||
if (fabs(distance - offset_delta) > distance_tolerance)
|
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;
|
converter[island_line_index] = offset_line_index;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -856,9 +766,12 @@ std::vector<size_t> get_line_indices(const Neighbor* input, const Positions& end
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Fix expolygon with hole bigger than contour
|
/// Fix expolygon with hole bigger than contour
|
||||||
|
/// NOTE: when change contour and index it is neccesary also fix source indices
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="shape">In/Out expolygon</param>
|
/// <param name="shape">[In/Out] expolygon</param>
|
||||||
bool set_biggest_hole_as_contour(ExPolygon& shape){
|
/// <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();
|
Point contour_size = BoundingBox(shape.contour.points).size();
|
||||||
Polygons &holes = shape.holes;
|
Polygons &holes = shape.holes;
|
||||||
size_t contour_index = holes.size();
|
size_t contour_index = holes.size();
|
||||||
@ -873,9 +786,28 @@ bool set_biggest_hole_as_contour(ExPolygon& shape){
|
|||||||
return false; // contour is set correctly
|
return false; // contour is set correctly
|
||||||
|
|
||||||
// some hole is bigger than contour and become contour
|
// 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
|
Polygon tmp = holes[contour_index]; // copy
|
||||||
std::swap(tmp, shape.contour);
|
std::swap(tmp, shape.contour);
|
||||||
holes[contour_index] = std::move(tmp);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -891,9 +823,9 @@ struct Field
|
|||||||
// same size as polygon.points.size()
|
// same size as polygon.points.size()
|
||||||
// indexes to source island lines
|
// indexes to source island lines
|
||||||
// in case (index > lines.size()) it means fill the gap from tiny part of island
|
// 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
|
// 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
|
// inner part of field
|
||||||
ExPolygon inner;
|
ExPolygon inner;
|
||||||
@ -901,7 +833,44 @@ struct Field
|
|||||||
std::map<size_t, size_t> field_2_inner;
|
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
|
// IMPROVE do not use pointers on node but pointers on Neighbor
|
||||||
Field create_thick_field(const ThickPart& part, const Lines &lines, const SampleConfig &config)
|
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 =
|
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_indexes;
|
std::vector<size_t> source_indices;
|
||||||
auto inser_point_b = [&lines, &b_connection, &source_indexes]
|
auto inser_point_b = [&lines, &b_connection, &source_indices]
|
||||||
(size_t &index, Points &points, std::set<size_t> &done)
|
(size_t &index, Points &points, std::set<size_t> &done)
|
||||||
{
|
{
|
||||||
const Line &line = lines[index];
|
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());
|
assert(connection_item != b_connection.end());
|
||||||
done.insert(index);
|
done.insert(index);
|
||||||
index = connection_item->second;
|
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>
|
/// <summary>
|
||||||
/// Insert change into
|
/// 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="lines">island(ExPolygon) converted to lines</param>
|
||||||
/// <param name="index"></param> ...
|
/// <param name="index"></param> ...
|
||||||
/// <returns>False when change lead to close loop(into first change) otherwise True</returns>
|
/// <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 {
|
(size_t &index, Points &points, std::set<size_t> &done, size_t input_index)->bool {
|
||||||
auto change_item = wide_tiny_changes.find(index);
|
auto change_item = wide_tiny_changes.find(index);
|
||||||
while (change_item != wide_tiny_changes.end()) {
|
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() ||
|
if (points.empty() ||
|
||||||
!PointUtils::is_equal(points.back(), change.new_b)) {
|
!PointUtils::is_equal(points.back(), change.new_b)) {
|
||||||
points.push_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 {
|
} else {
|
||||||
source_indexes.back() = source_indexe_for_change;
|
source_indices.back() = source_index_for_change;
|
||||||
}
|
}
|
||||||
// prevent double points
|
// prevent double points
|
||||||
if (!PointUtils::is_equal(lines[change.next_line_index].b,
|
if (!PointUtils::is_equal(lines[change.next_line_index].b,
|
||||||
change.next_new_a)) {
|
change.next_new_a)) {
|
||||||
points.push_back(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);
|
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 input_index = std::min(input_index1, input_index2); // Why select min index?
|
||||||
size_t outline_index = input_index;
|
size_t outline_index = input_index;
|
||||||
// Done indexes is used to detect holes in field
|
// 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 {
|
do {
|
||||||
if (!insert_changes(outline_index, points, done_indexes, input_index))
|
if (!insert_changes(outline_index, points, done_indices, input_index))
|
||||||
break;
|
break;
|
||||||
inser_point_b(outline_index, points, done_indexes);
|
inser_point_b(outline_index, points, done_indices);
|
||||||
} while (outline_index != input_index);
|
} while (outline_index != input_index);
|
||||||
|
|
||||||
assert(points.size() >= 3);
|
assert(points.size() >= 3);
|
||||||
Field field;
|
Field field;
|
||||||
field.border.contour = Polygon(points);
|
field.border.contour = Polygon(points);
|
||||||
// finding holes(another closed polygon)
|
// 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) {
|
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
|
// new hole
|
||||||
Points hole_points;
|
Points hole_points;
|
||||||
size_t hole_index = index;
|
size_t hole_index = index;
|
||||||
do {
|
do {
|
||||||
inser_point_b(hole_index, hole_points, done_indexes);
|
inser_point_b(hole_index, hole_points, done_indices);
|
||||||
} while (hole_index != index);
|
} while (hole_index != index);
|
||||||
field.border.holes.emplace_back(hole_points);
|
field.border.holes.emplace_back(hole_points);
|
||||||
}
|
}
|
||||||
// Set largest polygon as contour
|
// 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_index_for_change = source_index_for_change;
|
||||||
field.source_indexes = std::move(source_indexes);
|
field.source_indices = std::move(source_indices);
|
||||||
std::tie(field.inner, field.field_2_inner) =
|
std::tie(field.inner, field.field_2_inner) =
|
||||||
outline_offset(field.border, (float)config.minimal_distance_from_outline);
|
outline_offset(field.border, (float)config.minimal_distance_from_outline);
|
||||||
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_FIELD_TO_SVG_PATH
|
#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="field">Input field</param>
|
||||||
/// <param name="config">Parameters for sampling.</param>
|
/// <param name="config">Parameters for sampling.</param>
|
||||||
/// <returns>support for outline</returns>
|
/// <returns>support for outline</returns>
|
||||||
SupportIslandPoints sample_outline(
|
SupportIslandPoints sample_outline(const Field &field, const SampleConfig &config){
|
||||||
const Field &field, const SampleConfig &config)
|
|
||||||
{
|
|
||||||
const ExPolygon &border = field.border;
|
const ExPolygon &border = field.border;
|
||||||
const Polygon &contour = border.contour;
|
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 max_align_distance = config.max_align_distance;
|
||||||
coord_t sample_distance = config.outline_sample_distance;
|
coord_t sample_distance = config.outline_sample_distance;
|
||||||
SupportIslandPoints result;
|
SupportIslandPoints result;
|
||||||
|
|
||||||
using RestrictionPtr = std::shared_ptr<SupportOutlineIslandPoint::Restriction>;
|
using RestrictionPtr = std::shared_ptr<SupportOutlineIslandPoint::Restriction>;
|
||||||
auto add_sample = [&](size_t index, const RestrictionPtr& restriction, coord_t &last_support) {
|
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];
|
const double &line_length_double = restriction->lengths[index];
|
||||||
coord_t line_length = static_cast<coord_t>(std::round(line_length_double));
|
coord_t line_length = static_cast<coord_t>(std::round(line_length_double));
|
||||||
if (last_support + line_length > sample_distance) {
|
while (last_support + line_length > sample_distance){
|
||||||
do {
|
|
||||||
float ratio = static_cast<float>((sample_distance - last_support) / line_length_double);
|
float ratio = static_cast<float>((sample_distance - last_support) / line_length_double);
|
||||||
result.emplace_back(
|
SupportOutlineIslandPoint::Position position(index, ratio);
|
||||||
std::make_unique<SupportOutlineIslandPoint>(
|
result.emplace_back(std::make_unique<SupportOutlineIslandPoint>(
|
||||||
Position(index, ratio), restriction,
|
position, restriction, SupportIslandPoint::Type::thick_part_outline));
|
||||||
SupportIslandPoint::Type::outline)
|
|
||||||
);
|
|
||||||
last_support -= sample_distance;
|
last_support -= sample_distance;
|
||||||
} while (last_support + line_length > sample_distance);
|
|
||||||
}
|
}
|
||||||
last_support += line_length;
|
last_support += line_length;
|
||||||
};
|
};
|
||||||
@ -1258,14 +1220,12 @@ SupportIslandPoints sample_outline(
|
|||||||
sum_lengths += length;
|
sum_lengths += length;
|
||||||
lengths.push_back(length);
|
lengths.push_back(length);
|
||||||
}
|
}
|
||||||
// no samples on this polygon
|
|
||||||
|
|
||||||
using Restriction = SupportOutlineIslandPoint::RestrictionCircleSequence;
|
using Restriction = SupportOutlineIslandPoint::RestrictionCircleSequence;
|
||||||
auto restriction = std::make_shared<Restriction>(lines, lengths, max_align_distance);
|
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;
|
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);
|
add_sample(index, restriction, last_support);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// sample line sequence
|
// sample line sequence
|
||||||
@ -1313,13 +1273,12 @@ SupportIslandPoints sample_outline(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// convert map from field index to inner(line index)
|
// convert map from field index to inner(line index)
|
||||||
|
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;
|
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) {
|
|
||||||
if (inner_polygon.empty())
|
if (inner_polygon.empty())
|
||||||
return; // nothing to sample
|
return; // nothing to sample
|
||||||
|
|
||||||
@ -1327,8 +1286,8 @@ SupportIslandPoints sample_outline(
|
|||||||
size_t first_change_index = polygon.size();
|
size_t first_change_index = polygon.size();
|
||||||
for (size_t polygon_index = 0; polygon_index < polygon.size(); ++polygon_index) {
|
for (size_t polygon_index = 0; polygon_index < polygon.size(); ++polygon_index) {
|
||||||
size_t index = polygon_index + index_offset;
|
size_t index = polygon_index + index_offset;
|
||||||
assert(index < field.source_indexes.size());
|
assert(index < source_indices.size());
|
||||||
size_t source_index = field.source_indexes[index];
|
size_t source_index = source_indices[index];
|
||||||
if (source_index == change_index) {
|
if (source_index == change_index) {
|
||||||
// found change from wide to tiny part
|
// found change from wide to tiny part
|
||||||
first_change_index = polygon_index;
|
first_change_index = polygon_index;
|
||||||
@ -1353,8 +1312,8 @@ SupportIslandPoints sample_outline(
|
|||||||
polygon_index != stop_index; ++polygon_index) {
|
polygon_index != stop_index; ++polygon_index) {
|
||||||
if (polygon_index == polygon.size()) polygon_index = 0;
|
if (polygon_index == polygon.size()) polygon_index = 0;
|
||||||
size_t index = polygon_index + index_offset;
|
size_t index = polygon_index + index_offset;
|
||||||
assert(index < field.source_indexes.size());
|
assert(index < source_indices.size());
|
||||||
size_t source_index = field.source_indexes[index];
|
size_t source_index = source_indices[index];
|
||||||
if (source_index == change_index) {
|
if (source_index == change_index) {
|
||||||
if (inner_first == inner_invalid) continue;
|
if (inner_first == inner_invalid) continue;
|
||||||
// create Restriction object
|
// 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),
|
std::transform(inner_points.begin(), inner_points.end(), std::back_inserter(results),
|
||||||
[&](const Point &point) {
|
[&](const Point &point) {
|
||||||
return std::make_unique<SupportIslandInnerPoint>(
|
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>
|
/// </summary>
|
||||||
struct ProcessItem {
|
struct ProcessItem {
|
||||||
// previously processed island node
|
// previously processed island node
|
||||||
const VoronoiGraph::Node *prev_node;
|
const VoronoiGraph::Node *prev_node = nullptr;
|
||||||
|
|
||||||
// current island node to investigate neighbors
|
// current island node to investigate neighbors
|
||||||
const VoronoiGraph::Node *node;
|
const VoronoiGraph::Node *node = nullptr;
|
||||||
|
|
||||||
// index of island part stored in island_parts
|
// index of island part stored in island_parts
|
||||||
// NOTE: Can't use reference because of vector reallocation
|
// 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>;
|
using ProcessItems = std::vector<ProcessItem>;
|
||||||
|
|
||||||
@ -2109,7 +2068,7 @@ std::pair<ThinParts, ThickParts> separate_thin_thick(
|
|||||||
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);
|
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) {
|
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
|
||||||
@ -2160,20 +2119,22 @@ std::pair<ThinParts, ThickParts> separate_thin_thick(
|
|||||||
/// <param name="max_side_distance">Maximal distance from side</param>
|
/// <param name="max_side_distance">Maximal distance from side</param>
|
||||||
/// <returns>2x Static Support point(lay os sides of path)</returns>
|
/// <returns>2x Static Support point(lay os sides of path)</returns>
|
||||||
SupportIslandPoints create_side_points(
|
SupportIslandPoints create_side_points(
|
||||||
const VoronoiGraph::Nodes &path,
|
const VoronoiGraph::ExPath &path, const Lines& lines, const SampleConfig &config,
|
||||||
const Lines& lines,
|
SupportIslandPoint::Type type = SupportIslandPoint::Type::two_points)
|
||||||
coord_t width,
|
|
||||||
coord_t max_side_distance)
|
|
||||||
{
|
{
|
||||||
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());
|
std::reverse(reverse_path.begin(), reverse_path.end());
|
||||||
coord_t side_distance1 = max_side_distance; // copy
|
|
||||||
coord_t side_distance2 = max_side_distance; // copy
|
coord_t width = 2 * config.head_radius;
|
||||||
auto pos1 = create_position_on_path(path, lines, width, side_distance1);
|
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);
|
auto pos2 = create_position_on_path(reverse_path, lines, width, side_distance2);
|
||||||
assert(pos1.has_value());
|
assert(pos1.has_value());
|
||||||
assert(pos2.has_value());
|
assert(pos2.has_value());
|
||||||
SupportIslandPoint::Type type = SupportIslandPoint::Type::two_points;
|
|
||||||
SupportIslandPoints result;
|
SupportIslandPoints result;
|
||||||
result.reserve(2);
|
result.reserve(2);
|
||||||
result.push_back(create_no_move_point(*pos1, type));
|
result.push_back(create_no_move_point(*pos1, type));
|
||||||
@ -2181,109 +2142,19 @@ SupportIslandPoints create_side_points(
|
|||||||
return result;
|
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) {
|
void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radius, bool write_type) {
|
||||||
const char *color = nullptr;
|
const char *color = nullptr;
|
||||||
for (const auto &p : supportIslandPoints) {
|
for (const auto &p : supportIslandPoints) {
|
||||||
switch (p->type) {
|
switch (p->type) {
|
||||||
case SupportIslandPoint::Type::center_line1:
|
case SupportIslandPoint::Type::thin_part:
|
||||||
case SupportIslandPoint::Type::center_line2:
|
case SupportIslandPoint::Type::thin_part_change:
|
||||||
case SupportIslandPoint::Type::center_line3:
|
case SupportIslandPoint::Type::thin_part_loop: color = "lightred"; break;
|
||||||
case SupportIslandPoint::Type::center_circle:
|
case SupportIslandPoint::Type::thick_part_outline: color = "lightblue"; break;
|
||||||
case SupportIslandPoint::Type::center_circle_end:
|
case SupportIslandPoint::Type::thick_part_inner: color = "lightgreen"; break;
|
||||||
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::one_bb_center_point: color = "red"; break;
|
case SupportIslandPoint::Type::one_bb_center_point: color = "red"; break;
|
||||||
case SupportIslandPoint::Type::one_center_point:
|
case SupportIslandPoint::Type::one_center_point:
|
||||||
case SupportIslandPoint::Type::two_points:
|
case SupportIslandPoint::Type::two_points:
|
||||||
|
case SupportIslandPoint::Type::two_points_backup:
|
||||||
default: color = "black";
|
default: color = "black";
|
||||||
}
|
}
|
||||||
svg.draw(p->point, color, radius);
|
svg.draw(p->point, color, radius);
|
||||||
@ -2294,9 +2165,136 @@ void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // 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
|
#ifndef NDEBUG
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@ -2311,3 +2309,5 @@ bool SampleIslandUtils::is_uniform_cover_island_visualization_disabled() {
|
|||||||
#endif
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace Slic3r::sla
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#ifndef slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
|
#ifndef slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_
|
||||||
#define slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
|
#define slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_
|
||||||
|
|
||||||
#include <libslic3r/ExPolygon.hpp>
|
#include <libslic3r/ExPolygon.hpp>
|
||||||
#include "SampleConfig.hpp"
|
#include "SampleConfig.hpp"
|
||||||
@ -8,26 +8,17 @@
|
|||||||
namespace Slic3r::sla {
|
namespace Slic3r::sla {
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Utils class with only static function
|
/// Distribute support points across island area defined by ExPolygon.
|
||||||
/// Function for sampling island by Voronoi Graph.
|
|
||||||
/// </summary>
|
|
||||||
class SampleIslandUtils
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
SampleIslandUtils() = delete;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Main entry for sample island
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="island">Shape of island</param>
|
/// <param name="island">Shape of island</param>
|
||||||
/// <param name="config">Configuration for sampler</param>
|
/// <param name="config">Configuration of support density</param>
|
||||||
/// <returns>List of support points</returns>
|
/// <returns>Support points laying inside of island</returns>
|
||||||
static SupportIslandPoints uniform_cover_island(
|
SupportIslandPoints uniform_support_island(const ExPolygon &island, const SampleConfig &config);
|
||||||
const ExPolygon &island, const SampleConfig &config);
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
static bool is_uniform_cover_island_visualization_disabled();
|
/// Check for tests that developer do not forget disable visualization after debuging.
|
||||||
};
|
/// </summary>
|
||||||
|
bool is_uniform_support_island_visualization_disabled();
|
||||||
|
|
||||||
} // namespace Slic3r::sla
|
} // namespace Slic3r::sla
|
||||||
#endif // slic3r_SLA_SuppotstIslands_SampleIslandUtils_hpp_
|
#endif // slic3r_SLA_SuppotstIslands_UniformSupportIsland_hpp_
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
#include "libslic3r/Execution/ExecutionTBB.hpp" // parallel preparation of data for sampling
|
#include "libslic3r/Execution/ExecutionTBB.hpp" // parallel preparation of data for sampling
|
||||||
#include "libslic3r/Execution/Execution.hpp"
|
#include "libslic3r/Execution/Execution.hpp"
|
||||||
#include "libslic3r/KDTreeIndirect.hpp"
|
#include "libslic3r/KDTreeIndirect.hpp"
|
||||||
|
#include "libslic3r/ClipperUtils.hpp"
|
||||||
|
|
||||||
// SupportIslands
|
// SupportIslands
|
||||||
#include "libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp"
|
#include "libslic3r/SLA/SupportIslands/UniformSupportIsland.hpp"
|
||||||
|
|
||||||
using namespace Slic3r;
|
using namespace Slic3r;
|
||||||
using namespace Slic3r::sla;
|
using namespace Slic3r::sla;
|
||||||
@ -145,7 +146,7 @@ private:
|
|||||||
/// <param name="cnt">Circle center point</param>
|
/// <param name="cnt">Circle center point</param>
|
||||||
/// <param name="r2">squared value of Circle Radius (r2 = r*r)</param>
|
/// <param name="r2">squared value of Circle Radius (r2 = r*r)</param>
|
||||||
/// <returns>Intersection point</returns>
|
/// <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
|
// Vector from p1 to p2
|
||||||
Vec2d dp_d((p2 - p1).cast<double>());
|
Vec2d dp_d((p2 - p1).cast<double>());
|
||||||
// Vector from circle center to p1
|
// Vector from circle center to p1
|
||||||
@ -269,7 +270,7 @@ void support_part_overhangs(
|
|||||||
/// <param name="cfg"></param>
|
/// <param name="cfg"></param>
|
||||||
void support_island(const LayerPart &part, NearPoints& near_points, float part_z,
|
void support_island(const LayerPart &part, NearPoints& near_points, float part_z,
|
||||||
const SupportPointGeneratorConfig &cfg) {
|
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())};
|
//samples = {std::make_unique<SupportIslandPoint>(island.contour.centroid())};
|
||||||
for (const SupportIslandPointPtr &sample : samples)
|
for (const SupportIslandPointPtr &sample : samples)
|
||||||
near_points.add(LayerSupportPoint{
|
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
|
while (p_dist2 > dist2) { // line segment goes out of radius
|
||||||
if (prev_pt == nullptr)
|
if (prev_pt == nullptr)
|
||||||
prev_pt = &(*it);
|
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();
|
p_dist2 = (r.back() - pt).cast<double>().squaredNorm();
|
||||||
prev_pt = &r.back();
|
prev_pt = &r.back();
|
||||||
}
|
}
|
||||||
|
@ -838,11 +838,9 @@ void GLGizmoSlaSupports::draw_island_config() {
|
|||||||
if (float max_distance = unscale<float>(sample_config.max_distance); // [in mm]
|
if (float max_distance = unscale<float>(sample_config.max_distance); // [in mm]
|
||||||
ImGui::InputFloat("Max dist", &max_distance, .1f, 1.f, "%.2f mm")) {
|
ImGui::InputFloat("Max dist", &max_distance, .1f, 1.f, "%.2f mm")) {
|
||||||
sample_config.max_distance = scale_(max_distance);
|
sample_config.max_distance = scale_(max_distance);
|
||||||
sample_config.half_distance = sample_config.max_distance / 2;
|
|
||||||
exist_change = true;
|
exist_change = true;
|
||||||
} else if (ImGui::IsItemHovered())
|
} 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::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]
|
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")) {
|
ImGui::InputFloat("from outline", &minimal_distance_from_outline, .1f, 1.f, "%.2f mm")) {
|
||||||
sample_config.minimal_distance_from_outline = scale_(minimal_distance_from_outline);
|
sample_config.minimal_distance_from_outline = scale_(minimal_distance_from_outline);
|
||||||
@ -856,20 +854,6 @@ void GLGizmoSlaSupports::draw_island_config() {
|
|||||||
exist_change = true;
|
exist_change = true;
|
||||||
} else if (ImGui::IsItemHovered())
|
} 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::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]
|
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")) {
|
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);
|
sample_config.max_length_for_one_support_point = scale_(max_for_one);
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
#include <libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp>
|
#include <libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp>
|
||||||
#include <libslic3r/SLA/SupportIslands/SampleConfig.hpp>
|
#include <libslic3r/SLA/SupportIslands/SampleConfig.hpp>
|
||||||
#include <libslic3r/SLA/SupportIslands/VoronoiGraphUtils.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 <libslic3r/SLA/SupportIslands/PolygonUtils.hpp>
|
||||||
#include "nanosvg/nanosvg.h" // load SVG file
|
#include "nanosvg/nanosvg.h" // load SVG file
|
||||||
#include "sla_test_utils.hpp"
|
#include "sla_test_utils.hpp"
|
||||||
@ -357,7 +357,10 @@ ExPolygons createTestIslands(double size)
|
|||||||
bool useFrogLeg = false;
|
bool useFrogLeg = false;
|
||||||
// need post reorganization of longest path
|
// need post reorganization of longest path
|
||||||
ExPolygons result = {
|
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
|
// one support point
|
||||||
ExPolygon(PolygonUtils::create_equilateral_triangle(size)),
|
ExPolygon(PolygonUtils::create_equilateral_triangle(size)),
|
||||||
ExPolygon(PolygonUtils::create_square(size)),
|
ExPolygon(PolygonUtils::create_square(size)),
|
||||||
@ -389,6 +392,7 @@ ExPolygons createTestIslands(double size)
|
|||||||
|
|
||||||
ExPolygon(PolygonUtils::create_equilateral_triangle(scale_(18.6))),
|
ExPolygon(PolygonUtils::create_equilateral_triangle(scale_(18.6))),
|
||||||
create_cylinder_bottom_slice(),
|
create_cylinder_bottom_slice(),
|
||||||
|
load_svg(dir + "lm_issue.svg"), // change from thick to thin and vice versa on circle
|
||||||
|
|
||||||
// still problem
|
// still problem
|
||||||
// three support points
|
// three support points
|
||||||
@ -450,7 +454,7 @@ Points rasterize(const ExPolygon &island, double distance) {
|
|||||||
SupportIslandPoints test_island_sampling(const ExPolygon & island,
|
SupportIslandPoints test_island_sampling(const ExPolygon & island,
|
||||||
const SampleConfig &config)
|
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
|
Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer
|
||||||
bool is_ok = true;
|
bool is_ok = true;
|
||||||
@ -508,12 +512,9 @@ SampleConfig create_sample_config(double size) {
|
|||||||
|
|
||||||
SampleConfig cfg;
|
SampleConfig cfg;
|
||||||
cfg.max_distance = 3 * size + 0.1;
|
cfg.max_distance = 3 * size + 0.1;
|
||||||
cfg.half_distance = cfg.max_distance/2;
|
|
||||||
cfg.head_radius = size / 4;
|
cfg.head_radius = size / 4;
|
||||||
cfg.minimal_distance_from_outline = cfg.head_radius;
|
cfg.minimal_distance_from_outline = cfg.head_radius;
|
||||||
cfg.maximal_distance_from_outline = cfg.max_distance/4;
|
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_one_support_point = 2*size;
|
||||||
cfg.max_length_for_two_support_points = 4*size;
|
cfg.max_length_for_two_support_points = 4*size;
|
||||||
cfg.max_width_for_center_support_line = size;
|
cfg.max_width_for_center_support_line = size;
|
||||||
@ -590,6 +591,6 @@ 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
|
||||||
CHECK(SampleIslandUtils::is_visualization_disabled());
|
CHECK(is_uniform_support_island_visualization_disabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user