diff --git a/resources/data/sla_support.svg b/resources/data/sla_support.svg
index 7fef16f080..d131315194 100644
--- a/resources/data/sla_support.svg
+++ b/resources/data/sla_support.svg
@@ -26,13 +26,13 @@
showgrid="false"
showborder="true"
borderlayer="true"
- 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:zoom="3.3638608"
+ inkscape:cx="115.78957"
+ inkscape:cy="88.588685"
+ inkscape:window-width="1920"
+ inkscape:window-height="1129"
+ inkscape:window-x="1912"
+ inkscape:window-y="-8"
inkscape:window-maximized="1"
inkscape:current-layer="layer1" />
island supports
interface
/// Struct to store support points in KD tree to fast search for nearest ones.
@@ -799,6 +801,105 @@ std::vector load_curve_from_file() {
return {};
}
+#ifdef PERMANENT_SUPPORTS
+// Processing permanent support points
+// Permanent are manualy edited points by user
+namespace {
+struct LayerSupport
+{
+ SupportPoint point;
+ Point layer_position;
+ size_t part_index;
+};
+using LayerSupports = std::vector;
+
+size_t get_index_of_closest_part(const Point &coor, const LayerParts &parts) {
+ size_t count_lines = 0;
+ std::vector part_lines_ends;
+ part_lines_ends.reserve(parts.size());
+ for (const LayerPart &part : parts) {
+ count_lines += count_points(*part.shape);
+ part_lines_ends.push_back(count_lines);
+ }
+ Linesf lines;
+ lines.reserve(count_lines);
+ for (const LayerPart &part : parts)
+ append(lines, to_linesf({*part.shape}));
+ AABBTreeIndirect::Tree<2, double> tree =
+ AABBTreeLines::build_aabb_tree_over_indexed_lines(lines);
+
+ size_t line_idx = std::numeric_limits::max();
+ Vec2d coor_d = coor.cast();
+ Vec2d hit_point;
+ [[maybe_unused]] double distance_sq =
+ AABBTreeLines::squared_distance_to_indexed_lines(lines, tree, coor_d, line_idx, hit_point);
+
+ // Find part index of closest line
+ for (size_t part_index = 0; part_index < part_lines_ends.size(); ++part_index)
+ if (line_idx < part_lines_ends[part_index]) {
+ // check point lais inside prev or next part shape
+ // When assert appear check that part index is really the correct one
+ assert(union_ex(
+ get_polygons(parts[part_index].prev_parts),
+ get_polygons(parts[part_index].next_parts)).contains(coor));
+ return part_index;
+ }
+
+ assert(false); // not found
+ return 0;
+}
+
+LayerSupports supports_for_layer(const SupportPoints &points, size_t index,
+ float print_z, const LayerParts &parts) {
+ if (index >= points.size())
+ return {};
+
+ LayerSupports result;
+ for (const SupportPoint &point = points[index]; point.pos.z() < print_z; ++index) {
+ // find layer part for support
+ size_t part_index = parts.size();
+ Point coor(static_cast(scale_(point.pos.x())),
+ static_cast(scale_(point.pos.y())));
+
+ // find part for support point
+ for (const LayerPart &part : parts) {
+ if (part.shape_extent.contains(coor) &&
+ part.shape->contains(coor)) {
+ // parts do not overlap each other
+ assert(part_index >= parts.size());
+ part_index = &part - &parts.front();
+ }
+ }
+ if (part_index >= parts.size()) // support point is not in any part
+ part_index = get_index_of_closest_part(coor, parts);
+ result.push_back(LayerSupport{point, coor, part_index});
+ }
+ return {};
+}
+
+///
+/// Guess range of layers by its centers
+/// NOTE: not valid range for variable layer height but divide space
+///
+///
+///
+/// Range of layers
+MinMax get_layer_range(const Layers &layers, size_t layer_id) {
+ assert(layer_id < layers.size());
+ if (layer_id >= layers.size())
+ return MinMax{0., 0.};
+
+ float print_z = layers[layer_id].print_z;
+ float min = (layer_id == 0) ? 0.f : (layers[layer_id - 1].print_z + print_z) / 2.f;
+ float max = ((layer_id + 1) < layers.size()) ?
+ (layers[layer_id + 1].print_z + print_z) / 2.f :
+ print_z + (print_z - min); // last layer guess height by prev layer center
+ return MinMax{min, max};
+}
+
+} // namespace
+#endif // PERMANENT_SUPPORTS
+
LayerSupportPoints Slic3r::sla::generate_support_points(
const SupportPointGeneratorData &data,
const SupportPointGeneratorConfig &config,
@@ -822,17 +923,33 @@ LayerSupportPoints Slic3r::sla::generate_support_points(
// Storage for support points used by grid
LayerSupportPoints result;
+ // permanent supports MUST be sorted by z
+ assert(std::is_sorted(data.permanent_supports.begin(), data.permanent_supports.end(),
+ [](const SupportPoint &a, const SupportPoint &b) { return a.pos.z() < b.pos.z(); }));
+ // Index into data.permanent_supports
+ size_t permanent_index = 0;
+ // 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
+
// grid index == part in layer index
std::vector prev_grids; // same count as previous layer item size
for (size_t layer_id = 0; layer_id < layers.size(); ++layer_id) {
const Layer &layer = layers[layer_id];
-
prepare_supports_for_layer(result, layer.print_z, config);
// grid index == part in layer index
std::vector grids;
grids.reserve(layer.parts.size());
+
+#ifdef PERMANENT_SUPPORTS
+ float layer_max_z = get_layer_range(layers, layer_id).max;
+ if (data.permanent_supports[permanent_index].pos.z() < layer_max_z){
+ LayerSupports permanent =
+ supports_for_layer(data.permanent_supports, permanent_index, layer_max_z, layer.parts);
+ }
+#endif // PERMANENT_SUPPORTS
+
for (const LayerPart &part : layer.parts) {
if (part.prev_parts.empty()) { // Island ?
// only island add new grid
diff --git a/src/libslic3r/SLA/SupportPointGenerator.hpp b/src/libslic3r/SLA/SupportPointGenerator.hpp
index 26fb305e08..1d24881324 100644
--- a/src/libslic3r/SLA/SupportPointGenerator.hpp
+++ b/src/libslic3r/SLA/SupportPointGenerator.hpp
@@ -145,6 +145,7 @@ using LayerSupportPoints = std::vector;
struct Layer
{
// Absolute distance from Zero - copy value from heights
+ // It represents center of layer(range of layer is half layer size above and belowe)
float print_z; // [in mm]
// data for one expolygon
@@ -165,6 +166,9 @@ struct SupportPointGeneratorData
// and connection between layers for its part
// NOTE: contain links into slices
Layers layers;
+
+ // Manualy edited supports by user should be permanent
+ SupportPoints permanent_supports;
};
// call during generation of support points to check cancel event
diff --git a/src/libslic3r/SLAPrintSteps.cpp b/src/libslic3r/SLAPrintSteps.cpp
index 84c6132c3c..9fabd8136e 100644
--- a/src/libslic3r/SLAPrintSteps.cpp
+++ b/src/libslic3r/SLAPrintSteps.cpp
@@ -702,8 +702,19 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
ThrowOnCancel cancel = [this]() { throw_if_canceled(); };
StatusFunction status = statuscb;
- LayerSupportPoints layer_support_points =
- generate_support_points(po.m_support_point_generator_data, config, cancel, status);
+ // update permanent support points
+ SupportPointGeneratorData &data = po.m_support_point_generator_data;
+
+ data.permanent_supports.clear();
+ for (const SupportPoint &p : po.model_object()->sla_support_points)
+ if (p.type == SupportPointType::manual_add) {
+ data.permanent_supports.push_back(p);
+ data.permanent_supports.back().pos =
+ po.trafo().cast() * data.permanent_supports.back().pos;
+ }
+ std::sort(data.permanent_supports.begin(), data.permanent_supports.end(),
+ [](const SupportPoint& p1,const SupportPoint& p2){ return p1.pos.z() < p2.pos.z(); });
+ LayerSupportPoints layer_support_points = generate_support_points(data, config, cancel, status);
const AABBMesh& emesh = po.m_supportdata->input.emesh;
// Maximal move of support point to mesh surface,
@@ -714,6 +725,10 @@ void SLAPrint::Steps::support_points(SLAPrintObject &po)
SupportPoints support_points =
move_on_mesh_surface(layer_support_points, emesh, allowed_move, cancel);
+ // Naive implementation only append permanent supports to the result
+ support_points.insert(support_points.end(),
+ data.permanent_supports.begin(), data.permanent_supports.end());
+
throw_if_canceled();
MeshSlicingParamsEx params;
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
index 3db393d502..987ab631eb 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
@@ -33,9 +33,8 @@ namespace Slic3r {
namespace GUI {
GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
-: GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles)
-{
- show_sla_supports(true);
+ : GLGizmoSlaBase(parent, icon_filename, sprite_id, slaposDrillHoles /*slaposSupportPoints*/) {
+ show_sla_supports(false);
}
bool GLGizmoSlaSupports::on_init()
@@ -144,8 +143,6 @@ void GLGizmoSlaSupports::on_render()
glsafe(::glEnable(GL_BLEND));
glsafe(::glEnable(GL_DEPTH_TEST));
- show_sla_supports(!m_editing_mode);
-
render_volumes();
render_points(selection);
@@ -198,10 +195,15 @@ void GLGizmoSlaSupports::render_points(const Selection& selection)
const Transform3d& view_matrix = camera.get_view_matrix();
shader->set_uniform("projection_matrix", camera.get_projection_matrix());
+ const ColorRGBA selected_color = ColorRGBA::REDISH();
+ const ColorRGBA hovered_color = ColorRGBA::CYAN();
+ const ColorRGBA island_color = ColorRGBA::BLUEISH();
+ const ColorRGBA inactive_color = ColorRGBA::LIGHT_GRAY();
+ const ColorRGBA manual_color = ColorRGBA::ORANGE();
+
ColorRGBA render_color;
for (size_t i = 0; i < cache_size; ++i) {
const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i];
- const bool point_selected = m_editing_mode ? m_editing_cache[i].selected : false;
const bool clipped = is_mesh_point_clipped(support_point.pos.cast());
if (i < m_point_raycasters.size()) {
@@ -211,22 +213,18 @@ void GLGizmoSlaSupports::render_points(const Selection& selection)
if (clipped)
continue;
+ render_color = support_point.type == sla::SupportPointType::manual_add ?
+ manual_color : inactive_color;
// First decide about the color of the point.
- if (size_t(m_hover_id) == i && m_editing_mode) // ignore hover state unless editing mode is active
- render_color = { 0.f, 1.f, 1.f, 1.f };
- else { // neigher hover nor picking
- bool supports_new_island = m_lock_unique_islands && support_point.type == sla::SupportPointType::island;
- if (m_editing_mode) {
- if (point_selected)
- render_color = { 1.f, 0.3f, 0.3f, 1.f};
- else
- if (supports_new_island)
- render_color = { 0.3f, 0.3f, 1.f, 1.f };
- else
- render_color = { 0.7f, 0.7f, 0.7f, 1.f };
+ if (m_editing_mode) {
+ if (size_t(m_hover_id) == i) // ignore hover state unless editing mode is active
+ render_color = hovered_color;
+ else if (m_editing_cache[i].selected)
+ render_color = selected_color;
+ else if (m_lock_unique_islands) {
+ render_color = support_point.type == sla::SupportPointType::island?
+ island_color : ColorRGBA{ 0.7f, 0.7f, 0.7f, 1.f };
}
- else
- render_color = { 0.5f, 0.5f, 0.5f, 1.f };
}
m_cone.model.set_color(render_color);
@@ -680,10 +678,16 @@ RENDER_AGAIN:
}
else { // not in editing mode:
m_imgui->disabled_begin(!is_input_enabled());
- //ImGui::AlignTextToFramePadding();
-
ImGuiPureWrap::text(m_desc.at("points_density"));
- //ImGui::SameLine(settings_sliders_left);
+ ImGui::SameLine();
+
+ if (ImGui::Checkbox("##ShowSupportStructure", &m_show_support_structure)){
+ show_sla_supports(m_show_support_structure);
+ if (m_show_support_structure)
+ reslice_until_step(slaposPad);
+ } else if (ImGui::IsItemHovered())
+ ImGui::SetTooltip("%s", _u8L("Show/Hide supporting structure").c_str());
+
const char *support_points_density = "support_points_density_relative";
float density = static_cast(get_config_options({support_points_density})[0])->value;
if (m_imgui->slider_float("##density", &density, 0.f, 200.f, "%.f %%")){
@@ -711,7 +715,7 @@ RENDER_AGAIN:
wxGetApp().obj_list()->update_and_show_object_settings_item();
auto_generate();
}
-
+
const sla::SupportPoints &supports = m_normal_cache;
int count_user_edited = 0;
int count_island = 0;
@@ -734,22 +738,26 @@ RENDER_AGAIN:
}
ImVec4 light_gray{0.4f, 0.4f, 0.4f, 1.0f};
ImGui::TextColored(light_gray, stats.c_str());
+ if (supports.empty()){
+ ImGui::SameLine();
+ if (ImGuiPureWrap::button(m_desc.at("auto_generate")))
+ auto_generate();
+ }
- ImGui::Separator(); // START temporary debug
- ImGui::Text("Between delimiters is temporary GUI");
- sla::SampleConfig &sample_config = sla::SampleConfigFactory::get_sample_config();
- if (float overhang_sample_distance = sample_config.prepare_config.discretize_overhang_step;
- m_imgui->slider_float("overhang discretization", &overhang_sample_distance, 2e-5f, 10.f, "%.2f mm")){
- sample_config.prepare_config.discretize_overhang_step = overhang_sample_distance;
- } else if (ImGui::IsItemHovered())
- ImGui::SetTooltip("Smaller will slow down. Step for discretization overhang outline for test of support need");
-
- draw_island_config();
- ImGui::Text("Distribution depends on './resources/data/sla_support.svg'\ninstruction for edit are in file");
- ImGui::Separator();
-
- if (ImGuiPureWrap::button(m_desc.at("auto_generate")))
- auto_generate();
+ //ImGui::Separator(); // START temporary debug
+ //ImGui::Text("Between delimiters is temporary GUI");
+ //sla::SampleConfig &sample_config = sla::SampleConfigFactory::get_sample_config();
+ //if (float overhang_sample_distance = sample_config.prepare_config.discretize_overhang_step;
+ // m_imgui->slider_float("overhang discretization", &overhang_sample_distance, 2e-5f, 10.f, "%.2f mm")){
+ // sample_config.prepare_config.discretize_overhang_step = overhang_sample_distance;
+ //} else if (ImGui::IsItemHovered())
+ // ImGui::SetTooltip("Smaller will slow down. Step for discretization overhang outline for test of support need");
+ //
+ //draw_island_config();
+ //ImGui::Text("Distribution depends on './resources/data/sla_support.svg'\ninstruction for edit are in file");
+ //ImGui::Separator();
+ //if (ImGuiPureWrap::button(m_desc.at("auto_generate")))
+ // auto_generate();
ImGui::Separator();
if (ImGuiPureWrap::button(m_desc.at("manual_editing")))
@@ -1180,7 +1188,7 @@ void GLGizmoSlaSupports::editing_mode_apply_changes()
mo->sla_support_points.clear();
mo->sla_support_points = m_normal_cache;
- reslice_until_step(slaposPad);
+ reslice_until_step(m_show_support_structure ? slaposPad : slaposSupportPoints);
}
}
@@ -1280,7 +1288,8 @@ void GLGizmoSlaSupports::get_data_from_backend()
for (const SLAPrintObject* po : m_parent.sla_print()->objects()) {
if (po->model_object()->id() == mo->id()) {
m_normal_cache.clear();
- auto mat = po->trafo().inverse().cast();
+
+ auto mat = po->trafo().inverse().cast(); // TODO: WTF trafo????? !!!!!!
for (const sla::SupportPoint &p : po->get_support_points())
m_normal_cache.emplace_back(sla::SupportPoint{mat * p.pos, p.head_front_radius, p.type});
@@ -1298,25 +1307,25 @@ void GLGizmoSlaSupports::auto_generate()
{
//wxMessageDialog dlg(GUI::wxGetApp().plater(),
MessageDialog dlg(GUI::wxGetApp().plater(),
- _L("Autogeneration will erase all manually edited points.") + "\n\n" +
- _L("Are you sure you want to do it?") + "\n",
+ _L("Autogeneration with manually edited points is inperfect but preserve wanted postion of supports.") + "\n\n" +
+ _L("Do you want to remove manually edited points?") + "\n",
_L("Warning"), wxICON_WARNING | wxYES | wxNO);
-
ModelObject* mo = m_c->selection_info()->model_object();
-
- if (mo->sla_points_status != sla::PointsStatus::UserModified || m_normal_cache.empty() || dlg.ShowModal() == wxID_YES) {
- Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points"));
- wxGetApp().CallAfter([this]() { reslice_until_step(slaposSupportPoints); });
- mo->sla_points_status = sla::PointsStatus::Generating;
+ if (mo->sla_points_status == sla::PointsStatus::UserModified &&
+ dlg.ShowModal() == wxID_YES) {
+ mo->sla_support_points.clear();
}
+ Plater::TakeSnapshot snapshot(wxGetApp().plater(), _L("Autogenerate support points"));
+ wxGetApp().CallAfter([this]() { reslice_until_step(
+ m_show_support_structure ? slaposPad : slaposSupportPoints); });
+ mo->sla_points_status = sla::PointsStatus::Generating;
}
-
-
void GLGizmoSlaSupports::switch_to_editing_mode()
{
wxGetApp().plater()->enter_gizmos_stack();
m_editing_mode = true;
+ show_sla_supports(false);
m_editing_cache.clear();
for (const sla::SupportPoint& sp : m_normal_cache)
m_editing_cache.emplace_back(sp);
@@ -1330,6 +1339,7 @@ void GLGizmoSlaSupports::disable_editing_mode()
{
if (m_editing_mode) {
m_editing_mode = false;
+ show_sla_supports(m_show_support_structure);
wxGetApp().plater()->leave_gizmos_stack();
m_parent.set_as_dirty();
unregister_point_raycasters_for_picking();
diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
index eff8f09483..4fe62a378c 100644
--- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
+++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp
@@ -93,6 +93,7 @@ private:
void draw_island_config();
+ bool m_show_support_structure = false;
bool m_lock_unique_islands = false;
bool m_editing_mode = false; // Is editing mode active?
float m_new_point_head_diameter; // Size of a new point.