Add support permanent points for island and peninsulas

This commit is contained in:
Filip Sykala - NTB T15p 2025-01-10 15:27:37 +01:00 committed by Lukas Matena
parent c66df2ce99
commit 33f878e5bd
5 changed files with 119 additions and 32 deletions

View File

@ -25,6 +25,8 @@ public:
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
permanent, // permanent support point with static position
undefined
};

View File

@ -3,11 +3,11 @@
#include <cmath>
#include <optional>
#include <vector>
#include <optional>
#include <cassert>
#include <memory>
#include <libslic3r/ClipperUtils.hpp> // allign
#include <libslic3r/KDTreeIndirect.hpp> // closest point
#include <libslic3r/Geometry.hpp>
#include "libslic3r/Geometry/Voronoi.hpp"
#include <libslic3r/Geometry/VoronoiOffset.hpp>
@ -514,6 +514,65 @@ void align_samples(SupportIslandPoints &samples, const ExPolygon &island, const
}
void align_samples_with_permanent(
SupportIslandPoints &samples, const ExPolygon &island, const Points& permanent, const SampleConfig &config)
{
assert(!permanent.empty());
if (permanent.empty())
return align_samples(samples, island, config);
// detect whether add adding support points
size_t tolerance = 1 + size_t(permanent.size() * 0.1); // 1 + 10% of permanent points
bool extend_permanent = samples.size() > (permanent.size() + tolerance);
if (!extend_permanent) // use only permanent support points
return samples.clear();
// find closest samples to permanent support points
Points points;
points.reserve(samples.size());
for (const SupportIslandPointPtr &p : samples)
points.push_back(p->point);
auto point_accessor = [&points](size_t idx, size_t dim) -> coord_t & {
return points[idx][dim]; };
KDTreeIndirect<2, coord_t, decltype(point_accessor)> tree(point_accessor, samples.size());
for (size_t i = 0; i < permanent.size(); ++i) {
std::array<size_t, 5> closests = find_closest_points<5>(tree, permanent[i]);
bool found_closest = false;
for (size_t idx : closests) {
if (idx >= samples.size())
continue; // closest function return also size_t::max()
SupportIslandPointPtr &sample = samples[idx];
if (sample->type == SupportIslandPoint::Type::permanent)
continue; // already used
sample->type = SupportIslandPoint::Type::permanent;
found_closest = true;
break;
}
if (!found_closest) { // backup solution when closest 5 fails, took first non permanent
for (const auto &sample : samples)
if (sample->type != SupportIslandPoint::Type::permanent) {
sample->type = SupportIslandPoint::Type::permanent;
break;
}
}
}
// remove samples marked as permanent
samples.erase(std::remove_if(samples.begin(), samples.end(), [](const SupportIslandPointPtr &sample) {
return sample->type == SupportIslandPoint::Type::permanent; }), samples.end());
// add permanent into samples
for (const Point&p: permanent)
samples.push_back(
std::make_unique<SupportIslandNoMovePoint>(p, SupportIslandPoint::Type::permanent));
align_samples(samples, island, config);
// remove permanent samples inserted for aligning
samples.erase(std::remove_if(samples.begin(), samples.end(), [](const SupportIslandPointPtr &sample) {
return sample->type == SupportIslandPoint::Type::permanent; }), samples.end());
}
/// <summary>
/// Separation of thin and thick part of island
/// </summary>
@ -2231,7 +2290,8 @@ void draw(SVG &svg, const SupportIslandPoints &supportIslandPoints, coord_t radi
/// uniform support island ///
//////////////////////////////
namespace Slic3r::sla {
SupportIslandPoints uniform_support_island(const ExPolygon &island, const SampleConfig &config){
SupportIslandPoints uniform_support_island(
const ExPolygon &island, const Points& permanent, const SampleConfig &config){
ExPolygon simplified_island = get_simplified(island, config);
#ifdef OPTION_TO_STORE_ISLAND
std::string path;
@ -2334,7 +2394,11 @@ SupportIslandPoints uniform_support_island(const ExPolygon &island, const Sample
#endif // OPTION_TO_STORE_ISLAND
// allign samples
align_samples(supports, island, config);
if (permanent.empty())
align_samples(supports, island, config);
else
align_samples_with_permanent(supports, island, permanent, config);
#ifdef OPTION_TO_STORE_ISLAND
if (!path.empty()) {
SVG svg = draw_island(path, island, simplified_island);
@ -2354,7 +2418,8 @@ SupportIslandPoints uniform_support_island(const ExPolygon &island, const Sample
}
// Follow implementation "create_supports_for_thick_part("
SupportIslandPoints uniform_support_peninsula(const Peninsula &peninsula, const SampleConfig &config){
SupportIslandPoints uniform_support_peninsula(
const Peninsula &peninsula, const Points& permanent, const SampleConfig &config){
// create_peninsula_field
Field field;
field.border = peninsula.unsuported_area;
@ -2372,7 +2437,12 @@ SupportIslandPoints uniform_support_peninsula(const Peninsula &peninsula, const
std::transform(inner_points.begin(), inner_points.end(), std::back_inserter(results),
[&inner](const Point &point) { return std::make_unique<SupportIslandInnerPoint>(
point, inner, SupportIslandPoint::Type::thick_part_inner);});
align_samples(results, peninsula.unsuported_area, config);
// allign samples
if (permanent.empty())
align_samples(results, peninsula.unsuported_area, config);
else
align_samples_with_permanent(results, peninsula.unsuported_area, permanent, config);
return results;
}

View File

@ -12,17 +12,21 @@ namespace Slic3r::sla {
/// Distribute support points across island area defined by ExPolygon.
/// </summary>
/// <param name="island">Shape of island</param>
/// <param name="permanent">Place supported by already existing supports</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);
/// <returns>Support points laying inside of the island</returns>
SupportIslandPoints uniform_support_island(
const ExPolygon &island, const Points &permanent, const SampleConfig &config);
/// <summary>
/// Distribute support points across peninsula
/// </summary>
/// <param name="peninsula">half island with anotation of the coast and land outline</param>
/// <param name="permanent">Place supported by already existing supports</param>
/// <param name="config">Density distribution parameters</param>
/// <returns></returns>
SupportIslandPoints uniform_support_peninsula(const Peninsula &peninsula, const SampleConfig &config);
/// <returns>Support points laying inside of the peninsula</returns>
SupportIslandPoints uniform_support_peninsula(
const Peninsula &peninsula, const Points& permanent, const SampleConfig &config);
/// <summary>
/// Check for tests that developer do not forget disable visualization after debuging.

View File

@ -16,8 +16,6 @@
using namespace Slic3r;
using namespace Slic3r::sla;
//#define PERMANENT_SUPPORTS
namespace {
/// <summary>
/// Struct to store support points in KD tree to fast search for nearest ones.
@ -274,10 +272,11 @@ void support_part_overhangs(
/// <param name="part">Island to support</param>
/// <param name="near_points">OUT place to store new supports</param>
/// <param name="part_z">z coordinate of part</param>
/// <param name="permanent">z coordinate of part</param>
/// <param name="cfg"></param>
void support_island(const LayerPart &part, NearPoints& near_points, float part_z,
const SupportPointGeneratorConfig &cfg) {
SupportIslandPoints samples = uniform_support_island(*part.shape, cfg.island_configuration);
const Points &permanent, const SupportPointGeneratorConfig &cfg) {
SupportIslandPoints samples = uniform_support_island(*part.shape, permanent, cfg.island_configuration);
for (const SupportIslandPointPtr &sample : samples)
near_points.add(LayerSupportPoint{
SupportPoint{
@ -296,10 +295,10 @@ void support_island(const LayerPart &part, NearPoints& near_points, float part_z
}
void support_peninsulas(const Peninsulas& peninsulas, NearPoints& near_points, float part_z,
const SupportPointGeneratorConfig &cfg) {
const Points &permanent, const SupportPointGeneratorConfig &cfg) {
for (const Peninsula& peninsula: peninsulas) {
SupportIslandPoints peninsula_supports =
uniform_support_peninsula(peninsula, cfg.island_configuration);
uniform_support_peninsula(peninsula, permanent, cfg.island_configuration);
for (const SupportIslandPointPtr &support : peninsula_supports)
near_points.add(LayerSupportPoint{
SupportPoint{
@ -814,11 +813,9 @@ std::vector<Vec2f> load_curve_from_file() {
return {};
}
#ifdef PERMANENT_SUPPORTS
// Processing permanent support points
// Permanent are manualy edited points by user
namespace {
size_t get_index_of_closest_part(const Point &coor, const LayerParts &parts, double max_allowed_distance_sq) {
size_t count_lines = 0;
std::vector<size_t> part_lines_ends;
@ -1041,6 +1038,9 @@ PermanentSupports prepare_permanent_supports(
// How to propagate permanent support position into previous layers? and how deep? requirements
// are chained. IMHO it should start togetjer from islands and permanent than propagate over surface
if (permanent_supports.empty())
return {};
// permanent supports MUST be sorted by z
assert(std::is_sorted(permanent_supports.begin(), permanent_supports.end(),
[](const SupportPoint &a, const SupportPoint &b) { return a.pos.z() < b.pos.z(); }));
@ -1102,10 +1102,12 @@ bool exist_permanent_support(const PermanentSupports& supports, size_t current_s
/// <summary>
/// copy permanent supports into near points
/// which has influence into current layer part
/// </summary>
/// <param name="near_points">OUTPUT for all permanent supports for this layer and part</param>
/// <param name="supports">Copied from</param>
/// <param name="supports">source for Copy</param>
/// <param name="support_index">current index into supports</param>
/// <param name="print_z">current layer index</param>
/// <param name="layer_index">current layer index</param>
/// <param name="part_index">current part index</param>
void copy_permanent_supports(NearPoints& near_points, const PermanentSupports& supports, size_t& support_index,
@ -1118,12 +1120,23 @@ void copy_permanent_supports(NearPoints& near_points, const PermanentSupports& s
/* radius_curve_index */ 0, // before support point - earlier influence on point distribution
/* current_radius */ calc_influence_radius(fabs(support.point_it->pos.z() - print_z), config)
});
// NOTE: increment index globaly
++support_index;
}
}
Points get_permanents(const PermanentSupports &supports, size_t support_index,
size_t layer_index, size_t part_index) {
Points result;
while (exist_permanent_support(supports, support_index, layer_index, part_index)) {
result.push_back(supports[support_index].layer_position); // copy
++support_index; // only local(temporary) increment
}
return result;
}
} // namespace
#endif // PERMANENT_SUPPORTS
LayerSupportPoints Slic3r::sla::generate_support_points(
const SupportPointGeneratorData &data,
@ -1148,12 +1161,10 @@ LayerSupportPoints Slic3r::sla::generate_support_points(
// Storage for support points used by grid
LayerSupportPoints result;
#ifdef PERMANENT_SUPPORTS
// Index into data.permanent_supports
size_t permanent_index = 0;
PermanentSupports permanent_supports =
prepare_permanent_supports(data.permanent_supports, layers, config);
#endif // PERMANENT_SUPPORTS
// grid index == part in layer index
std::vector<NearPoints> prev_grids; // same count as previous layer item size
@ -1166,11 +1177,12 @@ LayerSupportPoints Slic3r::sla::generate_support_points(
grids.reserve(layer.parts.size());
for (const LayerPart &part : layer.parts) {
if (part.prev_parts.empty()) { // Island ?
// only island add new grid
grids.emplace_back(&result);
// new island - needs support no doubt
support_island(part, grids.back(), layer.print_z, config);
size_t part_id = &part - &layer.parts.front();
if (part.prev_parts.empty()) { // Island ?
grids.emplace_back(&result); // only island add new grid
Points permanent = get_permanents(permanent_supports, permanent_index, layer_id, part_id);
support_island(part, grids.back(), layer.print_z, permanent, config);
copy_permanent_supports(grids.back(), permanent_supports, permanent_index, layer.print_z, layer_id, part_id, config);
continue;
}
@ -1179,12 +1191,11 @@ LayerSupportPoints Slic3r::sla::generate_support_points(
const LayerParts &prev_layer_parts = layers[layer_id - 1].parts;
NearPoints near_points = create_near_points(prev_layer_parts, part, prev_grids);
remove_supports_out_of_part(near_points, part, config);
#ifdef PERMANENT_SUPPORTS
size_t part_id = &part - &layer.parts.front();
if (!part.peninsulas.empty()) {
Points permanent = get_permanents(permanent_supports, permanent_index, layer_id, part_id);
support_peninsulas(part.peninsulas, near_points, layer.print_z, permanent, config);
}
copy_permanent_supports(near_points, permanent_supports, permanent_index, layer.print_z, layer_id, part_id, config);
#endif // PERMANENT_SUPPORTS
if (!part.peninsulas.empty())
support_peninsulas(part.peninsulas, near_points, layer.print_z, config);
support_part_overhangs(part, config, near_points, layer.print_z, maximal_radius);
grids.push_back(std::move(near_points));
}

View File

@ -450,7 +450,7 @@ Points rasterize(const ExPolygon &island, double distance) {
SupportIslandPoints test_island_sampling(const ExPolygon & island,
const SampleConfig &config)
{
auto points = uniform_support_island(island, config);
auto points = uniform_support_island(island, {}, config);
Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer
bool is_ok = true;