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.
This commit is contained in:
Kiss Lorand
2026-05-14 19:44:40 +03:00
committed by GitHub
parent dd8cb89f6d
commit d2ca5d3a1e
2 changed files with 42 additions and 7 deletions

View File

@@ -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<Fill> filler_support = std::shared_ptr<Fill>(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<Fill> filler_support = std::shared_ptr<Fill>(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) {

View File

@@ -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<IroningType>("ironing_type") != IroningType::NoIroning);
for (auto el : { "ironing_pattern", "ironing_flow", "ironing_spacing", "ironing_angle", "ironing_inset", "ironing_angle_fixed" })