diff --git a/src/libslic3r/ClipperUtils.cpp b/src/libslic3r/ClipperUtils.cpp index 31c73c4aa1..ea19eeb559 100644 --- a/src/libslic3r/ClipperUtils.cpp +++ b/src/libslic3r/ClipperUtils.cpp @@ -1,3 +1,7 @@ +#include +#include +#include + #include "ClipperUtils.hpp" #include "Geometry.hpp" #include "ShortestPath.hpp" @@ -930,6 +934,77 @@ Slic3r::Polylines intersection_pl(const Slic3r::Polylines &subject, const Slic3r Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r::Polygons &clip) { return _clipper_pl_closed(ClipperLib::ctIntersection, ClipperUtils::PolygonsProvider(subject), ClipperUtils::PolygonsProvider(clip)); } +// Orca: Sort and orient open polyline fragments produced by clipping `source` with +// intersection_pl(), so that they run in the same order and direction as the source +// polyline. Clipping creates new endpoints at the clip boundary, but it keeps the +// interior source vertices intact, so a fragment's position on the source path is +// recovered exactly by looking its vertices up in the source. Fragments without any +// surviving source vertex lie on a single source segment, found by a nearest-segment +// search. +void restore_source_path_order(const Slic3r::Polyline &source, Slic3r::Polylines &fragments) +{ + const Points &src = source.points; + if (src.size() < 2 || fragments.empty()) + return; + + std::unordered_map source_index; + source_index.reserve(src.size()); + for (size_t i = 0; i < src.size(); ++ i) + source_index.emplace(src[i], i); + + // Sort key: index of the source vertex where the fragment starts, then the signed + // offset of the fragment's start from that vertex, to order multiple fragments cut + // from one long source segment. + std::vector> keys(fragments.size()); + for (size_t n = 0; n < fragments.size(); ++ n) { + Polyline &pl = fragments[n]; + const size_t npos = size_t(-1); + size_t front = npos; + size_t back = npos; + for (const Point &pt : pl.points) + if (auto it = source_index.find(pt); it != source_index.end()) { + front = it->second; + break; + } + for (auto i = pl.points.rbegin(); i != pl.points.rend(); ++ i) + if (auto it = source_index.find(*i); it != source_index.end()) { + back = it->second; + break; + } + Vec2crd source_dir; + if (front == npos) { + // All vertices were created by clipping, thus the whole fragment lies on a + // single source segment. Find that segment. + double best = std::numeric_limits::max(); + for (size_t i = 0; i + 1 < src.size(); ++ i) + if (double d = Line::distance_to_squared(pl.first_point(), src[i], src[i + 1]); d < best) { + best = d; + front = i; + } + back = front; + source_dir = src[front + 1] - src[front]; + } else + source_dir = src[std::min(back + 1, src.size() - 1)] - src[front > 0 ? front - 1 : 0]; + if (front > back) { + pl.reverse(); + std::swap(front, back); + } else if (front == back && + (pl.last_point() - pl.first_point()).cast().dot(source_dir.cast()) < 0.) + pl.reverse(); + const Vec2crd seg = src[std::min(front + 1, src.size() - 1)] - src[front]; + keys[n] = { front, (pl.first_point() - src[front]).cast().dot(seg.cast()) }; + } + + std::vector order(fragments.size()); + std::iota(order.begin(), order.end(), size_t(0)); + std::sort(order.begin(), order.end(), [&keys](size_t a, size_t b) { return keys[a] < keys[b]; }); + Polylines sorted; + sorted.reserve(fragments.size()); + for (size_t n : order) + sorted.emplace_back(std::move(fragments[n])); + fragments = std::move(sorted); +} + Lines _clipper_ln(ClipperLib::ClipType clipType, const Lines &subject, const Polygons &clip) { // convert Lines to Polylines diff --git a/src/libslic3r/ClipperUtils.hpp b/src/libslic3r/ClipperUtils.hpp index 9c2fa23926..5ed161c27a 100644 --- a/src/libslic3r/ClipperUtils.hpp +++ b/src/libslic3r/ClipperUtils.hpp @@ -528,6 +528,10 @@ Slic3r::Polylines intersection_pl(const Slic3r::Polygons &subject, const Slic3r Slic3r::Polylines3 intersection_pl(const Slic3r::Polylines3 &subject, const Slic3r::Polygon &clip); Slic3r::Polylines3 intersection_pl(const Slic3r::Polylines3 &subject, const Slic3r::ExPolygon &clip); +// Orca: Sort and orient open polyline fragments produced by clipping `source` with +// intersection_pl(), so that they run in the same order and direction as the source polyline. +void restore_source_path_order(const Slic3r::Polyline &source, Slic3r::Polylines &fragments); + inline Slic3r::Lines intersection_ln(const Slic3r::Lines &subject, const Slic3r::Polygons &clip) { return _clipper_ln(ClipperLib::ctIntersection, subject, clip); diff --git a/src/libslic3r/Fill/Fill.cpp b/src/libslic3r/Fill/Fill.cpp index fbaade10ab..063481876f 100644 --- a/src/libslic3r/Fill/Fill.cpp +++ b/src/libslic3r/Fill/Fill.cpp @@ -272,10 +272,12 @@ struct SurfaceFillParams // For Gyroid: when true, use the parameterized "optimized" wave. bool gyroid_optimized = false; - bool anisotropic_surfaces{false}; CenterOfSurfacePattern center_of_surface_pattern{CenterOfSurfacePattern::Each_Surface}; bool separated_infills{false}; + // Orca: forced print order of surface fill loops/fragments for center-based patterns. + SurfaceFillOrder fill_order = SurfaceFillOrder::Default; + bool operator<(const SurfaceFillParams &rhs) const { #define RETURN_COMPARE_NON_EQUAL(KEY) if (this->KEY < rhs.KEY) return true; if (this->KEY > rhs.KEY) return false; #define RETURN_COMPARE_NON_EQUAL_TYPED(TYPE, KEY) if (TYPE(this->KEY) < TYPE(rhs.KEY)) return true; if (TYPE(this->KEY) > TYPE(rhs.KEY)) return false; @@ -308,9 +310,9 @@ struct SurfaceFillParams RETURN_COMPARE_NON_EQUAL(skin_infill_depth); RETURN_COMPARE_NON_EQUAL(infill_overhang_angle); RETURN_COMPARE_NON_EQUAL(gyroid_optimized); - RETURN_COMPARE_NON_EQUAL(anisotropic_surfaces); RETURN_COMPARE_NON_EQUAL(center_of_surface_pattern); RETURN_COMPARE_NON_EQUAL(separated_infills); + RETURN_COMPARE_NON_EQUAL_TYPED(unsigned, fill_order); return false; } @@ -337,10 +339,10 @@ struct SurfaceFillParams this->infill_lock_depth == rhs.infill_lock_depth && this->skin_infill_depth == rhs.skin_infill_depth && this->infill_overhang_angle == rhs.infill_overhang_angle && - this->anisotropic_surfaces == rhs.anisotropic_surfaces && this->center_of_surface_pattern == rhs.center_of_surface_pattern && this->separated_infills == rhs.separated_infills && - this->gyroid_optimized == rhs.gyroid_optimized; + this->gyroid_optimized == rhs.gyroid_optimized && + this->fill_order == rhs.fill_order; } }; @@ -879,7 +881,6 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p params.lateral_lattice_angle_1 = region_config.lateral_lattice_angle_1; params.lateral_lattice_angle_2 = region_config.lateral_lattice_angle_2; params.infill_overhang_angle = region_config.infill_overhang_angle; - params.anisotropic_surfaces = region_config.anisotropic_surfaces; params.center_of_surface_pattern = region_config.center_of_surface_pattern; params.separated_infills = region_config.separated_infills; if (params.pattern == ipLockedZag) { @@ -936,6 +937,14 @@ std::vector group_fills(const Layer &layer, LockRegionParam &lock_p params.extruder = region_config.bottom_surface_filament_id; else if (params.extrusion_role == erSolidInfill) params.extruder = region_config.internal_solid_filament_id; + // Orca: forced fill order applies only to top/bottom surfaces filled with a + // center-based pattern; everything else stays at Default to keep batching together. + if (params.pattern == ipConcentric || params.pattern == ipArchimedeanChords || params.pattern == ipOctagramSpiral) { + if (params.extrusion_role == erTopSolidInfill) + params.fill_order = region_config.top_surface_fill_order.value; + else if (params.extrusion_role == erBottomSurface) + params.fill_order = region_config.bottom_surface_fill_order.value; + } // Orca: apply fill multiline only for sparse infill params.multiline = params.extrusion_role == erInternalInfill ? int(region_config.fill_multiline) : 1; @@ -1322,12 +1331,12 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive: auto ®ion_config = layerm->region().config(); params.config = ®ion_config; params.pattern = surface_fill.params.pattern; + params.fill_order = surface_fill.params.fill_order; // Orca: Checking the filling of a centered surface by drawing for each model parts bool is_top_or_bottom = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface; bool is_centered_infill = surface_fill.params.pattern == ipArchimedeanChords || surface_fill.params.pattern == ipOctagramSpiral; if (is_top_or_bottom) { - params.is_anisotropic = surface_fill.params.anisotropic_surfaces; // Orca: anisotropic surfaces params.center_of_surface_pattern = surface_fill.params.center_of_surface_pattern; // Orca: center of surface pattern } // Orca: Each_Model centers the pattern on each model part's bbox; Each_Surface / Each_Assembly diff --git a/src/libslic3r/Fill/FillBase.cpp b/src/libslic3r/Fill/FillBase.cpp index cf74606dd4..29eb6120ed 100644 --- a/src/libslic3r/Fill/FillBase.cpp +++ b/src/libslic3r/Fill/FillBase.cpp @@ -162,10 +162,11 @@ void Fill::fill_surface_extrusion(const Surface* surface, const FillParams& para out.push_back(eec = new ExtrusionEntityCollection()); // Only concentric fills are not sorted. eec->no_sort = this->no_sort(); - // ORCA: special flag for flow rate calibration - auto is_flow_calib = 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(); - if (is_flow_calib || params.is_anisotropic) { // Orca: disable sorting while anisotropic surfaces + // Orca: a forced surface fill order must survive the G-code path planner, which would + // otherwise re-chain and possibly reverse the paths. This also covers the flow rate + // calibration, which forces an outward fill order on its top surfaces. + const bool keep_fill_order = params.fill_order != SurfaceFillOrder::Default; + if (keep_fill_order) { eec->no_sort = true; } size_t idx = eec->entities.size(); @@ -180,14 +181,13 @@ 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 || keep_fill_order) { for (size_t i = idx; i < eec->entities.size(); i++) eec->entities[i]->set_reverse(); } // Orca: run gap fill - if (!(params.is_anisotropic)) // Orca: Disable gap filling while anisotropic - this->_create_gap_fill(surface, params, eec); + this->_create_gap_fill(surface, params, eec); } } diff --git a/src/libslic3r/Fill/FillBase.hpp b/src/libslic3r/Fill/FillBase.hpp index ffeed3045b..8128b9c9d1 100644 --- a/src/libslic3r/Fill/FillBase.hpp +++ b/src/libslic3r/Fill/FillBase.hpp @@ -100,13 +100,16 @@ struct FillParams bool dont_sort{ false }; // do not sort the lines, just simply connect them bool can_reverse{true}; + // Orca: forced print order of surface fill loops/fragments for center-based patterns + // (Concentric, Archimedean Chords, Octagram Spiral). Default keeps shortest-path ordering. + SurfaceFillOrder fill_order { SurfaceFillOrder::Default }; + float horiz_move{0.0}; //move infill to get cross zag pattern bool symmetric_infill_y_axis{false}; coord_t symmetric_y_axis{0}; bool locked_zag{false}; float infill_lock_depth{0.0}; float skin_infill_depth{0.0}; - bool is_anisotropic{false}; CenterOfSurfacePattern center_of_surface_pattern{CenterOfSurfacePattern::Each_Surface}; }; static_assert(IsTriviallyCopyable::value, "FillParams class is not POD (and it should be - see constructor)."); diff --git a/src/libslic3r/Fill/FillConcentric.cpp b/src/libslic3r/Fill/FillConcentric.cpp index 4f2a0b8970..a75d2ed7d3 100644 --- a/src/libslic3r/Fill/FillConcentric.cpp +++ b/src/libslic3r/Fill/FillConcentric.cpp @@ -41,6 +41,10 @@ void FillConcentric::_fill_surface_single( // generate paths from the outermost to the innermost, to avoid // adhesion problems of the first central tiny loops loops = union_pt_chained_outside_in(loops); + + // Orca: an outward fill order prints the innermost loops first instead. + if (params.fill_order == SurfaceFillOrder::Outward) + std::reverse(loops.begin(), loops.end()); // split paths using a nearest neighbor search size_t iPathFirst = polylines_out.size(); @@ -108,6 +112,17 @@ void FillConcentric::_fill_surface_single(const FillParams& params, all_extrusions.emplace_back(&wall); } + // Orca: a forced fill order prints the loops in strictly monotonic depth order so + // that surfaces broken up by holes or slots cannot hop outward and back inward. + const bool forced_fill_order = params.fill_order != SurfaceFillOrder::Default; + if (forced_fill_order) { + const bool outward = params.fill_order == SurfaceFillOrder::Outward; + std::stable_sort(all_extrusions.begin(), all_extrusions.end(), + [outward](const Arachne::ExtrusionLine *a, const Arachne::ExtrusionLine *b) { + return outward ? a->inset_idx > b->inset_idx : a->inset_idx < b->inset_idx; + }); + } + // Split paths using a nearest neighbor search. size_t firts_poly_idx = thick_polylines_out.size(); Point last_pos(0, 0); @@ -136,7 +151,8 @@ void FillConcentric::_fill_surface_single(const FillParams& params, if (j < thick_polylines_out.size()) thick_polylines_out.erase(thick_polylines_out.begin() + int(j), thick_polylines_out.end()); - reorder_by_shortest_traverse(thick_polylines_out); + if (!forced_fill_order) + reorder_by_shortest_traverse(thick_polylines_out); } else { Polylines polylines; diff --git a/src/libslic3r/Fill/FillPlanePath.cpp b/src/libslic3r/Fill/FillPlanePath.cpp index 01f4ca00da..edfbaef7d6 100644 --- a/src/libslic3r/Fill/FillPlanePath.cpp +++ b/src/libslic3r/Fill/FillPlanePath.cpp @@ -133,49 +133,26 @@ void FillPlanePath::_fill_surface_single( polylines = intersection_pl(std::move(polylines), expolygon); if (!polylines.empty()) { Polylines chained; - if (!params.is_anisotropic) { // Orca: not anisotropic surface - if ((params.dont_connect() || params.density > 0.5)) { - // ORCA: special flag for flow rate calibration - auto is_flow_calib = 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), nullptr); - - // Then add the center spiral back - chained.push_back(std::move(center_spiral)); - } else { - chained = chain_polylines(std::move(polylines), nullptr); + if (params.dont_connect() || params.density > 0.5) { + if (params.fill_order != SurfaceFillOrder::Default) { + // Orca: print the fragments in the order they appear along the generated + // path, which runs from the center outwards. The Euclidean distance from + // the center cannot be used for this: along the Octagram Spiral the radius + // oscillates by far more than the ring spacing, so fragments of different + // rings would interleave. + restore_source_path_order(polyline, polylines); + chained = std::move(polylines); + if (params.fill_order == SurfaceFillOrder::Inward) { + // The source path runs from the center outwards; flip everything for inward. + std::reverse(chained.begin(), chained.end()); + for (Polyline &pl : chained) + pl.reverse(); } - } else - connect_infill(std::move(polylines), expolygon, chained, this->spacing, params); - } else { // Orca: anisotropic surface - const Point _center(0., 0.); - for (Polyline& segment : polylines) { // sort paths by its direction - if (segment.size() > 1) { // need at least two points to evaluate direction - if (segment.first_point().ccw(segment.points[1], _center) < 0) - segment.reverse(); - } - chained.emplace_back(std::move(segment)); + } else { + chained = chain_polylines(std::move(polylines), nullptr); } - std::sort(chained.begin(), chained.end(), [&_center](const Polyline& a, const Polyline& b) { // just sort polylines from center to outside - return a.distance_to(_center) < b.distance_to(_center); - }); - } + } else + connect_infill(std::move(polylines), expolygon, chained, this->spacing, params); // paths must be repositioned and rotated back for (Polyline& pl : chained) { pl.translate(shift.x(), shift.y()); diff --git a/src/libslic3r/Preset.cpp b/src/libslic3r/Preset.cpp index f48e3d9145..500ef42738 100644 --- a/src/libslic3r/Preset.cpp +++ b/src/libslic3r/Preset.cpp @@ -1046,6 +1046,8 @@ static std::vector s_Preset_print_options{ "top_surface_expansion_margin", "top_surface_expansion_direction", "bottom_surface_pattern", + "top_surface_fill_order", + "bottom_surface_fill_order", "infill_direction", "solid_infill_direction", "top_layer_direction", @@ -1061,7 +1063,6 @@ static std::vector s_Preset_print_options{ "skin_infill_density", "align_infill_direction_to_model", "extra_solid_infills", - "anisotropic_surfaces", "center_of_surface_pattern", "separated_infills", "minimum_sparse_infill_area", @@ -1307,7 +1308,6 @@ static std::vector s_Preset_print_options{ "interlocking_depth", "interlocking_boundary_avoidance", "interlocking_beam_width", - "calib_flowrate_topinfill_special_order", // Z Anti-Aliasing (ZAA) "zaa_enabled", "zaa_minimize_perimeter_height", diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 8379e0d086..e37e873c0b 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -212,7 +212,6 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n "chamber_minimal_temperature", "thumbnails", "thumbnails_format", - "anisotropic_surfaces", "center_of_surface_pattern", "separated_infills", "seam_gap", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 724397d3a3..d8ed7ace58 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -313,6 +313,14 @@ static t_config_enum_values s_keys_map_WallDirection{ }; CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WallDirection) +//Orca +static t_config_enum_values s_keys_map_SurfaceFillOrder{ + { "default", int(SurfaceFillOrder::Default) }, + { "outward", int(SurfaceFillOrder::Outward) }, + { "inward", int(SurfaceFillOrder::Inward) }, +}; +CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(SurfaceFillOrder) + //BBS static t_config_enum_values s_keys_map_PrintSequence { { "by layer", int(PrintSequence::ByLayer) }, @@ -2322,6 +2330,40 @@ void PrintConfigDef::init_fff_params() def->max = 100; def->set_default_value(new ConfigOptionPercent(100)); + auto def_top_fill_order = def = this->add("top_surface_fill_order", coEnum); + def->label = L("Top surface fill order"); + def->category = L("Strength"); + def->tooltip = L("Direction in which top surfaces are filled when using a center-based pattern " + "(Concentric, Archimedean Chords, Octagram Spiral).\n" + "Outward starts at the center of the surface, so any excess material is pushed " + "towards the edge where it is least visible. Inward starts at the edge and ends " + "with the tight curves at the center.\n" + "Default uses shortest-path ordering, which may run in either direction."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values.push_back("default"); + def->enum_values.push_back("outward"); + def->enum_values.push_back("inward"); + def->enum_labels.push_back(L("Default")); + def->enum_labels.push_back(L("Outward")); + def->enum_labels.push_back(L("Inward")); + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(SurfaceFillOrder::Default)); + + def = this->add("bottom_surface_fill_order", coEnum); + def->label = L("Bottom surface fill order"); + def->category = L("Strength"); + def->tooltip = L("Direction in which bottom surfaces are filled when using a center-based pattern " + "(Concentric, Archimedean Chords, Octagram Spiral).\n" + "Inward starts each surface with the wider outer curves, which improves first layer " + "adhesion on build plates where the tight curves at the center may not stick. " + "Outward starts at the center, pushing any excess material towards the edge.\n" + "Default uses shortest-path ordering, which may run in either direction."); + def->enum_keys_map = &ConfigOptionEnum::get_enum_values(); + def->enum_values = def_top_fill_order->enum_values; + def->enum_labels = def_top_fill_order->enum_labels; + def->mode = comAdvanced; + def->set_default_value(new ConfigOptionEnum(SurfaceFillOrder::Default)); + def = this->add("internal_solid_infill_pattern", coEnum); def->label = L("Internal solid infill pattern"); def->category = L("Strength"); @@ -4605,11 +4647,6 @@ void PrintConfigDef::init_fff_params() def->mode = comAdvanced; def->set_default_value(new ConfigOptionInt(2)); - // ORCA: special flag for flow rate calibration - def = this->add("calib_flowrate_topinfill_special_order", coBool); - def->mode = comDevelop; - def->set_default_value(new ConfigOptionBool(false)); - def = this->add("ironing_type", coEnum); def->label = L("Ironing type"); def->category = L("Quality"); @@ -7198,17 +7235,6 @@ void PrintConfigDef::init_fff_params() def->min = 0; def->set_default_value(new ConfigOptionFloat(0.6)); - def = this->add("anisotropic_surfaces", coBool); - def->label = L("Anisotropic surfaces"); - def->category = L("Strength"); - def->tooltip = L("Anisotropic patterns on the top and bottom surfaces.\n" - "Co-directional printing mode will be applied. For certain patterns, omni-directional filling provides color " - "dispersion when using multi-colored or silk plastics.\n" - "This option disable the gap fill.\n" - "This option can increase a printing time."); - def->mode = comExpert; - def->set_default_value(new ConfigOptionBool(false)); - def = this->add("separated_infills", coBool); def->label = L("Separated infills"); def->category = L("Strength"); @@ -8966,6 +8992,8 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va "internal_bridge_support_thickness", "top_area_threshold", "reduce_wall_solid_infill","filament_load_time","filament_unload_time", "smooth_coefficient", "overhang_totally_speed", "silent_mode", "overhang_speed_classic", "filament_prime_volume", + "calib_flowrate_topinfill_special_order", + "anisotropic_surfaces", // superseded by top_surface_fill_order / bottom_surface_fill_order }; if (ignore.find(opt_key) != ignore.end()) { diff --git a/src/libslic3r/PrintConfig.hpp b/src/libslic3r/PrintConfig.hpp index 1a2540a82b..2cad1da3d2 100644 --- a/src/libslic3r/PrintConfig.hpp +++ b/src/libslic3r/PrintConfig.hpp @@ -193,6 +193,15 @@ enum class WallDirection Count, }; +// Orca: print order of surface fill loops/fragments for center-based fill patterns +// (Concentric, Archimedean Chords, Octagram Spiral). +enum class SurfaceFillOrder { + Default, + Outward, + Inward, + Count, +}; + //BBS enum class PrintSequence { ByLayer, @@ -660,6 +669,7 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(WipeTowerWallType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(ToolChangeOrderingType) CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PowerLossRecoveryMode) +CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(SurfaceFillOrder) #undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS @@ -1209,9 +1219,6 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionInt, interlocking_beam_layer_count)) ((ConfigOptionInt, interlocking_depth)) ((ConfigOptionInt, interlocking_boundary_avoidance)) - - // Orca: internal use only - ((ConfigOptionBool, calib_flowrate_topinfill_special_order)) // ORCA: special flag for flow rate calibration ) // This object is mapped to Perl as Slic3r::Config::PrintRegion. @@ -1235,6 +1242,8 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionPercent, bottom_surface_density)) ((ConfigOptionEnum, top_surface_pattern)) ((ConfigOptionEnum, bottom_surface_pattern)) + ((ConfigOptionEnum, top_surface_fill_order)) + ((ConfigOptionEnum, bottom_surface_fill_order)) ((ConfigOptionEnum, internal_solid_infill_pattern)) ((ConfigOptionFloatOrPercent, outer_wall_line_width)) ((ConfigOptionFloatsNullable, outer_wall_speed)) @@ -1255,7 +1264,6 @@ PRINT_CONFIG_CLASS_DEFINE( ((ConfigOptionFloat, lightning_prune_angle)) ((ConfigOptionFloat, lightning_straightening_angle)) ((ConfigOptionBool, align_infill_direction_to_model)) - ((ConfigOptionBool, anisotropic_surfaces)) ((ConfigOptionEnum, center_of_surface_pattern)) ((ConfigOptionBool, separated_infills)) ((ConfigOptionString, extra_solid_infills)) diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index d89a9b8ab4..0d31f6c04c 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1393,6 +1393,8 @@ bool PrintObject::invalidate_state_by_config_options( } else if ( opt_key == "top_surface_pattern" || opt_key == "bottom_surface_pattern" + || opt_key == "top_surface_fill_order" + || opt_key == "bottom_surface_fill_order" || opt_key == "internal_solid_infill_pattern" || opt_key == "external_fill_link_max_length" || opt_key == "infill_anchor" @@ -1400,7 +1402,6 @@ bool PrintObject::invalidate_state_by_config_options( || opt_key == "top_surface_line_width" || opt_key == "top_surface_density" || opt_key == "bottom_surface_density" - || opt_key == "anisotropic_surfaces" || opt_key == "center_of_surface_pattern" || opt_key == "separated_infills" || opt_key == "initial_layer_line_width" diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 4bd9749073..57f25eba24 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -721,7 +721,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in toggle_field("top_surface_expansion_direction", has_top_surface_expansion); // Orca: Archimedean Chords and Octagram Spiral are the centered surface patterns that the - // pattern-centering, anisotropic-surface and separated-infill features act on. + // pattern-centering feature acts on. auto is_centered_pattern = [](InfillPattern p) { return p == InfillPattern::ipArchimedeanChords || p == InfillPattern::ipOctagramSpiral; }; @@ -729,9 +729,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in bool is_bottom_centered = is_centered_pattern(config->option>("bottom_surface_pattern")->value); bool has_centered_surface = (has_top_shell && is_top_centered) || (has_bottom_shell && is_bottom_centered); - // Orca: center of surface pattern / anisotropic surfaces + // Orca: center of surface pattern toggle_line("center_of_surface_pattern", has_centered_surface); - toggle_line("anisotropic_surfaces", has_centered_surface); // Orca: separate infills bool is_internal_infill_separable = is_separable_infill_pattern(config->option>("sparse_infill_pattern")->value) || @@ -739,9 +738,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in config->opt_string("solid_infill_rotate_template") != ""; toggle_line("separated_infills", is_internal_infill_separable); - // Orca: no need gaps - for (auto el : {"gap_fill_target", "filter_out_gap_fill"}) - toggle_field(el, !config->opt_bool("anisotropic_surfaces")); + // Fill order is only meaningful for the center-based surface fill patterns; hide it otherwise. + auto is_centered_fill = [](InfillPattern p) { return p == ipConcentric || p == ipArchimedeanChords || p == ipOctagramSpiral; }; + toggle_line("top_surface_fill_order", has_top_shell && is_centered_fill(config->opt_enum("top_surface_pattern"))); + toggle_line("bottom_surface_fill_order", has_bottom_shell && is_centered_fill(config->opt_enum("bottom_surface_pattern"))); for (auto el : { "infill_direction", "sparse_infill_line_width", "gap_fill_target","filter_out_gap_fill","infill_wall_overlap", "bridge_angle", "internal_bridge_angle", "relative_bridge_angle", diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 1d4d741931..45c3098d36 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -13904,7 +13904,6 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i _obj->config.set_key_value("top_solid_infill_flow_ratio", new ConfigOptionFloat(1.0f)); _obj->config.set_key_value("infill_direction", new ConfigOptionFloat(45)); _obj->config.set_key_value("solid_infill_direction", new ConfigOptionFloat(135)); - _obj->config.set_key_value("anisotropic_surfaces", new ConfigOptionBool(false)); _obj->config.set_key_value("center_of_surface_pattern", new ConfigOptionEnum(CenterOfSurfacePattern::Each_Surface)); _obj->config.set_key_value("separated_infills", new ConfigOptionBool(false)); _obj->config.set_key_value("align_infill_direction_to_model", new ConfigOptionBool(true)); @@ -13914,7 +13913,8 @@ void adjust_settings_for_flowrate_calib(ModelObjectPtrs& objects, bool linear, i _obj->config.set_key_value("seam_slope_type", new ConfigOptionEnum(SeamScarfType::None)); _obj->config.set_key_value("gap_fill_target", new ConfigOptionEnum(GapFillTarget::gftNowhere)); print_config->set_key_value("max_volumetric_extrusion_rate_slope", new ConfigOptionFloat(0)); - _obj->config.set_key_value("calib_flowrate_topinfill_special_order", new ConfigOptionBool(true)); + // ORCA: print the top surface spiral from the center outwards, so the tiles are comparable. + _obj->config.set_key_value("top_surface_fill_order", new ConfigOptionEnum(SurfaceFillOrder::Outward)); // extract flowrate from name, filename format: flowrate_xxx std::string obj_name = _obj->name; diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index 06045912a1..bf0b1337bf 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -2766,6 +2766,7 @@ 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_fill_order", "strength_settings_top_bottom_shells#fill-order"); optgroup->append_single_option_line("top_layer_direction", "strength_settings_infill#top-bottom-direction"); optgroup->append_single_option_line("top_surface_expansion", "strength_settings_top_bottom_shells#surface-expansion"); optgroup->append_single_option_line("top_surface_expansion_margin", "strength_settings_top_bottom_shells#surface-expansion-margin"); @@ -2774,9 +2775,9 @@ void TabPrint::build() 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_fill_order", "strength_settings_top_bottom_shells#fill-order"); optgroup->append_single_option_line("bottom_layer_direction", "strength_settings_infill#top-bottom-direction"); optgroup->append_single_option_line("center_of_surface_pattern", "strength_settings_top_bottom_shells#center-surface-pattern-on"); - optgroup->append_single_option_line("anisotropic_surfaces", "strength_settings_top_bottom_shells#anisotropic-surfaces"); 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");