ENH: Relative bridge direction + Align bridge/Ironing angles with model (#12055)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
This commit is contained in:
@@ -935,10 +935,13 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
params.fixed_angle = !region_config.solid_infill_rotate_template.value.empty();
|
||||
}
|
||||
params.bridge_angle = float(surface.bridge_angle);
|
||||
|
||||
|
||||
// ORCA: Align infill angle to model
|
||||
float align_offset = 0.f;
|
||||
if (region_config.align_infill_direction_to_model) {
|
||||
auto m = layer.object()->trafo().matrix();
|
||||
params.angle += atan2((float) m(1, 0), (float) m(0, 0));
|
||||
align_offset = atan2((float)m(1, 0), (float)m(0, 0));
|
||||
params.angle += align_offset;
|
||||
}
|
||||
|
||||
// Calculate the actual flow we'll be using for this infill.
|
||||
@@ -1024,6 +1027,7 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
if (fill.region_id == size_t(-1)) {
|
||||
fill.region_id = region_id;
|
||||
fill.surface = surface;
|
||||
fill.surface.bridge_angle = params->bridge_angle;
|
||||
fill.expolygons.emplace_back(std::move(fill.surface.expolygon));
|
||||
//BBS
|
||||
fill.region_id_group.push_back(region_id);
|
||||
@@ -1578,7 +1582,7 @@ void Layer::make_ironing()
|
||||
if (ironing_params.extruder != -1) {
|
||||
//TODO just_infill is currently not used.
|
||||
ironing_params.just_infill = false;
|
||||
// Get filament-specific overrides if configured, otherwise use default values
|
||||
// ORCA: Get filament-specific overrides if configured, otherwise use process values
|
||||
size_t extruder_idx = ironing_params.extruder - 1;
|
||||
ironing_params.line_spacing = (!config.filament_ironing_spacing.is_nil(extruder_idx)
|
||||
? config.filament_ironing_spacing.get_at(extruder_idx)
|
||||
@@ -1592,7 +1596,12 @@ void Layer::make_ironing()
|
||||
ironing_params.speed = (!config.filament_ironing_speed.is_nil(extruder_idx)
|
||||
? config.filament_ironing_speed.get_at(extruder_idx)
|
||||
: config.ironing_speed);
|
||||
ironing_params.angle = (config.ironing_angle_fixed ? 0 : calculate_infill_rotation_angle(this->object(), this->id(), config.solid_infill_direction.value, config.solid_infill_rotate_template.value)) + config.ironing_angle * M_PI / 180.;
|
||||
double ironing_angle = (config.ironing_angle_fixed ? 0 : calculate_infill_rotation_angle(this->object(), this->id(), config.solid_infill_direction.value, config.solid_infill_rotate_template.value)) + config.ironing_angle * M_PI / 180.;
|
||||
if (config.align_infill_direction_to_model) {
|
||||
auto m = this->object()->trafo().matrix();
|
||||
ironing_angle += atan2((double)m(1, 0), (double)m(0, 0));
|
||||
}
|
||||
ironing_params.angle = ironing_angle;
|
||||
ironing_params.fixed_angle = config.ironing_angle_fixed || !config.solid_infill_rotate_template.value.empty();
|
||||
ironing_params.pattern = config.ironing_pattern;
|
||||
ironing_params.layerm = layerm;
|
||||
|
||||
@@ -83,6 +83,12 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
(this->layer()->id() >= size_t(region_config.bottom_shell_layers.value) &&
|
||||
this->layer()->print_z >= region_config.bottom_shell_thickness - EPSILON);
|
||||
|
||||
double model_rotation_rad = 0.0;
|
||||
if (region_config.align_infill_direction_to_model) {
|
||||
auto m = this->layer()->object()->trafo().matrix();
|
||||
model_rotation_rad = std::atan2((double)m(1, 0), (double)m(0, 0));
|
||||
}
|
||||
|
||||
PerimeterGenerator g(
|
||||
// input:
|
||||
&slices,
|
||||
@@ -94,6 +100,7 @@ void LayerRegion::make_perimeters(const SurfaceCollection &slices, const LayerRe
|
||||
&this->layer()->object()->config(),
|
||||
&print_config,
|
||||
spiral_mode,
|
||||
model_rotation_rad,
|
||||
|
||||
// output:
|
||||
&this->perimeters,
|
||||
@@ -517,10 +524,27 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
SurfaceCollection bridges;
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges. layer" << this->layer()->print_z;
|
||||
const double custom_angle = this->region().config().bridge_angle.value;
|
||||
bridges.surfaces = custom_angle > 0 ?
|
||||
expand_merge_surfaces(this->fill_surfaces.surfaces, stBottomBridge, expansion_zones, closing_radius, Geometry::deg2rad(custom_angle)) :
|
||||
// ORCA: Relative/Align Bridge Angle
|
||||
const auto ®ion_config = this->region().config();
|
||||
const double custom_angle_deg = region_config.bridge_angle.value;
|
||||
const bool relative_angle = region_config.relative_bridge_angle.value;
|
||||
const double custom_angle_rad = Geometry::deg2rad(custom_angle_deg);
|
||||
|
||||
double align_offset_rad = 0.0;
|
||||
if (region_config.align_infill_direction_to_model) {
|
||||
auto m = this->layer()->object()->trafo().matrix();
|
||||
align_offset_rad = std::atan2((double)m(1, 0), (double)m(0, 0));
|
||||
}
|
||||
|
||||
bridges.surfaces = (custom_angle_deg > 0.0 && !relative_angle) ?
|
||||
expand_merge_surfaces(this->fill_surfaces.surfaces, stBottomBridge, expansion_zones, closing_radius, custom_angle_rad + align_offset_rad) :
|
||||
expand_bridges_detect_orientations(this->fill_surfaces.surfaces, expansion_zones, closing_radius);
|
||||
if (custom_angle_deg > 0.0 && relative_angle) {
|
||||
for (Surface &bridge_surface : bridges.surfaces) {
|
||||
if (bridge_surface.bridge_angle >= 0)
|
||||
bridge_surface.bridge_angle += custom_angle_rad;
|
||||
}
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(trace) << "Processing external surface, detecting bridges - done";
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
{
|
||||
@@ -782,12 +806,25 @@ void LayerRegion::process_external_surfaces(const Layer *lower_layer, const Poly
|
||||
// would get merged into a single one while they need different directions
|
||||
// also, supply the original expolygon instead of the grown one, because in case
|
||||
// of very thin (but still working) anchors, the grown expolygon would go beyond them
|
||||
double custom_angle = Geometry::deg2rad(this->region().config().bridge_angle.value);
|
||||
if (custom_angle > 0.0) {
|
||||
bridges[idx_last].bridge_angle = custom_angle;
|
||||
// ORCA: Relative/Align Bridge Angle
|
||||
const auto ®ion_config = this->region().config();
|
||||
const double custom_angle_deg = region_config.bridge_angle.value;
|
||||
const bool relative_angle = region_config.relative_bridge_angle.value;
|
||||
const double custom_angle_rad = Geometry::deg2rad(custom_angle_deg);
|
||||
|
||||
double align_offset_rad = 0.0;
|
||||
if (region_config.align_infill_direction_to_model) {
|
||||
auto m = this->layer()->object()->trafo().matrix();
|
||||
align_offset_rad = std::atan2((double)m(1, 0), (double)m(0, 0));
|
||||
}
|
||||
|
||||
if (custom_angle_deg > 0.0 && !relative_angle) {
|
||||
bridges[idx_last].bridge_angle = custom_angle_rad + align_offset_rad;
|
||||
} else {
|
||||
auto [bridging_dir, unsupported_dist] = detect_bridging_direction(to_polygons(initial), to_polygons(lower_layer->lslices));
|
||||
bridges[idx_last].bridge_angle = PI + std::atan2(bridging_dir.y(), bridging_dir.x());
|
||||
if (custom_angle_deg > 0.0 && relative_angle)
|
||||
bridges[idx_last].bridge_angle += custom_angle_rad;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1760,8 +1760,14 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
|
||||
BridgeDetector detector{ unsupported,
|
||||
lower_island.expolygons,
|
||||
perimeter_spacing / 4}; // Use a finer BridgeDetector. This affects coverage resolution, not extrusion spacing.
|
||||
|
||||
if (detector.detect_angle(Geometry::deg2rad(this->config->bridge_angle.value)))
|
||||
// ORCA: Relative/Align Bridge Angle
|
||||
const double custom_angle_deg = this->config->bridge_angle.value;
|
||||
const bool relative_angle = this->config->relative_bridge_angle.value;
|
||||
const double detect_angle_rad = (custom_angle_deg > 0.0 && !relative_angle)
|
||||
? Geometry::deg2rad(custom_angle_deg) +
|
||||
(this->config->align_infill_direction_to_model ? this->m_model_rotation_rad : 0.0)
|
||||
: 0.0;
|
||||
if (detector.detect_angle(detect_angle_rad))
|
||||
expolygons_append(bridgeable, union_ex(detector.coverage(-1, true)));
|
||||
}
|
||||
if (!bridgeable.empty() && !surface->expolygon.holes.empty()) { // keep out if cannot be bridged or no holes to bridge
|
||||
|
||||
@@ -117,6 +117,7 @@ public:
|
||||
const PrintObjectConfig* object_config,
|
||||
const PrintConfig* print_config,
|
||||
const bool spiral_mode,
|
||||
const double model_rotation_rad,
|
||||
// Output:
|
||||
// Loops with the external thin walls
|
||||
ExtrusionEntityCollection* loops,
|
||||
@@ -132,6 +133,7 @@ public:
|
||||
config(config), object_config(object_config), print_config(print_config),
|
||||
m_spiral_vase(spiral_mode),
|
||||
m_scaled_resolution(scaled<double>(print_config->resolution.value > EPSILON ? print_config->resolution.value : EPSILON)),
|
||||
m_model_rotation_rad(model_rotation_rad),
|
||||
loops(loops), gap_fill(gap_fill), fill_surfaces(fill_surfaces), fill_no_overlap(fill_no_overlap),
|
||||
m_ext_mm3_per_mm(-1), m_mm3_per_mm(-1), m_mm3_per_mm_overhang(-1), m_ext_mm3_per_mm_smaller_width(-1)
|
||||
{}
|
||||
@@ -157,6 +159,7 @@ private:
|
||||
private:
|
||||
bool m_spiral_vase;
|
||||
double m_scaled_resolution;
|
||||
double m_model_rotation_rad;
|
||||
double m_ext_mm3_per_mm;
|
||||
double m_mm3_per_mm;
|
||||
double m_mm3_per_mm_overhang;
|
||||
|
||||
@@ -1161,6 +1161,7 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"small_perimeter_threshold",
|
||||
"bridge_angle",
|
||||
"internal_bridge_angle",
|
||||
"relative_bridge_angle",
|
||||
"filter_out_gap_fill",
|
||||
"travel_acceleration",
|
||||
"inner_wall_acceleration",
|
||||
|
||||
@@ -1241,11 +1241,16 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = L("External bridge infill direction");
|
||||
def->category = L("Strength");
|
||||
// xgettext:no-c-format, no-boost-format
|
||||
def->tooltip = L("Bridging angle override. If left to zero, the bridging angle will be calculated "
|
||||
"automatically. Otherwise the provided angle will be used for external bridges. "
|
||||
"Use 180° for zero angle.");
|
||||
def->tooltip = L("External Bridging angle override.\n"
|
||||
"If left to zero, the bridging angle will be calculated automatically for each specific bridge.\n"
|
||||
"Otherwise the provided angle will be used according to:\n"
|
||||
" - The absolute coordinates\n"
|
||||
" - The absolute coordinates + Model rotation: If Align infill direction to model is enabled\n"
|
||||
" - The optimal automatic angle + this value: If 'Relative Bridge Angle' is enabled\n\n"
|
||||
"Use 180° for zero absolute angle.");
|
||||
def->sidetext = u8"°"; // degrees, don't need translation
|
||||
def->min = 0;
|
||||
def->max = 180;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.));
|
||||
|
||||
@@ -1253,14 +1258,28 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("internal_bridge_angle", coFloat);
|
||||
def->label = L("Internal bridge infill direction");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Internal bridging angle override. If left to zero, the bridging angle will be calculated "
|
||||
"automatically. Otherwise the provided angle will be used for internal bridges. "
|
||||
"Use 180° for zero angle.\n\nIt is recommended to leave it at 0 unless there is a specific model need not to.");
|
||||
def->tooltip = L("Internal Bridging angle override.\n"
|
||||
"If left to zero, the bridging angle will be calculated automatically for each specific bridge.\n"
|
||||
"Otherwise the provided angle will be used according to:\n"
|
||||
" - The absolute coordinates\n"
|
||||
" - The absolute coordinates + Model rotation: If Align infill direction to model is enabled\n"
|
||||
" - The optimal automatic angle + this value: If 'Relative Bridge Angle' is enabled\n\n"
|
||||
"Use 180° for zero absolute angle.");
|
||||
def->sidetext = u8"°"; // degrees, don't need translation
|
||||
def->min = 0;
|
||||
def->max = 180;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(0.));
|
||||
|
||||
// ORCA: Relative bridge angle
|
||||
def = this->add("relative_bridge_angle", coBool);
|
||||
def->label = L("Relative bridge angle");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("When enabled, the bridge angle values are added to the automatically calculated bridge direction instead of overriding it.\n"
|
||||
"Recommended to add a small angle (<10°) to improve bridge covering in closed shapes.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("bridge_density", coPercent);
|
||||
def->label = L("External bridge density");
|
||||
def->category = L("Strength");
|
||||
@@ -2924,7 +2943,8 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("align_infill_direction_to_model", coBool);
|
||||
def->label = L("Align infill direction to model");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Aligns infill and surface fill directions to follow the model's orientation on the build plate. When enabled, fill directions rotate with the model to maintain optimal strength characteristics.");
|
||||
def->tooltip = L("Aligns infill, bridge, ironing and surface fill directions to follow the model's orientation on the build plate.\n"
|
||||
"When enabled, directions rotate with the model to maintain optimal strength characteristics.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
|
||||
@@ -1080,6 +1080,7 @@ PRINT_CONFIG_CLASS_DEFINE(
|
||||
((ConfigOptionFloat, bottom_shell_thickness))
|
||||
((ConfigOptionFloat, bridge_angle))
|
||||
((ConfigOptionFloat, internal_bridge_angle)) // ORCA: Internal bridge angle override
|
||||
((ConfigOptionBool, relative_bridge_angle)) // ORCA: Relative bridge angle flag
|
||||
((ConfigOptionFloat, bridge_flow))
|
||||
((ConfigOptionFloat, internal_bridge_flow))
|
||||
((ConfigOptionFloat, bridge_speed))
|
||||
|
||||
@@ -1275,6 +1275,7 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "ensure_vertical_shell_thickness"
|
||||
|| opt_key == "bridge_angle"
|
||||
|| opt_key == "internal_bridge_angle" // ORCA: Internal bridge angle override
|
||||
|| opt_key == "relative_bridge_angle" // ORCA: Relative bridge angle
|
||||
//BBS
|
||||
|| opt_key == "bridge_density"
|
||||
|| opt_key == "internal_bridge_density") {
|
||||
@@ -3217,8 +3218,19 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
|
||||
// ORCA: Internal bridge angle override
|
||||
if (candidate.region->region().config().internal_bridge_angle > 0)
|
||||
bridging_angle = candidate.region->region().config().internal_bridge_angle.value * PI / 180.0; // Convert degrees to radians
|
||||
if (candidate.region->region().config().internal_bridge_angle.value > 0) {
|
||||
const auto ®ion_config = candidate.region->region().config();
|
||||
const double custom_angle_rad = Geometry::deg2rad(region_config.internal_bridge_angle.value);
|
||||
if (region_config.relative_bridge_angle.value)
|
||||
bridging_angle += custom_angle_rad;
|
||||
else {
|
||||
bridging_angle = custom_angle_rad;
|
||||
if (region_config.align_infill_direction_to_model) {
|
||||
auto m = po->trafo().matrix();
|
||||
bridging_angle += std::atan2((double)m(1, 0), (double)m(0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());
|
||||
if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) {
|
||||
|
||||
@@ -655,7 +655,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
toggle_field("bottom_surface_density", has_bottom_shell);
|
||||
|
||||
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",
|
||||
"sparse_infill_speed", "bridge_speed", "internal_bridge_speed", "bridge_angle", "internal_bridge_angle", "relative_bridge_angle",
|
||||
"solid_infill_direction", "solid_infill_rotate_template", "internal_solid_infill_pattern", "solid_infill_filament",
|
||||
})
|
||||
toggle_field(el, have_infill || has_solid_infill);
|
||||
|
||||
@@ -2499,6 +2499,7 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("extra_solid_infills", "strength_settings_infill#extra-solid-infill");
|
||||
optgroup->append_single_option_line("bridge_angle", "strength_settings_advanced#bridge-infill-direction");
|
||||
optgroup->append_single_option_line("internal_bridge_angle", "strength_settings_advanced#bridge-infill-direction"); // ORCA: Internal bridge angle override
|
||||
optgroup->append_single_option_line("relative_bridge_angle", "strength_settings_advanced#bridge-infill-direction");
|
||||
optgroup->append_single_option_line("minimum_sparse_infill_area", "strength_settings_advanced#minimum-sparse-infill-threshold");
|
||||
optgroup->append_single_option_line("infill_combination", "strength_settings_advanced#infill-combination");
|
||||
optgroup->append_single_option_line("infill_combination_max_layer_height", "strength_settings_advanced#max-layer-height");
|
||||
|
||||
Reference in New Issue
Block a user