Add support settings into GUI

This commit is contained in:
Filip Sykala - NTB T15p 2024-10-10 10:26:24 +02:00 committed by Lukas Matena
parent 02ca74cced
commit 74db5d1ed2
13 changed files with 459 additions and 278 deletions

View File

@ -26,13 +26,13 @@
showgrid="false"
showborder="true"
borderlayer="true"
inkscape:zoom="3.3638608"
inkscape:cx="92.601929"
inkscape:cy="110.88449"
inkscape:window-width="1920"
inkscape:window-height="1129"
inkscape:window-x="1912"
inkscape:window-y="-8"
inkscape:zoom="19.02887"
inkscape:cx="14.845863"
inkscape:cy="132.29897"
inkscape:window-width="2400"
inkscape:window-height="1261"
inkscape:window-x="-9"
inkscape:window-y="-9"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
<defs
@ -130,7 +130,7 @@
id="path12735" />
<path
style="fill:none;stroke:#b50000;stroke-width:0.4;stroke-linejoin:round;stroke-dasharray:none;stop-color:#000000"
d="M 5,0 6,3.8987591 8,15 10,40"
d="M 4,0 6,3.8987591 8,15 10,40"
id="path3587"
sodipodi:nodetypes="cccc" />
<text
@ -174,7 +174,7 @@
style="font-weight:normal;font-size:2.11667px;stroke-width:0.4"
x="24.711193"
y="63.303635"
id="tspan9676" /></text>
id="tspan9676">Last virtual point of curve is [last.x, ∞]</tspan></text>
<text
xml:space="preserve"
style="font-size:2.11667px;line-height:1.3;font-family:'Cascadia Code';-inkscape-font-specification:'Cascadia Code';text-align:center;text-decoration-color:#000000;text-anchor:middle;fill:#364e59;stroke:none;stroke-width:0.4;stroke-linejoin:round;stroke-dasharray:none;stop-color:#000000"
@ -356,6 +356,6 @@
id="text17458"><tspan
sodipodi:role="line"
id="tspan17456"
style="stroke-width:2.5"></tspan></text>
style="stroke-width:2.5" /></text>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -473,6 +473,7 @@ set(SLIC3R_SOURCES
SLA/SupportIslands/PostProcessNeighbors.cpp
SLA/SupportIslands/PostProcessNeighbors.hpp
SLA/SupportIslands/SampleConfig.hpp
SLA/SupportIslands/SampleConfigFactory.cpp
SLA/SupportIslands/SampleConfigFactory.hpp
SLA/SupportIslands/SampleIslandUtils.cpp
SLA/SupportIslands/SampleIslandUtils.hpp

View File

@ -13,12 +13,12 @@ struct SampleConfig
{
// Every point on island has at least one support point in maximum distance
// MUST be bigger than zero
coord_t max_distance = 2;
coord_t half_distance = 1; // has to be half od max_distance
coord_t max_distance = static_cast<coord_t>(scale_(5.));
coord_t half_distance = static_cast<coord_t>(scale_(2.5)); // has to be half od max_distance
// Support point head radius
// MUST be bigger than zero
coord_t head_radius = 1; // [nano meter]
coord_t head_radius = static_cast<coord_t>(scale_(.4)); // [nano meter]
// When it is possible, there will be this minimal distance from outline.
// zero when head should be on outline
@ -27,7 +27,7 @@ struct SampleConfig
// Measured as sum of VD edge length from outline
// Used only when there is no space for outline offset on first/last point
// Must be bigger than minimal_distance_from_outline
coord_t maximal_distance_from_outline = 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.
@ -44,23 +44,23 @@ struct SampleConfig
// Maximal length of longest path in voronoi diagram to be island
// supported only by one single support point this point will be in center of path.
coord_t max_length_for_one_support_point = 1.;
coord_t max_length_for_one_support_point = static_cast<coord_t>(scale_(1.));
// Maximal length of island supported by 2 points
coord_t max_length_for_two_support_points = 1.;
coord_t max_length_for_two_support_points = static_cast<coord_t>(scale_(1.));
// Maximal width of line island supported in the middle of line
// Must be greater or equal to min_width_for_outline_support
coord_t max_width_for_center_support_line = 1.;
coord_t max_width_for_center_support_line = static_cast<coord_t>(scale_(1.));
// Minimal width to be supported by outline
// Must be smaller or equal to max_width_for_center_support_line
coord_t min_width_for_outline_support = 1.;
coord_t min_width_for_outline_support = static_cast<coord_t>(scale_(1.));
// Term criteria for end of alignment
// Minimal change in manhatn move of support position before termination
coord_t minimal_move = 1000; // in nanometers, devide from print resolution to quater pixel
coord_t minimal_move = static_cast<coord_t>(scale_(.01)); // devide from print resolution to quater pixel
// Maximal count of align iteration
size_t count_iteration = 100;
@ -75,7 +75,7 @@ struct SampleConfig
// There is no need to calculate with precisse island
// NOTE: Slice of Cylinder bottom has tip of trinagles on contour
// (neighbor coordinate - create issue in voronoi)
double simplification_tolerance = 1e4; // [nm]
double simplification_tolerance = scale_(0.05 /*mm*/);
};
} // namespace Slic3r::sla
#endif // slic3r_SLA_SuppotstIslands_SampleConfig_hpp_

View File

@ -0,0 +1,104 @@
#include "SampleConfigFactory.hpp"
using namespace Slic3r::sla;
bool SampleConfigFactory::verify(SampleConfig &cfg) {
auto verify_max = [](coord_t &c, coord_t max) {
assert(c <= max);
if (c > max) {
c = max;
return false;
}
return true;
};
auto verify_min = [](coord_t &c, coord_t min) {
assert(c >= min);
if (c < min) {
c = min;
return false;
}
return true;
};
auto verify_min_max = [](coord_t &min, coord_t &max) {
// min must be smaller than max
assert(min < max);
if (min > max) {
std::swap(min, max);
return false;
} else if (min == max) {
min /= 2; // cut in half
return false;
}
return true;
};
bool res = true;
res &= verify_min_max(cfg.max_length_for_one_support_point, cfg.max_length_for_two_support_points);
res &= verify_min_max(cfg.min_width_for_outline_support, cfg.max_width_for_center_support_line); // check histeresis
res &= verify_max(cfg.max_length_for_one_support_point,
2 * cfg.max_distance +
2 * cfg.head_radius +
2 * cfg.minimal_distance_from_outline);
res &= verify_min(cfg.max_length_for_one_support_point,
2 * cfg.head_radius + 2 * cfg.minimal_distance_from_outline);
res &= verify_max(cfg.max_length_for_two_support_points,
2 * cfg.max_distance +
2 * 2 * cfg.head_radius +
2 * cfg.minimal_distance_from_outline);
res &= verify_min(cfg.max_width_for_center_support_line,
2 * cfg.head_radius + 2 * cfg.minimal_distance_from_outline);
res &= verify_max(cfg.max_width_for_center_support_line,
2 * cfg.max_distance + 2 * cfg.head_radius);
if (!res) while (!verify(cfg));
return res;
}
SampleConfig SampleConfigFactory::create(float support_head_diameter_in_mm)
{
coord_t head_diameter = static_cast<coord_t>(scale_(support_head_diameter_in_mm));
coord_t minimal_distance = head_diameter * 7;
coord_t min_distance = head_diameter / 2 + minimal_distance;
coord_t max_distance = 3 * min_distance;
// TODO: find valid params !!!!
SampleConfig result;
result.max_distance = max_distance;
result.half_distance = result.max_distance / 2;
result.head_radius = head_diameter / 2;
result.minimal_distance_from_outline = result.head_radius;
result.maximal_distance_from_outline = result.max_distance/3;
assert(result.minimal_distance_from_outline < result.maximal_distance_from_outline);
result.minimal_support_distance = result.minimal_distance_from_outline +
result.half_distance;
result.min_side_branch_length = 2 * result.minimal_distance_from_outline + result.max_distance/4;
result.max_length_for_one_support_point =
max_distance / 3 +
2 * result.minimal_distance_from_outline +
head_diameter;
result.max_length_for_two_support_points =
result.max_length_for_one_support_point + max_distance / 2;
result.max_width_for_center_support_line =
2 * head_diameter + 2 * result.minimal_distance_from_outline +
max_distance / 2;
result.min_width_for_outline_support = result.max_width_for_center_support_line - 2 * head_diameter;
result.outline_sample_distance = 3*result.max_distance/4;
// Align support points
// TODO: propagate print resolution
result.minimal_move = scale_(0.1); // 0.1 mm is enough
// [in nanometers --> 0.01mm ], devide from print resolution to quater pixel is too strict
result.count_iteration = 30; // speed VS precission
result.max_align_distance = result.max_distance / 2;
verify(result);
return result;
}
std::optional<SampleConfig> SampleConfigFactory::gui_sample_config_opt;
SampleConfig &SampleConfigFactory::get_sample_config() {
// init config
if (!gui_sample_config_opt.has_value())
// create default configuration
gui_sample_config_opt = sla::SampleConfigFactory::create(.4f);
return *gui_sample_config_opt;
}

View File

@ -1,8 +1,9 @@
#ifndef slic3r_SLA_SuppotstIslands_SampleConfigFactory_hpp_
#define slic3r_SLA_SuppotstIslands_SampleConfigFactory_hpp_
#include <optional>
#include "SampleConfig.hpp"
#include "../SupportPointGenerator.hpp"
#include "libslic3r/PrintConfig.hpp"
namespace Slic3r::sla {
@ -14,78 +15,15 @@ class SampleConfigFactory
public:
SampleConfigFactory() = delete;
// factory method to iniciate config
static SampleConfig create(const SupportPointGeneratorConfig &config)
{
coord_t head_diameter = scale_((double)config.head_diameter.min);
coord_t minimal_distance = head_diameter * 7;
coord_t min_distance = head_diameter / 2 + minimal_distance;
coord_t max_distance = 3 * min_distance;
// TODO: find valid params !!!!
SampleConfig result;
result.max_distance = max_distance;
result.half_distance = result.max_distance / 2;
result.head_radius = head_diameter / 2;
result.minimal_distance_from_outline = result.head_radius;
result.maximal_distance_from_outline = result.max_distance/3;
assert(result.minimal_distance_from_outline < result.maximal_distance_from_outline);
result.minimal_support_distance = result.minimal_distance_from_outline +
result.half_distance;
static bool verify(SampleConfig &cfg);
static SampleConfig create(float support_head_diameter_in_mm);
result.min_side_branch_length = 2 * result.minimal_distance_from_outline;
result.max_length_for_one_support_point =
2 * result.minimal_distance_from_outline +
head_diameter;
coord_t max_length_for_one_support_point =
2 * max_distance +
head_diameter +
2 * result.minimal_distance_from_outline;
if (result.max_length_for_one_support_point > max_length_for_one_support_point)
result.max_length_for_one_support_point = max_length_for_one_support_point;
coord_t min_length_for_one_support_point =
2 * head_diameter +
2 * result.minimal_distance_from_outline;
if (result.max_length_for_one_support_point < min_length_for_one_support_point)
result.max_length_for_one_support_point = min_length_for_one_support_point;
result.max_length_for_two_support_points =
2 * max_distance + 2 * head_diameter +
2 * result.minimal_distance_from_outline;
coord_t max_length_for_two_support_points =
2 * max_distance +
2 * head_diameter +
2 * result.minimal_distance_from_outline;
if (result.max_length_for_two_support_points > max_length_for_two_support_points)
result.max_length_for_two_support_points = max_length_for_two_support_points;
assert(result.max_length_for_two_support_points > result.max_length_for_one_support_point);
result.max_width_for_center_support_line =
2 * head_diameter + 2 * result.minimal_distance_from_outline +
max_distance / 2;
coord_t min_width_for_center_support_line = head_diameter + 2 * result.minimal_distance_from_outline;
if (result.max_width_for_center_support_line < min_width_for_center_support_line)
result.max_width_for_center_support_line = min_width_for_center_support_line;
coord_t max_width_for_center_support_line = 2 * max_distance + head_diameter;
if (result.max_width_for_center_support_line > max_width_for_center_support_line)
result.max_width_for_center_support_line = max_width_for_center_support_line;
result.min_width_for_outline_support = result.max_width_for_center_support_line - 2 * head_diameter;
assert(result.min_width_for_outline_support <= result.max_width_for_center_support_line);
result.outline_sample_distance = 3*result.max_distance/4;
// Align support points
// TODO: propagate print resolution
result.minimal_move = scale_(0.1); // 0.1 mm is enough
// [in nanometers --> 0.01mm ], devide from print resolution to quater pixel is too strict
result.count_iteration = 30; // speed VS precission
result.max_align_distance = result.max_distance / 2;
return result;
}
private:
// TODO: REMOVE IT. Do not use in production
// Global variable to temporary set configuration from GUI into SLA print steps
static std::optional<SampleConfig> gui_sample_config_opt;
public:
static SampleConfig &get_sample_config();
};
} // namespace Slic3r::sla
#endif // slic3r_SLA_SuppotstIslands_SampleConfigFactory_hpp_

View File

@ -57,26 +57,84 @@ const ExPolygon &get_expolygon_with_biggest_contour(const ExPolygons &expolygons
}
return *biggest;
}
/// <summary>
/// When radius of all points is smaller than max radius set output center and return true
/// </summary>
/// <param name="points"></param>
/// <param name="max_radius"></param>
/// <param name="output_center"></param>
/// <returns>True when Bounding box of points is smaller than max radius</returns>
bool get_center(const Points &points, coord_t max_radius, Point& output_center){
if (points.size()<=2)
return false;
auto it = points.begin();
Point min = *it;
Point max = *it;
for (++it; it != points.end(); ++it) {
if (min.x() > it->x()) {
min.x() = it->x();
if (max.x() - min.x() > max_radius)
return false;
} else if(max.x() < it->x()) {
max.x() = it->x();
if (max.x() - min.x() > max_radius)
return false;
}
if (min.y() > it->y()) {
min.y() = it->y();
if (max.y() - min.y() > max_radius)
return false;
} else if (max.y() < it->y()) {
max.y() = it->y();
if (max.y() - min.y() > max_radius)
return false;
}
}
// prevent overflow of point range, no care about 1 size
output_center = min/2 + max/2;
return true;
}
/// <summary>
/// Decrease level of detail
/// </summary>
/// <param name="island">Polygon to reduce count of points</param>
/// <param name="config">Define progressivness of reduction</param>
/// <returns>Simplified island</returns>
ExPolygon get_simplified(const ExPolygon &island, const SampleConfig &config) {
//// closing similar to FDM arachne do before voronoi inspiration in make_expolygons inside TriangleMeshSlicer
//float closing_radius = scale_(0.0499f);
//float offset_out = closing_radius;
//float offset_in = -closing_radius;
//ExPolygons closed_expolygons = offset2_ex({island}, offset_out, offset_in); // mitter
//ExPolygon closed_expolygon = get_expolygon_with_biggest_contour(closed_expolygons);
//// "Close" operation still create neighbor pixel for sharp triangle tip - cause VD issues
ExPolygons simplified_expolygons = island.simplify(config.simplification_tolerance);
return simplified_expolygons.empty() ?
island : get_expolygon_with_biggest_contour(simplified_expolygons);
}
} // namespace
SupportIslandPoints SampleIslandUtils::uniform_cover_island(
const ExPolygon &island, const SampleConfig &config
) {
// closing similar to FDM arachne do before voronoi
// inspired by make_expolygons inside TriangleMeshSlicer
float closing_radius = scale_(0.0499f);
float offset_out = closing_radius;
float offset_in = -closing_radius;
ExPolygons closed_expolygons = offset2_ex({island}, offset_out, offset_in); // mitter
ExPolygon closed_expolygon = get_expolygon_with_biggest_contour(closed_expolygons);
ExPolygon simplified_island = get_simplified(island, config);
// "Close" operation create neighbor pixel for sharp triangle tip
double tolerance = scale_(0.05);
ExPolygons simplified_expolygons = island.simplify(tolerance);
ExPolygon simplified_expolygon = get_expolygon_with_biggest_contour(simplified_expolygons);
// 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));
return result;
}
Slic3r::Geometry::VoronoiDiagram vd;
Lines lines = to_lines(simplified_expolygon);
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);
@ -976,7 +1034,6 @@ SupportIslandPoints SampleIslandUtils::sample_voronoi_graph(
// every island has to have a point on contour
assert(start_node != nullptr);
longest_path = VoronoiGraphUtils::create_longest_path(start_node);
// longest_path = create_longest_path_recursive(start_node);
#ifdef SLA_SAMPLE_ISLAND_UTILS_STORE_VORONOI_GRAPH_TO_SVG_PATH
{

View File

@ -34,6 +34,7 @@ public:
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
undefined
};

View File

@ -10,7 +10,6 @@
#include "libslic3r/KDTreeIndirect.hpp"
// SupportIslands
#include "libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp"
#include "libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp"
using namespace Slic3r;
@ -232,7 +231,6 @@ void support_part_overhangs(
(const LayerSupportPoint &support_point, const Point &p) -> bool {
// Debug visualization of all sampled outline
//return false;
coord_t r = support_point.current_radius;
Point dp = support_point.position_on_layer - p;
if (std::abs(dp.x()) > r) return false;
@ -269,9 +267,8 @@ void support_part_overhangs(
/// <param name="part_z">z coordinate of part</param>
/// <param name="cfg"></param>
void support_island(const LayerPart &part, NearPoints& near_points, float part_z,
const SupportPointGeneratorConfig &cfg) {
SampleConfig sample_cfg = SampleConfigFactory::create(cfg);
SupportIslandPoints samples = SampleIslandUtils::uniform_cover_island(*part.shape, sample_cfg);
const SupportPointGeneratorConfig &cfg) {
SupportIslandPoints samples = SampleIslandUtils::uniform_cover_island(*part.shape, cfg.island_configuration);
//samples = {std::make_unique<SupportIslandPoint>(island.contour.centroid())};
for (const SupportIslandPointPtr &sample : samples)
near_points.add(LayerSupportPoint{
@ -443,6 +440,12 @@ Points sample_overhangs(const LayerPart& part, double dist2) {
void prepare_supports_for_layer(LayerSupportPoints &supports, float layer_z,
const SupportPointGeneratorConfig &config) {
auto set_radius = [&config](LayerSupportPoint &support, float radius) {
if (!is_approx(config.density_relative, 1.f, 1e-4f)) // exist relative density
radius /= config.density_relative;
support.current_radius = static_cast<coord_t>(scale_(radius));
};
const std::vector<Vec2f>& curve = config.support_curve;
// calculate support area for each support point as radius
// IMPROVE: use some offsets of previous supported island
@ -458,7 +461,7 @@ void prepare_supports_for_layer(LayerSupportPoints &supports, float layer_z,
if ((index+1) >= curve.size()) {
// set maximal radius
support.current_radius = static_cast<coord_t>(scale_(curve.back().x()));
set_radius(support, curve.back().x());
continue;
}
// interpolate radius on input curve
@ -467,7 +470,7 @@ void prepare_supports_for_layer(LayerSupportPoints &supports, float layer_z,
assert(a.y() <= diff_z && diff_z <= b.y());
float t = (diff_z - a.y()) / (b.y() - a.y());
assert(0 <= t && t <= 1);
support.current_radius = static_cast<coord_t>(scale_(a.x() + t * (b.x() - a.x())));
set_radius(support, a.x() + t * (b.x() - a.x()));
}
}

View File

@ -13,6 +13,7 @@
#include "libslic3r/Point.hpp"
#include "libslic3r/ExPolygon.hpp"
#include "libslic3r/SLA/SupportPoint.hpp"
#include "libslic3r/SLA/SupportIslands/SampleConfig.hpp"
namespace Slic3r::sla {
@ -31,7 +32,7 @@ struct SupportPointGeneratorConfig{
/// <summary>
/// Size range for support point interface (head)
/// </summary>
MinMax<float> head_diameter = {0.2f, 0.6f}; // [in mm]
float head_diameter = 0.4f; // [in mm]
// FIXME: calculate actual pixel area from printer config:
// const float pixel_area =
@ -46,6 +47,9 @@ struct SupportPointGeneratorConfig{
// y axis .. mean difference of height(Z)
// Points of lines [in mm]
std::vector<Vec2f> support_curve;
// Configuration for sampling island
SampleConfig island_configuration;
};
struct LayerPart; // forward decl.

View File

@ -53,6 +53,7 @@
#include "libslic3r/SLA/RasterBase.hpp"
#include "libslic3r/SLA/SupportTree.hpp"
#include "libslic3r/SLA/SupportTreeStrategies.hpp"
#include "libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/TriangleMesh.hpp"
@ -627,99 +628,104 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
BOOST_LOG_TRIVIAL(debug) << "Support point count "
<< mo.sla_support_points.size();
// Unless the user modified the points or we already did the calculation,
// we will do the autoplacement. Otherwise we will just blindly copy the
// frontend data into the backend cache.
if (mo.sla_points_status != sla::PointsStatus::UserModified) {
throw_if_canceled();
sla::SupportPointGeneratorConfig config;
const SLAPrintObjectConfig& cfg = po.config();
// the density config value is in percents:
config.density_relative = float(cfg.support_points_density_relative / 100.f);
switch (cfg.support_tree_type) {
case sla::SupportTreeType::Default:
case sla::SupportTreeType::Organic:
config.head_diameter = {float(cfg.support_head_front_diameter), .0};
break;
case sla::SupportTreeType::Branching:
config.head_diameter = {float(cfg.branchingsupport_head_front_diameter), .0};
break;
}
// scaling for the sub operations
double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0;
double init = current_status();
auto statuscb = [this, d, init](unsigned st)
{
double current = init + st * d;
if(std::round(current_status()) < std::round(current))
report_status(current, OBJ_STEP_LABELS(slaposSupportPoints));
};
// Construction of this object does the calculation.
throw_if_canceled();
// TODO: filter small unprintable islands in slices
// (Island with area smaller than 1 pixel was skipped in support generator)
std::vector<ExPolygons> slices = po.get_model_slices(); // copy
const std::vector<float>& heights = po.m_model_height_levels;
sla::ThrowOnCancel cancel = [this]() { throw_if_canceled(); };
sla::StatusFunction status = statuscb;
sla::SupportPointGeneratorData data =
sla::prepare_generator_data(std::move(slices), heights, cancel, status);
sla::LayerSupportPoints layer_support_points =
sla::generate_support_points(data, config, cancel, status);
const AABBMesh& emesh = po.m_supportdata->input.emesh;
// Maximal move of support point to mesh surface,
// no more than height of layer
assert(po.m_model_height_levels.size() > 1);
double allowed_move = (po.m_model_height_levels[1] - po.m_model_height_levels[0]) +
std::numeric_limits<float>::epsilon();
sla::SupportPoints support_points =
sla::move_on_mesh_surface(layer_support_points, emesh, allowed_move, cancel);
throw_if_canceled();
MeshSlicingParamsEx params;
params.closing_radius = float(po.config().slice_closing_radius.value);
std::vector<ExPolygons> blockers =
slice_volumes(po.model_object()->volumes,
po.m_model_height_levels, po.trafo(), params,
[](const ModelVolume *vol) {
return vol->is_support_blocker();
});
std::vector<ExPolygons> enforcers =
slice_volumes(po.model_object()->volumes,
po.m_model_height_levels, po.trafo(), params,
[](const ModelVolume *vol) {
return vol->is_support_enforcer();
});
SuppPtMask mask{blockers, enforcers, po.config().support_enforcers_only.getBool()};
filter_support_points_by_modifiers(support_points, mask, po.m_model_height_levels);
po.m_supportdata->input.pts = support_points;
BOOST_LOG_TRIVIAL(debug)
<< "Automatic support points: "
<< po.m_supportdata->input.pts.size();
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
// the update status to GLGizmoSlaSupports
report_status(-1, _u8L("Generating support points"),
SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
} else {
if (mo.sla_points_status == sla::PointsStatus::UserModified) {
// There are either some points on the front-end, or the user
// removed them on purpose. No calculation will be done.
po.m_supportdata->input.pts = po.transformed_support_points();
return;
}
// Unless the user modified the points or we already did the calculation,
// we will do the autoplacement. Otherwise we will just blindly copy the
// frontend data into the backend cache.
// if (mo.sla_points_status != sla::PointsStatus::UserModified)
throw_if_canceled();
const SLAPrintObjectConfig& cfg = po.config();
// the density config value is in percents:
sla::SupportPointGeneratorConfig config;
config.density_relative = float(cfg.support_points_density_relative / 100.f);
switch (cfg.support_tree_type) {
case sla::SupportTreeType::Default:
case sla::SupportTreeType::Organic:
config.head_diameter = float(cfg.support_head_front_diameter);
break;
case sla::SupportTreeType::Branching:
config.head_diameter = float(cfg.branchingsupport_head_front_diameter);
break;
}
// copy current configuration for sampling islands
config.island_configuration = sla::SampleConfigFactory::get_sample_config();
// scaling for the sub operations
double d = objectstep_scale * OBJ_STEP_LEVELS[slaposSupportPoints] / 100.0;
double init = current_status();
auto statuscb = [this, d, init](unsigned st)
{
double current = init + st * d;
if(std::round(current_status()) < std::round(current))
report_status(current, OBJ_STEP_LABELS(slaposSupportPoints));
};
// Construction of this object does the calculation.
throw_if_canceled();
// TODO: filter small unprintable islands in slices
// (Island with area smaller than 1 pixel was skipped in support generator)
std::vector<ExPolygons> slices = po.get_model_slices(); // copy
const std::vector<float>& heights = po.m_model_height_levels;
sla::ThrowOnCancel cancel = [this]() { throw_if_canceled(); };
sla::StatusFunction status = statuscb;
sla::SupportPointGeneratorData data =
sla::prepare_generator_data(std::move(slices), heights, cancel, status);
sla::LayerSupportPoints layer_support_points =
sla::generate_support_points(data, config, cancel, status);
const AABBMesh& emesh = po.m_supportdata->input.emesh;
// Maximal move of support point to mesh surface,
// no more than height of layer
assert(po.m_model_height_levels.size() > 1);
double allowed_move = (po.m_model_height_levels[1] - po.m_model_height_levels[0]) +
std::numeric_limits<float>::epsilon();
sla::SupportPoints support_points =
sla::move_on_mesh_surface(layer_support_points, emesh, allowed_move, cancel);
throw_if_canceled();
MeshSlicingParamsEx params;
params.closing_radius = float(po.config().slice_closing_radius.value);
std::vector<ExPolygons> blockers =
slice_volumes(po.model_object()->volumes,
po.m_model_height_levels, po.trafo(), params,
[](const ModelVolume *vol) {
return vol->is_support_blocker();
});
std::vector<ExPolygons> enforcers =
slice_volumes(po.model_object()->volumes,
po.m_model_height_levels, po.trafo(), params,
[](const ModelVolume *vol) {
return vol->is_support_enforcer();
});
SuppPtMask mask{blockers, enforcers, po.config().support_enforcers_only.getBool()};
filter_support_points_by_modifiers(support_points, mask, po.m_model_height_levels);
po.m_supportdata->input.pts = support_points;
BOOST_LOG_TRIVIAL(debug)
<< "Automatic support points: "
<< po.m_supportdata->input.pts.size();
// Using RELOAD_SLA_SUPPORT_POINTS to tell the Plater to pass
// the update status to GLGizmoSlaSupports
report_status(-1, _u8L("Generating support points"),
SlicingStatus::RELOAD_SLA_SUPPORT_POINTS);
}
void SLAPrint::Steps::support_tree(SLAPrintObject &po)

View File

@ -22,6 +22,8 @@
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/SLAPrint.hpp"
#include "libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp"
static const double CONE_RADIUS = 0.25;
static const double CONE_HEIGHT = 0.75;
@ -677,6 +679,92 @@ RENDER_AGAIN:
}
else { // not in editing mode:
m_imgui->disabled_begin(!is_input_enabled());
if (int density = static_cast<const ConfigOptionInt*>(get_config_options({"support_points_density_relative"})[0])->value;
ImGui::SliderInt("points_density", &density, 0, 200, "%d \%")) {
mo->config.set("support_points_density_relative", density);
} else if (ImGui::IsItemHovered()) {
ImGui::SetTooltip("Divider for the supported radius\nSmaller mean less point(75% -> supported radius is enlaged to 133%, for 50% it is 200% of radius)\nLarger mean more points(125% -> supported radius is reduced to 80%, for value 150% it is 66% of radius, for 200% -> 50%)");
}
if (ImGui::TreeNode("Support islands:")) {
sla::SampleConfig &sample_config = sla::SampleConfigFactory::get_sample_config();
bool exist_change = false;
if (float simplification_tolerance = unscale<float>(sample_config.simplification_tolerance); // [in mm]
ImGui::InputFloat("input simplify", &simplification_tolerance, .1f, 1.f, "%.2f mm")) {
sample_config.simplification_tolerance = scale_(simplification_tolerance);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("There is no need to calculate with precisse island\nNOTE: Slice of Cylinder bottom has tip of trinagles on contour\n(neighbor coordinate -> create issue in boost::voronoi)");
if (float max_distance = unscale<float>(sample_config.max_distance); // [in mm]
ImGui::InputFloat("Max dist", &max_distance, .1f, 1.f, "%.2f mm")) {
sample_config.max_distance = scale_(max_distance);
sample_config.half_distance = sample_config.max_distance / 2;
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Every support point on island has at least one support point in maximum distance\nMUST be bigger than zero");
ImGui::SameLine(); ImGui::Text("half is %.2f", unscale<float>(sample_config.half_distance));
if (float minimal_distance_from_outline = unscale<float>(sample_config.minimal_distance_from_outline); // [in mm]
ImGui::InputFloat("from outline", &minimal_distance_from_outline, .1f, 1.f, "%.2f mm")) {
sample_config.minimal_distance_from_outline = scale_(minimal_distance_from_outline);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("When it is possible, there will be this minimal distance from outline.\nZERO when head center should be on outline\nSHOULD be positive number");
ImGui::SameLine();
if (float maximal_distance_from_outline = unscale<float>(sample_config.maximal_distance_from_outline); // [in mm]
ImGui::InputFloat("max from outline", &maximal_distance_from_outline, .1f, 1.f, "%.2f mm")) {
sample_config.maximal_distance_from_outline = scale_(maximal_distance_from_outline);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Measured as sum of VD edge length from outline\nUsed only when there is no space for outline offset on first/last point\nMust be bigger than minimal_distance_from_outline");
ImGui::Text("max_interesting_angle is %.0f", float(sample_config.max_interesting_angle*180/M_PI));
if (ImGui::IsItemHovered()) ImGui::SetTooltip(" When angle on outline is smaller than max_interesting_angle\nthan create unmovable support point.\nShould be in range from 90 to 180");
if (float minimal_support_distance = unscale<float>(sample_config.minimal_support_distance); // [in mm]
ImGui::InputFloat("Thin dist", &minimal_support_distance, .1f, 1.f, "%.2f mm")) {
sample_config.minimal_support_distance = scale_(minimal_support_distance);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Distinguish when to add support point on Voronoi Diagram\nMUST be bigger than minimal_distance_from_outline\nSmaller -> more supports AND Larger -> less amount");
if (float min_side_branch_length = unscale<float>(sample_config.min_side_branch_length); // [in mm]
ImGui::InputFloat("min_side_branch_length", &min_side_branch_length, .1f, 1.f, "%.2f mm")) {
sample_config.min_side_branch_length = scale_(min_side_branch_length);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("minimal length of side branch to be sampled\nit is used for sampling in center only");
if (float max_for_one = unscale<float>(sample_config.max_length_for_one_support_point); // [in mm]
ImGui::InputFloat("Max len for one", &max_for_one, .1f, 1.f, "%.2f mm")) {
sample_config.max_length_for_one_support_point = scale_(max_for_one);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Maximal island length (longest voronoi path) for support by point in path center");
if (float max_for_two = unscale<float>(sample_config.max_length_for_two_support_points); // [in mm]
ImGui::InputFloat("Max len for two", &max_for_two, .1f, 1.f, "%.2f mm")) {
sample_config.max_length_for_two_support_points = scale_(max_for_two);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Maximal island length (longest voronoi path)\n for support by 2 points on path sides");
if (float max_width_for_center_support_line = unscale<float>(sample_config.max_width_for_center_support_line); // [in mm]
ImGui::InputFloat("thin max width", &max_width_for_center_support_line, .1f, 1.f, "%.2f mm")) {
sample_config.max_width_for_center_support_line = scale_(max_width_for_center_support_line);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Maximal width of line island supported in the middle of line\nMust be greater or equal to thick min width(to make hysteresis)");
if (float min_width_for_outline_support = unscale<float>(sample_config.min_width_for_outline_support); // [in mm]
ImGui::InputFloat("thick min width", &min_width_for_outline_support, .1f, 1.f, "%.2f mm")) {
sample_config.min_width_for_outline_support = scale_(min_width_for_outline_support);
exist_change = true;
} else if (ImGui::IsItemHovered())
ImGui::SetTooltip("Minimal width to be supported by outline\nMust be smaller or equal to thin max width(to make hysteresis)");
ImGui::Text("head radius is set to %.2f", unscale<float>(sample_config.head_radius));
ImGui::Text("Alignment stop criteria: min_move(%.0f um), iter(%d x), max_VD_move(%.2f mm)", unscale<float>(sample_config.minimal_move)*1000, sample_config.count_iteration,
unscale<float>(sample_config.max_align_distance)
);
if (exist_change){
sla::SampleConfigFactory::verify(sample_config);
}
ImGui::TreePop();
}
ImGui::Text("Distribution depends on './resources/data/sla_support.svg'\ninstruction for edit are in file");
@ -720,9 +808,7 @@ RENDER_AGAIN:
// wxGetApp().obj_list()->update_and_show_object_settings_item();
//}
bool generate = ImGuiPureWrap::button(m_desc.at("auto_generate"));
if (generate)
if (ImGuiPureWrap::button(m_desc.at("auto_generate")))
auto_generate();
ImGui::Separator();

View File

@ -7,6 +7,7 @@
#include <libslic3r/ClipperUtils.hpp>
#include <libslic3r/TriangleMeshSlicer.hpp>
#include <libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp>
#include <libslic3r/SLA/SupportIslands/SampleConfig.hpp>
#include <libslic3r/SLA/SupportIslands/VoronoiGraphUtils.hpp>
#include <libslic3r/SLA/SupportIslands/SampleIslandUtils.hpp>
@ -17,7 +18,7 @@
using namespace Slic3r;
using namespace Slic3r::sla;
#define STORE_SAMPLE_INTO_SVG_FILES
//#define STORE_SAMPLE_INTO_SVG_FILES
TEST_CASE("Overhanging point should be supported", "[SupGen]") {
@ -304,9 +305,23 @@ ExPolygon create_mountains(double size) {
{size / 7, size}});
}
/// Neighbor points create trouble for voronoi - test of neccessary offseting(closing) of contour
ExPolygon create_cylinder_bottom_slice() {
indexed_triangle_set its_cylinder = its_make_cylinder(6.6551999999999998, 11.800000000000001);
MeshSlicingParams param;
Polygons polygons = slice_mesh(its_cylinder, 0.0125000002, param);
return ExPolygon{polygons.front()};
}
ExPolygon load_frog(){
TriangleMesh mesh = load_model("frog_legs.obj");
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, {0.1f});
return slices.front()[1];
}
ExPolygons createTestIslands(double size)
{
bool useFrogLeg = false;
bool useFrogLeg = false;
// need post reorganization of longest path
ExPolygons result = {
// one support point
@ -338,6 +353,9 @@ ExPolygons createTestIslands(double size)
create_tiny_wide_test_2(3 * size, 2 / 3. * size),
create_tiny_between_holes(3 * size, 2 / 3. * size),
ExPolygon(PolygonUtils::create_equilateral_triangle(scale_(18.6))),
create_cylinder_bottom_slice(),
// still problem
// three support points
ExPolygon(PolygonUtils::create_equilateral_triangle(3 * size)),
@ -347,14 +365,9 @@ ExPolygons createTestIslands(double size)
create_trinagle_with_hole(size),
create_square_with_hole(size, size / 2),
create_square_with_hole(size, size / 3)
};
if (useFrogLeg) {
TriangleMesh mesh = load_model("frog_legs.obj");
std::vector<ExPolygons> slices = slice_mesh_ex(mesh.its, {0.1f});
ExPolygon frog_leg = slices.front()[1];
result.push_back(frog_leg);
}
};
if (useFrogLeg)
result.push_back(load_frog());
return result;
}
@ -403,7 +416,7 @@ Points rasterize(const ExPolygon &island, double distance) {
SupportIslandPoints test_island_sampling(const ExPolygon & island,
const SampleConfig &config)
{
auto points = SupportPointGenerator::uniform_cover_island(island, config);
auto points = SampleIslandUtils::uniform_cover_island(island, config);
Points chck_points = rasterize(island, config.head_radius); // TODO: Use resolution of printer
bool is_ok = true;
@ -447,7 +460,7 @@ SupportIslandPoints test_island_sampling(const ExPolygon & island,
}
}
CHECK(!points.empty());
//CHECK(is_ok);
CHECK(is_ok);
// all points must be inside of island
for (const auto &point : points) { CHECK(island.contains(point->point)); }
@ -539,12 +552,6 @@ TEST_CASE("speed sampling", "[hide], [SupGen]") {
size_t count = 1;
std::vector<std::vector<Vec2f>> result1;
result1.reserve(islands.size()*count);
for (size_t i = 0; i<count; ++i)
for (const auto& island: islands)
result1.emplace_back(sample_expolygon(island, samples_per_mm2, m_rng));
std::vector<std::vector<Vec2f>> result2;
result2.reserve(islands.size()*count);
for (size_t i = 0; i < count; ++i)
@ -560,7 +567,7 @@ TEST_CASE("speed sampling", "[hide], [SupGen]") {
#ifdef STORE_SAMPLE_INTO_SVG_FILES
for (size_t i = 0; i < result1.size(); ++i) {
for (size_t i = 0; i < result2.size(); ++i) {
size_t island_index = i % islands.size();
ExPolygon &island = islands[island_index];
@ -568,9 +575,6 @@ TEST_CASE("speed sampling", "[hide], [SupGen]") {
std::string name = "sample_" + std::to_string(i) + ".svg";
SVG svg(name, LineUtils::create_bounding_box(lines));
svg.draw(island, "lightgray");
svg.draw_text({0, 0}, ("random samples " + std::to_string(result1[i].size())).c_str(), "blue");
for (Vec2f &p : result1[i])
svg.draw((p * 1e6).cast<coord_t>(), "blue", 1e6);
svg.draw_text({0., 5e6}, ("uniform samples " + std::to_string(result2[i].size())).c_str(), "green");
for (Vec2f &p : result2[i])
svg.draw((p * 1e6).cast<coord_t>(), "green", 1e6);
@ -580,16 +584,17 @@ TEST_CASE("speed sampling", "[hide], [SupGen]") {
/// <summary>
/// Check for correct sampling of island
///
/// </summary>
TEST_CASE("Small islands should be supported in center", "[SupGen], [VoronoiSkeleton]")
{
double size = 3e7;
SampleConfig cfg = create_sample_config(size);
ExPolygons islands = createTestIslands(size);
float head_diameter = .4f;
SampleConfig cfg = SampleConfigFactory::create(head_diameter);
ExPolygons islands = createTestIslands(21 * scale_(head_diameter));
for (ExPolygon &island : islands) {
// information for debug which island cause problem
[[maybe_unused]] size_t debug_index = &island - &islands.front();
// TODO: index 17 - create field again
auto points = test_island_sampling(island, cfg);
double angle = 3.14 / 3; // cca 60 degree
@ -601,46 +606,31 @@ TEST_CASE("Small islands should be supported in center", "[SupGen], [VoronoiSkel
}
}
std::vector<Vec2f> sample_old(const ExPolygon &island)
{
// Create the support point generator
static TriangleMesh mesh;
static AABBMesh emesh(mesh);
static sla::SupportPointGenerator::Config autogencfg;
//autogencfg.minimal_distance = 8.f;
static sla::SupportPointGenerator generator{emesh, autogencfg, [] {}, [](int) {}};
// tear preasure
float tp = autogencfg.tear_pressure();
size_t layer_id = 13;
coordf_t print_z = 11.f;
SupportPointGenerator::MyLayer layer(layer_id, print_z);
ExPolygon poly = island;
BoundingBox bbox(island.contour.points);
Vec2f centroid;
float area = island.area();
float h = 17.f;
sla::SupportPointGenerator::Structure s(layer, poly, bbox, centroid,area,h);
auto flag = sla::SupportPointGenerator::IslandCoverageFlags(
sla::SupportPointGenerator::icfIsNew | sla::SupportPointGenerator::icfWithBoundary);
SupportPointGenerator::PointGrid3D grid3d;
generator.uniformly_cover({island}, s, s.area * tp, grid3d, flag);
std::vector<Vec2f> result;
result.reserve(grid3d.grid.size());
for (auto g : grid3d.grid) {
const Vec3f &p = g.second.position;
Vec2f p2f(p.x(), p.y());
result.emplace_back(scale_(p2f));
}
return result;
}
//TEST_CASE("Cell polygon check", "") {
// coord_t max_distance = 9;
// Points points{Point{0,0}, Point{10,0}};
// using VD = Slic3r::Geometry::VoronoiDiagram;
// VD vd;
// vd.construct_voronoi(points.begin(), points.end());
// assert(points.size() == vd.cells().size());
// Polygons cells(points.size());
// for (const VD::cell_type &cell : vd.cells())
// cells[cell.source_index()] = VoronoiGraphUtils::to_polygon(cell, points, max_distance);
//
// REQUIRE(cells[0].size() >= 3);
// REQUIRE(cells[1].size() >= 3);
// Polygons cell_overlaps = intersection(cells[0], cells[1]);
// double area = 0;
// for (const Polygon &cell_overlap : cell_overlaps)
// area += cell_overlap.area();
// CHECK(area < 1);
//}
#include <libslic3r/SLA/SupportIslands/SampleConfigFactory.hpp>
std::vector<Vec2f> sample_filip(const ExPolygon &island)
{
static SampleConfig cfg = create_sample_config(1e6);
SupportIslandPoints points = SupportPointGenerator::uniform_cover_island(island, cfg);
SupportIslandPoints points = SampleIslandUtils::uniform_cover_island(island, cfg);
std::vector<Vec2f> result;
result.reserve(points.size());
@ -675,16 +665,8 @@ void store_sample(const std::vector<Vec2f> &samples, const ExPolygon& island)
}
TEST_CASE("Compare sampling test", "[hide]")
{
enum class Sampling {
old,
filip
} sample_type = Sampling::old;
std::function<std::vector<Vec2f>(const ExPolygon &)> sample =
(sample_type == Sampling::old) ? sample_old :
(sample_type == Sampling::filip) ? sample_filip :
nullptr;
{
std::function<std::vector<Vec2f>(const ExPolygon &)> sample = sample_filip;
ExPolygons islands = createTestIslands(1e6);
ExPolygons islands_big = createTestIslands(3e6);
islands.insert(islands.end(), islands_big.begin(), islands_big.end());

View File

@ -129,8 +129,7 @@ void test_supports(const std::string &obj_filename,
// Create the support point generator
sla::SupportPointGeneratorConfig autogencfg;
float head_diam = 2 * supportcfg.head_front_radius_mm;
autogencfg.head_diameter = {head_diam, head_diam};
autogencfg.head_diameter = 2 * supportcfg.head_front_radius_mm;
sla::ThrowOnCancel cancel = []() {};
sla::StatusFunction status = [](int) {};
sla::SupportPointGeneratorData gen_data = sla::prepare_generator_data(std::move(out.model_slices), out.slicegrid, cancel, status);