From d2ca5d3a1e79964fb0ad509d492e4dafb3acff7d Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Thu, 14 May 2026 19:44:40 +0300 Subject: [PATCH] Fix hybrid tree first-layer support base behavior and expose density setting (#13454) Fix hybrid tree first-layer support base flow and UI consistency Fix incorrect first-layer generation for hybrid tree supports. The support base on the first layer used brim flow instead of support material flow, which could lead to incorrect extrusion behavior and wrong material usage in multi-material prints. Additionally, spacing and density for the regular support part were not consistently derived from the actual first-layer support flow when first-layer and regular line widths differed. Hybrid-specific behavior is clarified: - first-layer expansion applies only to the regular support part - tree-only regions keep their existing behavior The first-layer support pattern is aligned with normal and organic tree supports to ensure consistent and meaningful density behavior. Also make first-layer support expansion and density settings visible whenever supports are enabled, as density was already affecting hybrid supports but was hidden in the UI. --- src/libslic3r/Support/TreeSupport.cpp | 40 ++++++++++++++++++++++++--- src/slic3r/GUI/ConfigManipulation.cpp | 9 ++++-- 2 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 2209afbd7b..aaf3ba4443 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -1593,13 +1593,20 @@ void TreeSupport::generate_toolpaths() } else { // base_areas - Flow flow = (layer_id == 0 && m_raft_layers == 0) ? m_object->print()->brim_flow() : support_flow; + bool support_base_on_bed = (layer_id == 0 && m_raft_layers == 0); + Flow flow = support_base_on_bed ? m_support_params.first_layer_flow : support_flow; bool need_infill = with_infill; if(m_object_config->support_base_pattern==smpDefault) need_infill &= area_group.need_infill; - std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(layer_id == 0 ? ipConcentric : m_support_params.base_fill_pattern)); + // Orca: Use rectilinear for support base on the bed + const InfillPattern base_fill_pattern = support_base_on_bed ? ipRectilinear : m_support_params.base_fill_pattern; + std::shared_ptr filler_support = std::shared_ptr(Fill::new_from_type(base_fill_pattern)); filler_support->set_bounding_box(bbox_object); - filler_support->spacing = support_spacing * support_density; // constant spacing to align support infill lines + + filler_support->spacing = + support_base_on_bed ? + flow.spacing() : // Orca: On the bed-contacting support base layer, use first-layer flow spacing directly. + support_spacing * support_density; // constant spacing to align support infill lines filler_support->angle = Geometry::deg2rad(object_config.support_angle.value); Polygons loops = to_polygons(poly); @@ -1639,7 +1646,7 @@ void TreeSupport::generate_toolpaths() // strengthen lightnings while it may make support harder. decide to enable it or not. if yes, proper values for params are remained to be tested auto& lightning_layer = generator->getTreesForLayer(printZ_to_lightninglayer[print_z]); - Flow flow = (layer_id == 0 && m_raft_layers == 0) ? m_object->print()->brim_flow() :support_flow; + Flow flow = (layer_id == 0 && m_raft_layers == 0) ? m_support_params.first_layer_flow : support_flow; ExPolygons areas = offset_ex(ts_layer->base_areas, -flow.scaled_spacing()); for (auto& area : areas) @@ -2342,6 +2349,31 @@ void TreeSupport::draw_circles() floor_areas = std::move(new_floor_areas); } + // Orca: Hybrid tree first-layer expansion belongs only to the normal-support + // part. area_poly is collected from ePolygon nodes above, which are the normal + // support nodes in Hybrid mode. Apply the expansion before area_groups and + // lslices are built so toolpaths and brim avoidance use the same footprint. + if (layer_nr == 0 && m_raft_layers == 0 && m_support_params.support_style == smsTreeHybrid && + m_object_config->raft_first_layer_expansion.value > 0.f) { + ExPolygons expanded_base_areas; + const float inflate_factor_1st_layer = float(scale_(m_object_config->raft_first_layer_expansion.value)); + Polygons trimming = offset(m_object->layers().front()->lslices, float(scale_(m_support_params.gap_xy_first_layer)), + SUPPORT_SURFACES_OFFSET_PARAMETERS); + // Orca: Match normal support expansion: grow in steps and re-trim against the object each time. + const int nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / m_support_params.first_layer_flow.scaled_width()))); + const float step = inflate_factor_1st_layer / nsteps; + for (const ExPolygon &expoly : ts_layer->base_areas) { + if (overlaps({ expoly }, area_poly)) { // normal support in Hybrid mode + Polygons expanded = to_polygons(expoly); + for (int i = 0; i < nsteps; ++i) + expanded = diff(expand(expanded, step), trimming); + append(expanded_base_areas, union_ex(expanded)); + } else + expanded_base_areas.emplace_back(expoly); + } + ts_layer->base_areas = std::move(expanded_base_areas); + } + auto &area_groups = ts_layer->area_groups; for (auto& expoly : ts_layer->base_areas) { diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index f3a359f560..65a475d817 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -788,9 +788,12 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_line("raft_contact_distance", have_raft && !have_support_soluble); - // Orca: Raft, grid, snug and organic supports use these two parameters to control the size & density of the "brim"/flange - for (auto el : { "raft_first_layer_expansion", "raft_first_layer_density"}) - toggle_field(el, have_support_material && !(support_is_normal_tree && !have_raft)); + // Orca: First-layer density is available for supports broadly. + toggle_field("raft_first_layer_density", have_support_material); + // Orca: For regular tree (Slim/Strong) without raft, hide first-layer expansion. + // Keep it enabled for non-tree supports, organic tree, hybrid tree, and any raft case. + toggle_field("raft_first_layer_expansion", + have_support_material && ((!support_is_normal_tree || support_style == smsTreeHybrid) || have_raft)); bool has_ironing = (config->opt_enum("ironing_type") != IroningType::NoIroning); for (auto el : { "ironing_pattern", "ironing_flow", "ironing_spacing", "ironing_angle", "ironing_inset", "ironing_angle_fixed" })