Enforce Archimedean chords direction on top / bottom surface

This commit is contained in:
Suren Khorenyan 2025-12-23 16:32:34 +03:00
parent 59ad126b48
commit 199721cf28
7 changed files with 158 additions and 30 deletions

View file

@ -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<FillArchimedeanChords*>(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();
}

View file

@ -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 &params,
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<FillArchimedeanChords*>(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<FillArchimedeanChords*>(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

View file

@ -884,10 +884,11 @@ static std::vector<std::string> 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",

View file

@ -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<InfillPattern>(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<ArchimedeanChordsDirection>::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>(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<ArchimedeanChordsDirection>::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>(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<InfillPattern>(ipMonotonic));
def = this->add("outer_wall_line_width", coFloatOrPercent);
def->label = L("Outer wall");
def->category = L("Quality");

View file

@ -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<InfillPattern>, top_surface_pattern))
((ConfigOptionEnum<ArchimedeanChordsDirection>, top_surface_archimedean_direction))
((ConfigOptionEnum<InfillPattern>, bottom_surface_pattern))
((ConfigOptionEnum<ArchimedeanChordsDirection>, bottom_surface_archimedean_direction))
((ConfigOptionEnum<InfillPattern>, internal_solid_infill_pattern))
((ConfigOptionFloatOrPercent, outer_wall_line_width))
((ConfigOptionFloat, outer_wall_speed))

View file

@ -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<InfillPattern>("top_surface_pattern") == ipArchimedeanChords;
bool has_bottom_archimedean = has_bottom_shell &&
config->opt_enum<InfillPattern>("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",

View file

@ -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");