diff --git a/CMakeLists.txt b/CMakeLists.txt index f6c98f681c8..15390384abb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -571,7 +571,7 @@ endif() # boost::process was introduced first in version 1.64.0, # boost::beast::detail::base64 was introduced first in version 1.66.0 if(POLICY CMP0167) - cmake_policy(SET CMP0167 NEW) + cmake_policy(SET CMP0167 OLD) endif() set(Boost_NO_SYSTEM_PATHS TRUE) find_package(Boost 1.83.0 REQUIRED COMPONENTS system filesystem thread log log_setup locale regex chrono atomic date_time iostreams program_options nowide) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 1e193962e09..e1bdafc2685 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -8685,7 +8685,6 @@ std::optional GCode::texture_mapping_seam_hiding_hint(const { if (m_curr_print == nullptr || m_layer == nullptr || - m_layer->upper_layer == nullptr || m_writer.filament() == nullptr || loop.paths.empty() || !is_external_perimeter(loop.role())) @@ -8708,8 +8707,9 @@ std::optional GCode::texture_mapping_seam_hiding_hint(const const PrintObject *layer_object = m_layer->object(); const Layer *upper_layer = m_layer->upper_layer; + const Layer *lower_layer = m_layer->lower_layer; const int object_layer_count = layer_object ? int(layer_object->layer_count()) : 0; - if (layer_object == nullptr || upper_layer == nullptr || object_layer_count <= 0) + if (layer_object == nullptr || object_layer_count <= 0 || (upper_layer == nullptr && lower_layer == nullptr)) return std::nullopt; std::vector component_ids = decode_offset_component_ids_for_gcode(*zone, num_physical); @@ -8896,9 +8896,16 @@ std::optional GCode::texture_mapping_seam_hiding_hint(const const unsigned int current_component_id = unsigned(m_writer.filament()->id() + 1); const std::optional current_state = texture_state_for_layer(m_layer, current_component_id); - const std::optional upper_state = texture_state_for_layer(upper_layer, 0); - if (!current_state || !upper_state) + const std::optional upper_state = + upper_layer != nullptr ? texture_state_for_layer(upper_layer, 0) : std::optional(); + const std::optional lower_state = + lower_layer != nullptr ? texture_state_for_layer(lower_layer, 0) : std::optional(); + if (!current_state || + (upper_layer != nullptr && !upper_state) || + (lower_layer != nullptr && !lower_state) || + (!upper_state && !lower_state)) return std::nullopt; + const bool require_bidirectional_cover = upper_state.has_value() && lower_state.has_value(); std::vector reference_nozzles; reference_nozzles.reserve(component_ids.size() + 2); @@ -8955,6 +8962,11 @@ std::optional GCode::texture_mapping_seam_hiding_hint(const float width_delta_mm { 0.f }; }; + struct SeamAdjacentOcclusion { + float cover_mm { 0.f }; + float score_mm { 0.f }; + }; + struct SeamHidingCandidate { Point point; float cover_mm { 0.f }; @@ -9114,6 +9126,147 @@ std::optional GCode::texture_mapping_seam_hiding_hint(const if (is_external_perimeter(path.role()) && path.polyline.points.size() >= 2) external_paths.push_back(&path); + auto evaluate_sample_for_path = [&](const ExtrusionPath &path, + size_t path_index, + size_t segment_index, + double segment_t, + double arc_mm, + double span_mm) -> std::optional { + const Points &points = path.polyline.points; + if (segment_index == 0 || segment_index >= points.size()) + return std::nullopt; + + const float path_outer_width_mm = std::max( + 0.01f, + path.width > EPSILON ? path.width : float(m_config.outer_wall_line_width.get_abs_value(reference_nozzle))); + const float texture_mapping_max_outer_width_mm = std::max( + 0.05f, + float(m_config.texture_mapping_outer_wall_gradient_max_line_width.value)); + const float base_outer_width_mm = vertex_color_match_mode ? texture_mapping_max_outer_width_mm : path_outer_width_mm; + const float flow_reference_width_mm = path_outer_width_mm; + const double half_flow_reference_scaled = scale_(0.5 * double(flow_reference_width_mm)); + const float max_local_edge_tangent_delta_mm = std::max(0.75f, base_outer_width_mm * 1.5f); + const float max_local_edge_normal_delta_mm = + std::max(1.25f, base_outer_width_mm * 3.f + 2.f * max_allowed_distance_mm); + const Point &a = points[segment_index - 1]; + const Point &b = points[segment_index]; + const double ax = double(a.x()); + const double ay = double(a.y()); + const double bx = double(b.x()); + const double by = double(b.y()); + const double dx_scaled = bx - ax; + const double dy_scaled = by - ay; + const double len_scaled = std::hypot(dx_scaled, dy_scaled); + if (len_scaled <= EPSILON) + return std::nullopt; + + const Point sample_point(coord_t(std::llround(ax + segment_t * dx_scaled)), + coord_t(std::llround(ay + segment_t * dy_scaled))); + double outward_x = 0.0; + double outward_y = 0.0; + resolve_segment_shift_outward_normal_for_gcode(m_layer, + sample_point, + dx_scaled, + dy_scaled, + len_scaled, + double(sample_point.x()) - double(object_center.x()), + double(sample_point.y()) - double(object_center.y()), + outward_x, + outward_y); + + const double tangent_x = dx_scaled / len_scaled; + const double tangent_y = dy_scaled / len_scaled; + const std::optional current_envelope = + texture_envelope_for_state(*current_state, path, sample_point, outward_x, outward_y, reference_nozzle); + if (!current_envelope) + return std::nullopt; + + const Point current_outer_edge = scaled_offset_point(sample_point, + outward_x, + outward_y, + current_envelope->outer_offset_mm); + + auto evaluate_adjacent_occlusion = + [&](const std::optional &adjacent_state) -> std::optional { + if (!adjacent_state) + return std::nullopt; + + const std::optional adjacent_boundary = + find_normal_aware_layer_slice_boundary_point_for_gcode(adjacent_state->layer, + current_outer_edge, + outward_x, + outward_y, + tangent_x, + tangent_y, + max_local_edge_normal_delta_mm, + max_local_edge_tangent_delta_mm); + if (!adjacent_boundary) + return std::nullopt; + + const Point adjacent_centerline( + coord_t(std::llround(double(adjacent_boundary->point.x()) - adjacent_boundary->outward_x * half_flow_reference_scaled)), + coord_t(std::llround(double(adjacent_boundary->point.y()) - adjacent_boundary->outward_y * half_flow_reference_scaled))); + const std::optional adjacent_envelope = + texture_envelope_for_state(*adjacent_state, + path, + adjacent_centerline, + adjacent_boundary->outward_x, + adjacent_boundary->outward_y, + reference_nozzle); + if (!adjacent_envelope) + return std::nullopt; + + const Point adjacent_outer_edge = scaled_offset_point(adjacent_centerline, + adjacent_boundary->outward_x, + adjacent_boundary->outward_y, + adjacent_envelope->outer_offset_mm); + const double cover_scaled = + (double(adjacent_outer_edge.x()) - double(current_outer_edge.x())) * outward_x + + (double(adjacent_outer_edge.y()) - double(current_outer_edge.y())) * outward_y; + const float cover_mm = unscale(cover_scaled); + if (!std::isfinite(cover_mm)) + return std::nullopt; + + const float texture_cover_bonus_mm = std::max(0.f, current_envelope->width_delta_mm - adjacent_envelope->width_delta_mm); + const float score_mm = cover_mm > 0.f ? + std::max(0.f, cover_mm + 0.25f * texture_cover_bonus_mm - 0.2f * adjacent_boundary->tangent_delta_mm) : + 0.f; + return SeamAdjacentOcclusion{ std::max(0.f, cover_mm), score_mm }; + }; + + const std::optional upper_occlusion = evaluate_adjacent_occlusion(upper_state); + const std::optional lower_occlusion = evaluate_adjacent_occlusion(lower_state); + float cover_mm = 0.f; + float score_mm = 0.f; + if (require_bidirectional_cover) { + if (!upper_occlusion || !lower_occlusion) + return std::nullopt; + + cover_mm = std::min(upper_occlusion->cover_mm, lower_occlusion->cover_mm); + const float weaker_score_mm = std::min(upper_occlusion->score_mm, lower_occlusion->score_mm); + const float stronger_score_mm = std::max(upper_occlusion->score_mm, lower_occlusion->score_mm); + if (cover_mm > 0.f && weaker_score_mm > 0.f) + score_mm = weaker_score_mm + 0.35f * stronger_score_mm; + } else { + const std::optional &occlusion = upper_occlusion ? upper_occlusion : lower_occlusion; + if (!occlusion) + return std::nullopt; + cover_mm = occlusion->cover_mm; + score_mm = occlusion->score_mm; + } + + return SeamHidingCandidate{ + sample_point, + cover_mm, + score_mm, + arc_mm, + span_mm, + segment_t, + path_index, + segment_index + }; + }; + for (size_t external_path_idx = 0; external_path_idx < external_paths.size(); ++external_path_idx) { const ExtrusionPath *path_ptr = external_paths[external_path_idx]; const ExtrusionPath &path = *path_ptr; @@ -9127,12 +9280,7 @@ std::optional GCode::texture_mapping_seam_hiding_hint(const 0.05f, float(m_config.texture_mapping_outer_wall_gradient_max_line_width.value)); const float base_outer_width_mm = vertex_color_match_mode ? texture_mapping_max_outer_width_mm : path_outer_width_mm; - const float flow_reference_width_mm = path_outer_width_mm; - const double half_flow_reference_scaled = scale_(0.5 * double(flow_reference_width_mm)); const float sample_step_mm = std::clamp(0.5f * base_outer_width_mm, 0.15f, 0.5f); - const float max_local_edge_tangent_delta_mm = std::max(0.75f, base_outer_width_mm * 1.5f); - const float max_local_edge_normal_delta_mm = - std::max(1.25f, base_outer_width_mm * 3.f + 2.f * max_allowed_distance_mm); const Points &points = path.polyline.points; const size_t path_index = external_path_idx; double path_arc_start_mm = total_length_mm; @@ -9142,102 +9290,19 @@ std::optional GCode::texture_mapping_seam_hiding_hint(const double arc_mm, double span_mm, bool add_candidate) -> std::optional { - const Point &a = points[segment_index - 1]; - const Point &b = points[segment_index]; - const double ax = double(a.x()); - const double ay = double(a.y()); - const double bx = double(b.x()); - const double by = double(b.y()); - const double dx_scaled = bx - ax; - const double dy_scaled = by - ay; - const double len_scaled = std::hypot(dx_scaled, dy_scaled); - if (len_scaled <= EPSILON) + std::optional candidate = + evaluate_sample_for_path(path, path_index, segment_index, segment_t, arc_mm, span_mm); + if (!candidate) return std::nullopt; - const Point sample_point(coord_t(std::llround(ax + segment_t * dx_scaled)), - coord_t(std::llround(ay + segment_t * dy_scaled))); - double outward_x = 0.0; - double outward_y = 0.0; - resolve_segment_shift_outward_normal_for_gcode(m_layer, - sample_point, - dx_scaled, - dy_scaled, - len_scaled, - double(sample_point.x()) - double(object_center.x()), - double(sample_point.y()) - double(object_center.y()), - outward_x, - outward_y); - - const double tangent_x = dx_scaled / len_scaled; - const double tangent_y = dy_scaled / len_scaled; - const std::optional current_envelope = - texture_envelope_for_state(*current_state, path, sample_point, outward_x, outward_y, reference_nozzle); - if (!current_envelope) - return std::nullopt; - - const Point current_outer_edge = scaled_offset_point(sample_point, - outward_x, - outward_y, - current_envelope->outer_offset_mm); - const std::optional upper_boundary = - find_normal_aware_layer_slice_boundary_point_for_gcode(upper_layer, - current_outer_edge, - outward_x, - outward_y, - tangent_x, - tangent_y, - max_local_edge_normal_delta_mm, - max_local_edge_tangent_delta_mm); - if (!upper_boundary) - return std::nullopt; - - const Point upper_centerline( - coord_t(std::llround(double(upper_boundary->point.x()) - upper_boundary->outward_x * half_flow_reference_scaled)), - coord_t(std::llround(double(upper_boundary->point.y()) - upper_boundary->outward_y * half_flow_reference_scaled))); - const std::optional upper_envelope = - texture_envelope_for_state(*upper_state, - path, - upper_centerline, - upper_boundary->outward_x, - upper_boundary->outward_y, - reference_nozzle); - if (!upper_envelope) - return std::nullopt; - - const Point upper_outer_edge = scaled_offset_point(upper_centerline, - upper_boundary->outward_x, - upper_boundary->outward_y, - upper_envelope->outer_offset_mm); - const double cover_scaled = - (double(upper_outer_edge.x()) - double(current_outer_edge.x())) * outward_x + - (double(upper_outer_edge.y()) - double(current_outer_edge.y())) * outward_y; - const float cover_mm = unscale(cover_scaled); - if (!std::isfinite(cover_mm)) - return std::nullopt; - - const float texture_cover_bonus_mm = std::max(0.f, current_envelope->width_delta_mm - upper_envelope->width_delta_mm); - const float score_mm = cover_mm > 0.f ? - std::max(0.f, cover_mm + 0.25f * texture_cover_bonus_mm - 0.2f * upper_boundary->tangent_delta_mm) : - 0.f; - SeamHidingCandidate candidate{ - sample_point, - std::max(0.f, cover_mm), - score_mm, - arc_mm, - span_mm, - segment_t, - path_index, - segment_index - }; - if (add_candidate) { - candidates.push_back(candidate); - min_score_mm = std::min(min_score_mm, score_mm); - weighted_score_mm += double(score_mm) * span_mm; + candidates.push_back(*candidate); + min_score_mm = std::min(min_score_mm, candidate->score_mm); + weighted_score_mm += double(candidate->score_mm) * span_mm; weighted_length_mm += span_mm; - if (score_mm > best_score_mm) { - best_score_mm = score_mm; - best_candidate = candidate; + if (candidate->score_mm > best_score_mm) { + best_score_mm = candidate->score_mm; + best_candidate = *candidate; } } @@ -9309,106 +9374,12 @@ std::optional GCode::texture_mapping_seam_hiding_hint(const if (std::abs(t - best_candidate.segment_t) <= 1e-5) continue; const std::optional refined = - [&]() -> std::optional { - const Points &points = best_path->polyline.points; - const float path_outer_width_mm = std::max( - 0.01f, - best_path->width > EPSILON ? - best_path->width : - float(m_config.outer_wall_line_width.get_abs_value(reference_nozzle))); - const float texture_mapping_max_outer_width_mm = std::max( - 0.05f, - float(m_config.texture_mapping_outer_wall_gradient_max_line_width.value)); - const float base_outer_width_mm = - vertex_color_match_mode ? texture_mapping_max_outer_width_mm : path_outer_width_mm; - const float flow_reference_width_mm = path_outer_width_mm; - const double half_flow_reference_scaled = scale_(0.5 * double(flow_reference_width_mm)); - const float max_local_edge_tangent_delta_mm = std::max(0.75f, base_outer_width_mm * 1.5f); - const float max_local_edge_normal_delta_mm = - std::max(1.25f, base_outer_width_mm * 3.f + 2.f * max_allowed_distance_mm); - const Point &a = points[best_candidate.segment_index - 1]; - const Point &b = points[best_candidate.segment_index]; - const double ax = double(a.x()); - const double ay = double(a.y()); - const double bx = double(b.x()); - const double by = double(b.y()); - const double dx_scaled = bx - ax; - const double dy_scaled = by - ay; - const double len_scaled = std::hypot(dx_scaled, dy_scaled); - if (len_scaled <= EPSILON) - return std::nullopt; - const Point sample_point(coord_t(std::llround(ax + t * dx_scaled)), - coord_t(std::llround(ay + t * dy_scaled))); - double outward_x = 0.0; - double outward_y = 0.0; - resolve_segment_shift_outward_normal_for_gcode(m_layer, - sample_point, - dx_scaled, - dy_scaled, - len_scaled, - double(sample_point.x()) - double(object_center.x()), - double(sample_point.y()) - double(object_center.y()), - outward_x, - outward_y); - const double tangent_x = dx_scaled / len_scaled; - const double tangent_y = dy_scaled / len_scaled; - const std::optional current_envelope = - texture_envelope_for_state(*current_state, *best_path, sample_point, outward_x, outward_y, reference_nozzle); - if (!current_envelope) - return std::nullopt; - const Point current_outer_edge = scaled_offset_point(sample_point, - outward_x, - outward_y, - current_envelope->outer_offset_mm); - const std::optional upper_boundary = - find_normal_aware_layer_slice_boundary_point_for_gcode(upper_layer, - current_outer_edge, - outward_x, - outward_y, - tangent_x, - tangent_y, - max_local_edge_normal_delta_mm, - max_local_edge_tangent_delta_mm); - if (!upper_boundary) - return std::nullopt; - const Point upper_centerline( - coord_t(std::llround(double(upper_boundary->point.x()) - - upper_boundary->outward_x * half_flow_reference_scaled)), - coord_t(std::llround(double(upper_boundary->point.y()) - - upper_boundary->outward_y * half_flow_reference_scaled))); - const std::optional upper_envelope = - texture_envelope_for_state(*upper_state, - *best_path, - upper_centerline, - upper_boundary->outward_x, - upper_boundary->outward_y, - reference_nozzle); - if (!upper_envelope) - return std::nullopt; - const Point upper_outer_edge = scaled_offset_point(upper_centerline, - upper_boundary->outward_x, - upper_boundary->outward_y, - upper_envelope->outer_offset_mm); - const double cover_scaled = - (double(upper_outer_edge.x()) - double(current_outer_edge.x())) * outward_x + - (double(upper_outer_edge.y()) - double(current_outer_edge.y())) * outward_y; - const float cover_mm = unscale(cover_scaled); - const float texture_cover_bonus_mm = - std::max(0.f, current_envelope->width_delta_mm - upper_envelope->width_delta_mm); - const float score_mm = cover_mm > 0.f ? - std::max(0.f, cover_mm + 0.25f * texture_cover_bonus_mm - 0.2f * upper_boundary->tangent_delta_mm) : - 0.f; - return SeamHidingCandidate{ - sample_point, - std::max(0.f, cover_mm), - score_mm, - best_candidate.arc_mm + (t - best_candidate.segment_t) * len_mm, - 0.25 * len_mm, - t, - best_candidate.path_index, - best_candidate.segment_index - }; - }(); + evaluate_sample_for_path(*best_path, + best_candidate.path_index, + best_candidate.segment_index, + t, + best_candidate.arc_mm + (t - best_candidate.segment_t) * len_mm, + 0.25 * len_mm); if (refined && refined->score_mm > best_score_mm) { best_score_mm = refined->score_mm; best_candidate = *refined;