diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index e0d699d51a..9159de617f 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -1330,82 +1330,72 @@ SupportGeneratorLayersPtr generate_support_layers( // Install support layers into the object. // A support layer installed on a PrintObject has a unique print_z. SupportGeneratorLayersPtr layers_sorted; - layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() - + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size()); + layers_sorted.reserve(raft_layers.size() + bottom_contacts.size() + top_contacts.size() + intermediate_layers.size() + interface_layers.size() + base_interface_layers.size()); append(layers_sorted, raft_layers); append(layers_sorted, bottom_contacts); append(layers_sorted, top_contacts); append(layers_sorted, intermediate_layers); append(layers_sorted, interface_layers); append(layers_sorted, base_interface_layers); - - // remove duplicated layers + // remove dupliated layers std::sort(layers_sorted.begin(), layers_sorted.end()); layers_sorted.erase(std::unique(layers_sorted.begin(), layers_sorted.end()), layers_sorted.end()); // Sort the layers lexicographically by a raising print_z and a decreasing height. std::sort(layers_sorted.begin(), layers_sorted.end(), [](auto *l1, auto *l2) { return *l1 < *l2; }); - int layer_id = 0; - int interface_id = 0; + int layer_id_interface = 0; assert(object.support_layers().empty()); - for (size_t i = 0; i < layers_sorted.size();) { - // Group layers with nearly the same print_z (floating point fuzz). + // Find the last layer with roughly the same print_z, find the minimum layer height of all. + // Due to the floating point inaccuracies, the print_z may not be the same even if in theory they should. size_t j = i + 1; coordf_t zmax = layers_sorted[i]->print_z + EPSILON; for (; j < layers_sorted.size() && layers_sorted[j]->print_z <= zmax; ++j) ; - // Assign an average print_z to the set of layers with nearly equal print_z. - coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z); + coordf_t zavg = 0.5 * (layers_sorted[i]->print_z + layers_sorted[j - 1]->print_z); coordf_t height_min = layers_sorted[i]->height; - - bool empty_layer = true; - bool has_interface_layer = false; - + bool empty = true; + // For snug supports, layers where the direction of the support interface shall change are accounted for. + size_t num_interfaces = 0; + size_t num_top_contacts = 0; + double top_contact_bottom_z = 0; for (size_t u = i; u < j; ++u) { SupportGeneratorLayer &layer = *layers_sorted[u]; - - if (!layer.polygons.empty()) { - empty_layer = false; - - if (one_of(layer.layer_type, support_types_interface)) - has_interface_layer = true; + if (! layer.polygons.empty()) { + empty = false; + num_interfaces += one_of(layer.layer_type, support_types_interface); + if (layer.layer_type == SupporLayerType::TopContact) { + ++ num_top_contacts; + assert(num_top_contacts <= 1); + // All top contact layers sharing this print_z shall also share bottom_z. + //assert(num_top_contacts == 1 || (top_contact_bottom_z - layer.bottom_z) < EPSILON); + top_contact_bottom_z = layer.bottom_z; + } } - layer.print_z = zavg; - height_min = std::min(height_min, layer.height); + height_min = std::min(height_min, layer.height); } - - if (!empty_layer) { - // Default: keep the global interface_id counter. - int this_iface_id = interface_id; - - // Special case: TreeOrganic raft-interface layers. - // Interface IDs may be unstable here, leading to inconsistent angles. - // Use deterministic IDs: alternate 0/1 by distance from the base raft. - bool is_tree_organic = (object.config().support_style == SupportMaterialStyle::smsTreeOrganic || - (object.config().support_style == SupportMaterialStyle::smsDefault && is_tree(object.config().support_type))); - - if (is_tree_organic && - layer_id >= object.slicing_parameters().base_raft_layers && - layer_id < object.slicing_parameters().raft_layers()) - { - // Alternate IDs 0,1,0,1... based on distance from base raft. - this_iface_id = (layer_id - object.slicing_parameters().base_raft_layers) & 1; + if (! empty) { + // Here the upper_layer and lower_layer pointers are left to null at the support layers, + // as they are never used. These pointers are candidates for removal. + bool this_layer_contacts_only = num_top_contacts > 0 && num_top_contacts == num_interfaces; + size_t this_layer_id_interface = layer_id_interface; + if (this_layer_contacts_only) { + // Find a supporting layer for its interface ID. + for (auto it = object.support_layers().rbegin(); it != object.support_layers().rend(); ++ it) + if (const SupportLayer &other_layer = **it; std::abs(other_layer.print_z - top_contact_bottom_z) < EPSILON) { + // other_layer supports this top contact layer. Assign a different support interface direction to this layer + // from the layer that supports it. + this_layer_id_interface = other_layer.interface_id() + 1; + } } - // END special case - - object.add_support_layer(layer_id++, this_iface_id, height_min, zavg); - - // Only increment the global counter if this set actually contains interface/contact layers. - if (has_interface_layer) - interface_id++; + object.add_support_layer(layer_id ++, this_layer_id_interface, height_min, zavg); + if (num_interfaces && ! this_layer_contacts_only) + ++ layer_id_interface; } - i = j; } - return layers_sorted; } @@ -1581,12 +1571,12 @@ void generate_support_toolpaths( if (filler_base_interface) filler_base_interface->set_bounding_box(bbox_object); filler_support->set_bounding_box(bbox_object); - for (size_t support_layer_id = range.begin(); support_layer_id < range.end(); ++ support_layer_id) { SupportLayer &support_layer = *support_layers[support_layer_id]; LayerCache &layer_cache = layer_caches[support_layer_id]; - const float support_interface_angle = support_params.support_interface_angle(support_layer.interface_id()); + const float support_interface_angle = (support_params.support_style == smsGrid || config.support_interface_pattern == smipRectilinear) ? + support_params.interface_angle : support_params.raft_interface_angle(support_layer.interface_id()); // Find polygons with the same print_z. SupportGeneratorLayerExtruded &bottom_contact_layer = layer_cache.bottom_contact_layer; @@ -1679,9 +1669,10 @@ void generate_support_toolpaths( filler->angle = interface_as_base ? // If zero interface layers are configured, use the same angle as for the base layers. angles[support_layer_id % angles.size()] : + // Use interface angle for the interface layers. raft_contact ? support_params.raft_interface_angle(support_layer.interface_id()) : - support_interface_angle; // Use interface angle for the interface layers. + support_interface_angle; double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density; filler->spacing = raft_contact ? support_params.raft_interface_flow.spacing() : interface_as_base ? support_params.support_material_flow.spacing() : support_params.support_material_interface_flow.spacing(); diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 34dc32fb0c..655ee0d806 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -111,8 +111,6 @@ struct SupportParameters { SupportMaterialPattern support_pattern = object_config.support_base_pattern; this->with_sheath = object_config.tree_support_wall_count > 0; - // Cache Rectilinear Interlaced flag once - this->interlaced_interface = (object_config.support_interface_pattern == smipRectilinearInterlaced); this->base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; @@ -129,10 +127,37 @@ struct SupportParameters { ipConcentric : (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); - this->raft_angle_1st_layer = float(M_PI_2); - this->raft_angle_base = 0.0f; - this->raft_angle_interface = slicing_params.base_raft_layers == 1 ? 0.0f : float(M_PI_2); // make it perpendicular to the layer beneath it - + this->raft_angle_1st_layer = 0.f; + this->raft_angle_base = 0.f; + this->raft_angle_interface = 0.f; + if (slicing_params.base_raft_layers > 1) { + assert(slicing_params.raft_layers() >= 4); + // There are all raft layer types (1st layer, base, interface & contact layers) available. + this->raft_angle_1st_layer = this->interface_angle; + this->raft_angle_base = this->base_angle; + this->raft_angle_interface = this->interface_angle; + if ((slicing_params.interface_raft_layers & 1) == 0) + // Allign the 1st raft interface layer so that the object 1st layer is hatched perpendicularly to the raft contact interface. + this->raft_angle_interface += float(0.5 * M_PI); + } else if (slicing_params.base_raft_layers == 1 || slicing_params.interface_raft_layers > 1) { + assert(slicing_params.raft_layers() == 2 || slicing_params.raft_layers() == 3); + // 1st layer, interface & contact layers available. + this->raft_angle_1st_layer = this->base_angle; + this->raft_angle_interface = this->interface_angle + 0.5 * M_PI; + } else if (slicing_params.interface_raft_layers == 1) { + // Only the contact raft layer is non-empty, which will be printed as the 1st layer. + assert(slicing_params.base_raft_layers == 0); + assert(slicing_params.interface_raft_layers == 1); + assert(slicing_params.raft_layers() == 1); + this->raft_angle_1st_layer = float(0.5 * M_PI); + this->raft_angle_interface = this->raft_angle_1st_layer; + } else { + // No raft. + assert(slicing_params.base_raft_layers == 0); + assert(slicing_params.interface_raft_layers == 0); + assert(slicing_params.raft_layers() == 0); + } + const auto nozzle_diameter = print_config.nozzle_diameter.get_at(object_config.support_interface_filament - 1); const coordf_t extrusion_width = object_config.line_width.get_abs_value(nozzle_diameter); support_extrusion_width = object_config.support_line_width.get_abs_value(nozzle_diameter); @@ -229,8 +254,6 @@ struct SupportParameters { InfillPattern contact_fill_pattern; // Shall the sparse (base) layers be printed with a single perimeter line (sheath) for robustness? bool with_sheath; - // True if support interface pattern is Rectilinear Interlaced - bool interlaced_interface = false; // Branches of organic supports with area larger than this threshold will be extruded with double lines. double tree_branch_diameter_double_wall_area_scaled = 0.25 * sqr(scaled(5.0)) * M_PI;; @@ -239,30 +262,8 @@ struct SupportParameters { float raft_angle_interface; // Produce a raft interface angle for a given SupportLayer::interface_id() - float raft_interface_angle(size_t interface_id) const - { - // Raft interfaces → always alternate 0°/90° - return this->raft_angle_interface + - ((interface_id & 1) ? float(M_PI_2) : 0.0f); - } - - float support_interface_angle(size_t interface_id) const - { - if (this->support_style == smsSnug) { - // Snug → ±45° - return this->interface_angle + - ((interface_id & 1) ? -float(M_PI_4) : +float(M_PI_4)); - } - - if (this->interlaced_interface) { - // Interlaced → 0°/90° - return this->interface_angle + - ((interface_id & 1) ? float(M_PI_2) : 0.0f); - } - - // Default → fixed - return this->interface_angle; - } + float raft_interface_angle(size_t interface_id) const + { return this->raft_angle_interface + ((interface_id & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)); } bool independent_layer_height = false; const double thresh_big_overhang = Slic3r::sqr(scale_(10)); diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index d7a48288f8..50272009cd 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -618,7 +618,7 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p if(support_pattern == smpLightning) m_support_params.base_fill_pattern = ipLightning; - diameter_angle_scale_factor = std::clamp(m_object_config->tree_support_branch_diameter_angle * M_PI / 180., 0., M_PI_2 - EPSILON); + diameter_angle_scale_factor = std::clamp(m_object_config->tree_support_branch_diameter_angle * M_PI / 180., 0., 0.5 * M_PI - EPSILON); is_slim = is_tree_slim(support_type, m_support_params.support_style); is_strong = is_tree(support_type) && m_support_params.support_style == smsTreeStrong; base_radius = std::max(MIN_BRANCH_RADIUS, m_object_config->tree_support_branch_diameter.value / 2); @@ -1369,7 +1369,7 @@ void TreeSupport::generate_toolpaths() Flow support_flow = Flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_raft = Fill::new_from_type(ipRectilinear); - filler_raft->angle = layer_nr == 0 ? M_PI_2 : 0; + filler_raft->angle = layer_nr == 0 ? PI/2 : 0; filler_raft->spacing = support_flow.spacing(); FillParams fill_params; @@ -1404,24 +1404,14 @@ void TreeSupport::generate_toolpaths() // raft interfaces for (layer_nr = m_slicing_params.base_raft_layers; - layer_nr < m_raft_layers; + layer_nr < m_slicing_params.base_raft_layers + m_slicing_params.interface_raft_layers; layer_nr++) { SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_interface = Fill::new_from_type(ipRectilinear); - - const size_t iface_idx = layer_nr - m_slicing_params.base_raft_layers; - const bool single_raft_base_layer = (m_slicing_params.base_raft_layers == 1); - - // Make interface lines alternate 0°/90° between layers, to improve bonding. - // If only 1 base layer → start at 0°; otherwise (0 or ≥2 base layers) → start at 90°. - filler_interface->angle = - (iface_idx & 1) - ? (single_raft_base_layer ? float(M_PI_2) : 0.0f) - : (single_raft_base_layer ? 0.0f : float(M_PI_2)); - + filler_interface->angle = PI / 2; // interface should be perpendicular to base filler_interface->spacing = support_flow.spacing(); FillParams fill_params; @@ -1441,7 +1431,7 @@ void TreeSupport::generate_toolpaths() SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_raft = Fill::new_from_type(ipRectilinear); - filler_raft->angle = M_PI_2; + filler_raft->angle = PI / 2; filler_raft->spacing = support_flow.spacing(); for (auto& poly : first_non_raft_base) make_perimeter_and_infill(ts_layer->support_fills.entities, poly, std::min(size_t(1), wall_count), support_flow, erSupportMaterial, filler_raft, interface_density, false); @@ -1502,10 +1492,6 @@ void TreeSupport::generate_toolpaths() fill_params.density = interface_density; // Note: spacing means the separation between two lines as if they are tightly extruded filler_Roof1stLayer->spacing = interface_flow.spacing(); - - if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) - filler_Roof1stLayer->layer_id = area_group.interface_id; - // generate a perimeter first to support interface better ExtrusionEntityCollection* temp_support_fills = new ExtrusionEntityCollection(); make_perimeter_and_infill(temp_support_fills->entities, poly, 1, interface_flow, erSupportMaterial, @@ -1519,10 +1505,6 @@ void TreeSupport::generate_toolpaths() // floor_areas fill_params.density = bottom_interface_density; filler_interface->spacing = interface_flow.spacing(); - - if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) - filler_interface->layer_id = area_group.interface_id; - fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, filler_interface.get(), fill_params, erSupportMaterialInterface, interface_flow); } else if (area_group.type == SupportLayer::RoofType) { @@ -1719,7 +1701,7 @@ void TreeSupport::generate() draw_circles(); profiler.stage_finish(STAGE_DRAW_CIRCLES); - normalize_interface_ids(); + profiler.stage_start(STAGE_GENERATE_TOOLPATHS); m_object->print()->set_status(70, _u8L("Generating support")); @@ -1948,7 +1930,7 @@ void TreeSupport::draw_circles() { double angle; if (SQUARE_SUPPORT) - angle = (double) i / CIRCLE_RESOLUTION * TAU + M_PI_4 + nodes_angle; + angle = (double) i / CIRCLE_RESOLUTION * TAU + PI / 4.0 + nodes_angle; else angle = (double) i / CIRCLE_RESOLUTION * TAU; branch_circle.append(Point(cos(angle) * branch_radius_scaled, sin(angle) * branch_radius_scaled)); @@ -2012,6 +1994,7 @@ void TreeSupport::draw_circles() coordf_t max_layers_above_base = 0; coordf_t max_layers_above_roof = 0; coordf_t max_layers_above_roof1 = 0; + int interface_id = 0; bool has_circle_node = false; bool need_extra_wall = false; ExPolygons collision_sharp_tails; @@ -2106,17 +2089,18 @@ void TreeSupport::draw_circles() } } - if (obj_layer_nr > 0 && node.distance_to_top < 0) + if (obj_layer_nr>0 && node.distance_to_top < 0) append(roof_gap_areas, area); else if (obj_layer_nr > 0 && node.support_roof_layers_below == 1 && node.is_sharp_tail==false) { append(roof_1st_layer, area); max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top); } - else if (obj_layer_nr > 0 && node.support_roof_layers_below > 1 && node.is_sharp_tail == false) + else if (obj_layer_nr > 0 && node.support_roof_layers_below > 0 && node.is_sharp_tail == false) { append(roof_areas, area); max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top); + interface_id = node.obj_layer_nr % top_interface_layers; } else { @@ -2187,6 +2171,7 @@ void TreeSupport::draw_circles() for (auto& expoly : ts_layer->roof_areas) { //if (area(expoly) < SQ(scale_(1))) continue; area_groups.emplace_back(&expoly, SupportLayer::RoofType, max_layers_above_roof); + area_groups.back().interface_id = interface_id; } for (auto &expoly : ts_layer->floor_areas) { //if (area(expoly) < SQ(scale_(1))) continue; @@ -2427,27 +2412,6 @@ void TreeSupport::draw_circles() } } -/*! - * Reassigns sequential interface IDs to enforce A/B alternation - * for RectilinearInterlaced support patterns. - */ -void TreeSupport::normalize_interface_ids() -{ - if (m_object_config->support_interface_pattern != smipRectilinearInterlaced) - return; - - size_t iface_parity = 0; - for (int lid = m_raft_layers; lid < m_object->support_layer_count(); ++lid) { - SupportLayer* ts_layer = m_object->get_support_layer(lid); - if (!ts_layer) continue; - - for (auto &ag : ts_layer->area_groups) - ag.interface_id = iface_parity; - - iface_parity ^= 1; - } -} - double SupportNode::diameter_angle_scale_factor; void TreeSupport::drop_nodes() @@ -2459,7 +2423,7 @@ void TreeSupport::drop_nodes() const double angle = config.tree_support_branch_angle.value * M_PI / 180.; const int wall_count = std::max(1, config.tree_support_wall_count.value); double tan_angle = tan(angle); // when nodes are thick, they can move further. this is the max angle - const coordf_t max_move_distance = (angle < M_PI_2) ? (coordf_t)(tan_angle * layer_height)*wall_count : std::numeric_limits::max(); + const coordf_t max_move_distance = (angle < M_PI / 2) ? (coordf_t)(tan_angle * layer_height)*wall_count : std::numeric_limits::max(); const double max_move_distance2 = max_move_distance * max_move_distance; const size_t tip_layers = base_radius / layer_height; //The number of layers to be shrinking the circle to create a tip. This produces a 45 degree angle. const coordf_t radius_sample_resolution = m_ts_data->m_radius_sample_resolution; diff --git a/src/libslic3r/Support/TreeSupport.hpp b/src/libslic3r/Support/TreeSupport.hpp index cdc11f543c..e0446ad5f1 100644 --- a/src/libslic3r/Support/TreeSupport.hpp +++ b/src/libslic3r/Support/TreeSupport.hpp @@ -459,21 +459,6 @@ private: * \param contact_nodes The nodes to draw as support. */ void draw_circles(); - - /*! - * \brief Normalize interface IDs for interlaced supports. - * - * Reassigns sequential IDs to support layers when using - * RectilinearInterlaced, alternating (0,1,0,1,…) so consecutive - * interface layers never share orientation. Applies to all support - * area groups, including transition layers (e.g. TreeStrong - * body-to-interface), as well as top and bottom interfaces, so - * FillRectilinearInterlaced computes correct extrusion angles. - * - * \note Call after support layers are built and before toolpaths, - * so fillers use the normalized IDs. - */ - void normalize_interface_ids(); /*! * \brief Drops down the nodes of the tree support towards the build plate.