diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index 3a9412119f..b77955c6d6 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -167,6 +167,24 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para if (is_flow_calib) { eec->no_sort = true; } + + // ORCA: When Archimedean Chords direction is explicitly set, prevent path reversal + // to preserve the intended spiral direction. Settings are in PrintRegionConfig (params.config). + bool has_archimedean_direction = false; + auto* archimedean_fill = dynamic_cast(this); + if (archimedean_fill != nullptr && params.config != nullptr) { + ArchimedeanChordsDirection direction = ArchimedeanChordsDirection::Default; + if (params.extrusion_role == erTopSolidInfill) { + direction = params.config->top_surface_archimedean_direction.value; + } else if (params.extrusion_role == erBottomSurface) { + direction = params.config->bottom_surface_archimedean_direction.value; + } + + has_archimedean_direction = (direction != ArchimedeanChordsDirection::Default); + if (has_archimedean_direction) { + eec->no_sort = true; + } + } size_t idx = eec->entities.size(); if (params.use_arachne) { Flow new_flow = params.flow.with_spacing(float(this->spacing)); @@ -179,7 +197,7 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para params.extrusion_role, flow_mm3_per_mm, float(flow_width), params.flow.height()); } - if (!params.can_reverse || is_flow_calib) { + if (!params.can_reverse || is_flow_calib || has_archimedean_direction) { for (size_t i = idx; i < eec->entities.size(); i++) eec->entities[i]->set_reverse(); } diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index 6044ba43a2..bbad74690e 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -66,6 +66,71 @@ void InfillPolylineClipper::add_point(const Vec2d &fpt) } } +// ORCA: Helper function to chain Archimedean chord polylines with proper spiral direction. +// Handles flow calibration mode and user-specified spiral direction (FromCenter/ToCenter). +// For non-Archimedean fills or default direction, simply chains the polylines. +static Polylines chain_archimedean_polylines( + Polylines polylines, + bool is_archimedean_fill, + bool is_flow_calib, + const FillParams& params) +{ + if (polylines.empty()) { + return Polylines(); + } + + // Get Archimedean direction setting based on extrusion role + ArchimedeanChordsDirection spiral_direction = ArchimedeanChordsDirection::Default; + if (is_archimedean_fill && params.config) { + if (params.extrusion_role == erTopSolidInfill) { + spiral_direction = params.config->top_surface_archimedean_direction.value; + } else if (params.extrusion_role == erBottomSurface) { + spiral_direction = params.config->bottom_surface_archimedean_direction.value; + } + } + + // Check if special spiral direction handling is needed + bool apply_spiral_direction = is_flow_calib || + (is_archimedean_fill && spiral_direction != ArchimedeanChordsDirection::Default); + + if (!apply_spiral_direction) { + return chain_polylines(std::move(polylines)); + } + + // Find the center spiral (longest polyline) + auto it = std::max_element(polylines.begin(), polylines.end(), + [](const Polyline& a, const Polyline& b) { return a.length() < b.length(); }); + Polyline center_spiral = std::move(*it); + + // Spiral is generated centered at (0,0), so squaredNorm gives distance from center + bool is_inside_out = center_spiral.first_point().squaredNorm() < center_spiral.last_point().squaredNorm(); + + // Flow calibration needs inside-out (from center), same as FromCenter direction + if (is_flow_calib || spiral_direction == ArchimedeanChordsDirection::FromCenter) { + if (!is_inside_out) { + center_spiral.reverse(); + } + } else if (spiral_direction == ArchimedeanChordsDirection::ToCenter) { + if (is_inside_out) { + center_spiral.reverse(); + } + } + + // Chain the other polylines + polylines.erase(it); + Polylines chained = chain_polylines(std::move(polylines)); + + // Flow calibration and ToCenter: add spiral at end + // FromCenter: add spiral at beginning + if (spiral_direction == ArchimedeanChordsDirection::FromCenter) { + chained.insert(chained.begin(), std::move(center_spiral)); + } else { + chained.push_back(std::move(center_spiral)); + } + + return chained; +} + void FillPlanePath::_fill_surface_single( const FillParams ¶ms, unsigned int thickness_layers, @@ -122,32 +187,13 @@ void FillPlanePath::_fill_surface_single( if (!polylines.empty()) { Polylines chained; if (params.dont_connect() || params.density > 0.5) { - // ORCA: special flag for flow rate calibration - auto is_flow_calib = params.extrusion_role == erTopSolidInfill && + // ORCA: Check for Archimedean fill and flow calibration mode + bool is_archimedean_fill = dynamic_cast(this) != nullptr; + bool is_flow_calib = is_archimedean_fill && + params.extrusion_role == erTopSolidInfill && this->print_object_config->has("calib_flowrate_topinfill_special_order") && - this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool() && - dynamic_cast(this); - if (is_flow_calib) { - // We want the spiral part to be printed inside-out - // Find the center spiral line first, by looking for the longest one - auto it = std::max_element(polylines.begin(), polylines.end(), - [](const Polyline& a, const Polyline& b) { return a.length() < b.length(); }); - Polyline center_spiral = std::move(*it); - - // Ensure the spiral is printed from inside to out - if (center_spiral.first_point().squaredNorm() > center_spiral.last_point().squaredNorm()) { - center_spiral.reverse(); - } - - // Chain the other polylines - polylines.erase(it); - chained = chain_polylines(std::move(polylines)); - - // Then add the center spiral back - chained.push_back(std::move(center_spiral)); - } else { - chained = chain_polylines(std::move(polylines)); - } + this->print_object_config->option("calib_flowrate_topinfill_special_order")->getBool(); + chained = chain_archimedean_polylines(std::move(polylines), is_archimedean_fill, is_flow_calib, params); } else connect_infill(std::move(polylines), expolygon, chained, this->spacing, params); // paths must be repositioned and rotated back diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f5b0d1c713..fb2e809bd7 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -884,10 +884,11 @@ static std::vector s_Preset_print_options { "layer_height", "initial_layer_print_height", "wall_loops", "alternate_extra_wall", "slice_closing_radius", "spiral_mode", "spiral_mode_smooth", "spiral_mode_max_xy_smoothing", "spiral_starting_flow_ratio", "spiral_finishing_flow_ratio", "slicing_mode", "top_shell_layers", "top_shell_thickness", "top_surface_density", "bottom_surface_density", "bottom_shell_layers", "bottom_shell_thickness", "extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only", "wall_direction", - "seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density","fill_multiline", "sparse_infill_pattern", "lateral_lattice_angle_1", "lateral_lattice_angle_2", "infill_overhang_angle", "top_surface_pattern", "bottom_surface_pattern", + "seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density","fill_multiline", "sparse_infill_pattern", "lateral_lattice_angle_1", "lateral_lattice_angle_2", "infill_overhang_angle", + "top_surface_pattern", "top_surface_archimedean_direction", "bottom_surface_pattern", "bottom_surface_archimedean_direction", "infill_direction", "solid_infill_direction", "counterbore_hole_bridging","infill_shift_step", "sparse_infill_rotate_template", "solid_infill_rotate_template", "symmetric_infill_y_axis","skeleton_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_density", "align_infill_direction_to_model", "extra_solid_infills", - "minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern","gap_fill_target", + "minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern", "gap_fill_target", "ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle", "ironing_angle_fixed", "ironing_inset", "support_ironing", "support_ironing_pattern", "support_ironing_flow", "support_ironing_spacing", "max_travel_detour_distance", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 3e48103915..b53f62b67e 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -253,6 +253,14 @@ static t_config_enum_values s_keys_map_WallDirection{ }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WallDirection) +// Orca: Archimedean chords direction +static t_config_enum_values s_keys_map_ArchimedeanChordsDirection{ + { "default", int(ArchimedeanChordsDirection::Default) }, + { "from_center", int(ArchimedeanChordsDirection::FromCenter) }, + { "to_center", int(ArchimedeanChordsDirection::ToCenter) } +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(ArchimedeanChordsDirection) + //BBS static t_config_enum_values s_keys_map_PrintSequence { { "by layer", int(PrintSequence::ByLayer) }, @@ -1901,7 +1909,39 @@ void PrintConfigDef::init_fff_params() def->enum_labels = def_top_fill_pattern->enum_labels; def->set_default_value(new ConfigOptionEnum(ipMonotonic)); - def = this->add("internal_solid_infill_pattern", coEnum); + def = this->add("top_surface_archimedean_direction", coEnum); + def->label = L("Top surface chord direction"); + def->category = L("Strength"); + def->tooltip = L("Controls the direction of the Archimedean chords spiral pattern for top surface. " + "'Default' keeps the current behavior without forcing direction. " + "'From center' forces the spiral to go from center toward edges. " + "'To center' forces the spiral to go from edges toward center."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("default"); + def->enum_values.push_back("from_center"); + def->enum_values.push_back("to_center"); + def->enum_labels.push_back(L("Default")); + def->enum_labels.push_back(L("From center")); + def->enum_labels.push_back(L("To center")); + def->set_default_value(new ConfigOptionEnum(ArchimedeanChordsDirection::Default)); + + def = this->add("bottom_surface_archimedean_direction", coEnum); + def->label = L("Bottom surface chord direction"); + def->category = L("Strength"); + def->tooltip = L("Controls the direction of the Archimedean chords spiral pattern for bottom surface. " + "'Default' keeps the current behavior without forcing direction. " + "'From center' forces the spiral to go from center toward edges. " + "'To center' forces the spiral to go from edges toward center."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("default"); + def->enum_values.push_back("from_center"); + def->enum_values.push_back("to_center"); + def->enum_labels.push_back(L("Default")); + def->enum_labels.push_back(L("From center")); + def->enum_labels.push_back(L("To center")); + def->set_default_value(new ConfigOptionEnum(ArchimedeanChordsDirection::Default)); + + def = this->add("internal_solid_infill_pattern", coEnum); def->label = L("Internal solid infill pattern"); def->category = L("Strength"); def->tooltip = L("Line pattern of internal solid infill. if the detect narrow internal solid infill be enabled, the concentric pattern will be used for the small area."); @@ -1909,7 +1949,7 @@ void PrintConfigDef::init_fff_params() def->enum_values = def_top_fill_pattern->enum_values; def->enum_labels = def_top_fill_pattern->enum_labels; def->set_default_value(new ConfigOptionEnum(ipMonotonic)); - + def = this->add("outer_wall_line_width", coFloatOrPercent); def->label = L("Outer wall"); def->category = L("Quality"); diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 3e34a8362b..cdfd0c81b7 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -119,6 +119,17 @@ enum class WallDirection Count, }; +// Orca: Archimedean chords direction +enum class ArchimedeanChordsDirection +{ + // Keep current behavior without forcing direction + Default, + // Force spiral from center to edges (inner-out) + FromCenter, + // Force spiral from edges toward center (outer-in) + ToCenter, +}; + //BBS enum class PrintSequence { ByLayer, @@ -1034,7 +1045,9 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionPercent, top_surface_density)) ((ConfigOptionPercent, bottom_surface_density)) ((ConfigOptionEnum, top_surface_pattern)) + ((ConfigOptionEnum, top_surface_archimedean_direction)) ((ConfigOptionEnum, bottom_surface_pattern)) + ((ConfigOptionEnum, bottom_surface_archimedean_direction)) ((ConfigOptionEnum, internal_solid_infill_pattern)) ((ConfigOptionFloatOrPercent, outer_wall_line_width)) ((ConfigOptionFloat, outer_wall_speed)) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 60828cedcf..0064d90e26 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -652,6 +652,14 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co toggle_field("top_surface_density", has_top_shell); toggle_field("bottom_surface_density", has_bottom_shell); + // Orca: Show Archimedean direction only when the pattern is Archimedean Chords + bool has_top_archimedean = has_top_shell && + config->opt_enum("top_surface_pattern") == ipArchimedeanChords; + bool has_bottom_archimedean = has_bottom_shell && + config->opt_enum("bottom_surface_pattern") == ipArchimedeanChords; + toggle_line("top_surface_archimedean_direction", has_top_archimedean); + toggle_line("bottom_surface_archimedean_direction", has_bottom_archimedean); + for (auto el : { "infill_direction", "sparse_infill_line_width", "gap_fill_target","filter_out_gap_fill","infill_wall_overlap", "sparse_infill_speed", "bridge_speed", "internal_bridge_speed", "bridge_angle", "internal_bridge_angle", "solid_infill_direction", "solid_infill_rotate_template", "internal_solid_infill_pattern", "solid_infill_filament", diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index b98bb91ec3..d6b52b58c1 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2416,10 +2416,12 @@ void TabPrint::build() optgroup->append_single_option_line("top_shell_thickness", "strength_settings_top_bottom_shells#shell-thickness"); optgroup->append_single_option_line("top_surface_density", "strength_settings_top_bottom_shells#surface-density"); optgroup->append_single_option_line("top_surface_pattern", "strength_settings_top_bottom_shells#surface-pattern"); + optgroup->append_single_option_line("top_surface_archimedean_direction", "strength_settings_top_bottom_shells#spiral-direction"); optgroup->append_single_option_line("bottom_shell_layers", "strength_settings_top_bottom_shells#shell-layers"); optgroup->append_single_option_line("bottom_shell_thickness", "strength_settings_top_bottom_shells#shell-thickness"); optgroup->append_single_option_line("bottom_surface_density", "strength_settings_top_bottom_shells#surface-density"); optgroup->append_single_option_line("bottom_surface_pattern", "strength_settings_top_bottom_shells#surface-pattern"); + optgroup->append_single_option_line("bottom_surface_archimedean_direction", "strength_settings_top_bottom_shells#spiral-direction"); optgroup->append_single_option_line("top_bottom_infill_wall_overlap", "strength_settings_top_bottom_shells#infillwall-overlap"); optgroup = page->new_optgroup(L("Infill"), L"param_infill");