mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-03 17:13:22 +03:00
Compare commits
4 Commits
feature/au
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
757b6a5c46 | ||
|
|
6f011c9f6a | ||
|
|
ae16c76dd2 | ||
|
|
065540e48f |
@@ -266,10 +266,44 @@ std::vector<WaveSeed> wave_seeds(
|
||||
//(front.z() < 0 && back.z() < 0));
|
||||
// Hope that at least one end of an open polyline is clipped by the boundary, thus an intersection point is created.
|
||||
(front.z() < 0 || back.z() < 0));
|
||||
// However, with complex geometry, both endpoints may coincide with existing polygon
|
||||
// vertices (z >= 0), which is handled below.
|
||||
|
||||
if (front != back && front.z() >= 0 && back.z() >= 0) {
|
||||
// Very rare case when both endpoints intersect boundary ExPolygons in existing points.
|
||||
// So the ZFillFunction callback hasn't been called.
|
||||
// Both endpoints coincide with existing polygon vertices, so the
|
||||
// ZFillFunction callback was never called. With complex geometry
|
||||
// this is common because source and boundary contours share many
|
||||
// vertices. Determine src_id / boundary_id from Z coordinates
|
||||
// (and fall back to an AABB-tree point-in-polygon test when a
|
||||
// boundary ID is not directly available).
|
||||
coord_t src_z = -1, boundary_z = -1;
|
||||
// Scan all path points for the information we need.
|
||||
for (const ClipperLib_Z::IntPoint &point : path) {
|
||||
if (point.z() >= idx_boundary_end && point.z() < idx_src_end && src_z < 0)
|
||||
src_z = point.z();
|
||||
else if (point.z() >= idx_boundary_begin && point.z() < idx_boundary_end && boundary_z < 0)
|
||||
boundary_z = point.z();
|
||||
if (src_z >= 0 && boundary_z >= 0)
|
||||
break;
|
||||
}
|
||||
if (src_z >= 0) {
|
||||
uint32_t src_id = uint32_t(src_z - idx_boundary_end);
|
||||
if (boundary_z >= 0) {
|
||||
out.push_back({ src_id, uint32_t(boundary_z - 1), ClipperZUtils::from_zpath(path) });
|
||||
} else {
|
||||
// Source ID known but boundary unknown – use AABB tree.
|
||||
if (aabb_tree.empty())
|
||||
aabb_tree = build_aabb_tree_over_expolygons(boundary);
|
||||
int boundary_id = sample_in_expolygons(aabb_tree, boundary, Point(front.x(), front.y()));
|
||||
if (boundary_id >= 0)
|
||||
out.push_back({ src_id, uint32_t(boundary_id), ClipperZUtils::from_zpath(path) });
|
||||
}
|
||||
++ iseed;
|
||||
continue;
|
||||
}
|
||||
// Unable to determine source ID – drop the segment.
|
||||
continue;
|
||||
} else
|
||||
if (front == back && (front.z() < idx_boundary_end)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -49,6 +49,8 @@ static inline FlowRole opt_key_to_flow_role(const std::string &opt_key)
|
||||
return frInfill;
|
||||
else if (opt_key == "internal_solid_infill_line_width")
|
||||
return frSolidInfill;
|
||||
else if (opt_key == "bridge_line_width")
|
||||
return frSolidInfill;
|
||||
else if (opt_key == "top_surface_line_width")
|
||||
return frTopSolidInfill;
|
||||
else if (opt_key == "support_line_width")
|
||||
@@ -67,6 +69,26 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
|
||||
{
|
||||
assert(opt != nullptr);
|
||||
|
||||
auto opt_nozzle_diameters = config.option<ConfigOptionFloats>("nozzle_diameter");
|
||||
if (opt_nozzle_diameters == nullptr)
|
||||
throw_on_missing_variable(opt_key, "nozzle_diameter");
|
||||
const float nozzle_diameter = float(opt_nozzle_diameters->get_at(first_printing_extruder));
|
||||
|
||||
if (opt_key == "bridge_line_width") {
|
||||
if (opt->percent) {
|
||||
const double bridge_width = opt->get_abs_value(nozzle_diameter);
|
||||
if (bridge_width > 0.)
|
||||
return bridge_width;
|
||||
} else if (opt->value > 0.) {
|
||||
return opt->value;
|
||||
}
|
||||
|
||||
opt = config.option<ConfigOptionFloatOrPercent>("internal_solid_infill_line_width");
|
||||
if (opt == nullptr)
|
||||
throw_on_missing_variable(opt_key, "internal_solid_infill_line_width");
|
||||
return extrusion_width("internal_solid_infill_line_width", opt, config, first_printing_extruder);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This is the logic used for skit / brim, but not for the rest of the 1st layer.
|
||||
if (opt->value == 0. && first_layer) {
|
||||
@@ -84,17 +106,13 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionFloat
|
||||
throw_on_missing_variable(opt_key, "line_width");
|
||||
}
|
||||
|
||||
auto opt_nozzle_diameters = config.option<ConfigOptionFloats>("nozzle_diameter");
|
||||
if (opt_nozzle_diameters == nullptr)
|
||||
throw_on_missing_variable(opt_key, "nozzle_diameter");
|
||||
|
||||
if (opt->percent) {
|
||||
return opt->get_abs_value(float(opt_nozzle_diameters->get_at(first_printing_extruder)));
|
||||
return opt->get_abs_value(nozzle_diameter);
|
||||
}
|
||||
|
||||
if (opt->value == 0.) {
|
||||
// If user left option to 0, calculate a sane default width.
|
||||
return auto_extrusion_width(opt_key_to_flow_role(opt_key), float(opt_nozzle_diameters->get_at(first_printing_extruder)));
|
||||
return auto_extrusion_width(opt_key_to_flow_role(opt_key), nozzle_diameter);
|
||||
}
|
||||
|
||||
return opt->value;
|
||||
|
||||
@@ -34,16 +34,26 @@ Flow LayerRegion::bridging_flow(FlowRole role, bool thick_bridge) const
|
||||
const PrintRegionConfig ®ion_config = region.config();
|
||||
const PrintObject &print_object = *this->layer()->object();
|
||||
Flow bridge_flow;
|
||||
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will fall back to zero'th element, so everything is all right.
|
||||
auto nozzle_diameter = float(print_object.print()->config().nozzle_diameter.get_at(region.extruder(role) - 1));
|
||||
const ConfigOptionFloatOrPercent& bridge_width_opt = region_config.bridge_line_width;
|
||||
const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter);
|
||||
const bool has_bridge_width = bridge_width > 0.;
|
||||
const double bridge_flow_ratio = region_config.bridge_flow;
|
||||
|
||||
if (thick_bridge) {
|
||||
// The old Slic3r way (different from all other slicers): Use rounded extrusions.
|
||||
// Get the configured nozzle_diameter for the extruder associated to the flow role requested.
|
||||
// Here this->extruder(role) - 1 may underflow to MAX_INT, but then the get_at() will follback to zero'th element, so everything is all right.
|
||||
// Applies default bridge spacing.
|
||||
bridge_flow = Flow::bridging_flow(float(sqrt(region_config.bridge_flow)) * nozzle_diameter, nozzle_diameter);
|
||||
float thread_diameter = has_bridge_width ? float(bridge_width) : nozzle_diameter;
|
||||
if (bridge_flow_ratio > 0.)
|
||||
thread_diameter *= float(sqrt(bridge_flow_ratio));
|
||||
bridge_flow = Flow::bridging_flow(thread_diameter, nozzle_diameter);
|
||||
} else {
|
||||
// The same way as other slicers: Use normal extrusions. Apply bridge_flow while maintaining the original spacing.
|
||||
bridge_flow = this->flow(role).with_flow_ratio(region_config.bridge_flow);
|
||||
Flow base_flow = this->flow(role);
|
||||
if (has_bridge_width)
|
||||
base_flow = Flow(float(bridge_width), base_flow.height(), nozzle_diameter);
|
||||
bridge_flow = base_flow.with_flow_ratio(bridge_flow_ratio);
|
||||
}
|
||||
return bridge_flow;
|
||||
|
||||
@@ -83,6 +93,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 +110,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 +534,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 +816,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;
|
||||
|
||||
@@ -1097,6 +1097,7 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"infill_wall_overlap",
|
||||
"top_bottom_infill_wall_overlap",
|
||||
"bridge_flow",
|
||||
"bridge_line_width",
|
||||
"internal_bridge_flow",
|
||||
"elefant_foot_compensation",
|
||||
"elefant_foot_compensation_layers",
|
||||
@@ -1161,6 +1162,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",
|
||||
|
||||
@@ -1540,12 +1540,12 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
auto validate_extrusion_width = [min_nozzle_diameter, max_nozzle_diameter](const ConfigBase &config, const char *opt_key, double layer_height, std::string &err_msg) -> bool {
|
||||
double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
|
||||
double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
|
||||
if (extrusion_width_min == 0) {
|
||||
// Default "auto-generated" extrusion width is always valid.
|
||||
} else if (extrusion_width_min <= layer_height) {
|
||||
err_msg = L("Too small line width");
|
||||
return false;
|
||||
} else if (extrusion_width_max > max_nozzle_diameter * MAX_LINE_WIDTH_MULTIPLIER) {
|
||||
if (extrusion_width_min == 0) {
|
||||
// Default "auto-generated" extrusion width is always valid.
|
||||
} else if (extrusion_width_min <= layer_height) {
|
||||
err_msg = L("Too small line width");
|
||||
return false;
|
||||
} else if (extrusion_width_max > max_nozzle_diameter * MAX_LINE_WIDTH_MULTIPLIER) {
|
||||
err_msg = L("Too large line width");
|
||||
return false;
|
||||
}
|
||||
@@ -1667,6 +1667,25 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
for (const PrintRegion ®ion : object->all_regions())
|
||||
if (!validate_extrusion_width(region.config(), opt_key, layer_height, err_msg))
|
||||
return {err_msg, object, opt_key};
|
||||
|
||||
const bool allow_thin_bridge_width = object->config().thick_bridges && object->config().thick_internal_bridges;
|
||||
for (const PrintRegion ®ion : object->all_regions()) {
|
||||
const auto &bridge_width_opt = region.config().bridge_line_width;
|
||||
for (FlowRole bridge_role : { frPerimeter, frInfill, frSolidInfill, frTopSolidInfill }) {
|
||||
const double nozzle_diameter = m_config.nozzle_diameter.get_at(region.extruder(bridge_role) - 1);
|
||||
const double bridge_width = bridge_width_opt.get_abs_value(nozzle_diameter);
|
||||
if (bridge_width <= 0.)
|
||||
continue;
|
||||
if (bridge_width > nozzle_diameter) {
|
||||
err_msg = L("Bridge line width must not exceed nozzle diameter");
|
||||
return { err_msg, object, "bridge_line_width" };
|
||||
}
|
||||
if (!allow_thin_bridge_width && bridge_width <= layer_height) {
|
||||
err_msg = L("Too small line width");
|
||||
return { err_msg, object, "bridge_line_width" };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,58 +1258,97 @@ 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.");
|
||||
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");
|
||||
def->tooltip = L("Controls the density (spacing) of external bridge lines. Default is 100%.\n\n"
|
||||
"Lower density external bridges can help improve reliability as there is more space for air to circulate "
|
||||
"around the extruded bridge, improving its cooling speed. Minimum is 10%.\n\n"
|
||||
"Higher densities can produce smoother bridge surfaces, as overlapping lines provide "
|
||||
"additional support during printing. Maximum is 120%.\n"
|
||||
"Note: Bridge density that is too high can cause warping or overextrusion.");
|
||||
def->tooltip = L("Controls the density (spacing) of external bridge lines. Default is 100%.\n"
|
||||
"Theoretically, 100% means a solid bridge, but due to the tendency of bridge extrusions to sag, 100% may not be sufficient.\n\n"
|
||||
"- Higher than 100% density (Recommended Max 125%):\n"
|
||||
" - Pros: Produces smoother bridge surfaces, as overlapping lines provide additional support during printing.\n"
|
||||
" - Cons: Can cause overextrusion, which may reduce lower and upper surface quality and increase the risk of warping.\n\n"
|
||||
"- Lower than 100% density (Min 10%):\n"
|
||||
" - Pros: Can create a string-like first layer. Faster and with better cooling because there is more space for air to circulate around the extruded bridge.\n"
|
||||
" - Cons: May lead to sagging and poorer surface finish.\n\n"
|
||||
"Recommended range: Minimum 10% - Maximum 125%.");
|
||||
def->sidetext = "%";
|
||||
def->min = 10;
|
||||
def->max = 120;
|
||||
def->max = 125;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionPercent(100));
|
||||
|
||||
def = this->add("internal_bridge_density", coPercent);
|
||||
def->label = L("Internal bridge density");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Controls the density (spacing) of internal bridge lines. 100% means solid bridge. Default is 100%.\n\n"
|
||||
"Lower density internal bridges can help reduce top surface pillowing and improve internal bridge reliability as there is more space for "
|
||||
"air to circulate around the extruded bridge, improving its cooling speed.\n\n"
|
||||
"This option works particularly well when combined with the second internal bridge over infill option, "
|
||||
"further improving internal bridging structure before solid infill is extruded.");
|
||||
def->tooltip = L("Controls the density (spacing) of internal bridge lines. Default is 100%. 100% means a solid internal bridge.\n\n"
|
||||
"Internal bridges act as intermediate support between sparse infill and top solid infill and can strongly affect top surface quality.\n\n"
|
||||
"- Higher than 100% density (Recommended Max 125%):\n"
|
||||
" - Pros: Improves internal bridge strength and support under top layers, reducing sagging and improving top-surface finish.\n"
|
||||
" - Cons: Increases material use and print time; excessive density may cause overextrusion and internal stresses.\n\n"
|
||||
"- Lower than 100% density (Min 10%):\n"
|
||||
" - Pros: Can reduce pillowing and improve cooling (more airflow through the bridge), and may speed up printing.\n"
|
||||
" - Cons: May reduce internal support, increasing the risk of sagging and top surface defects.\n\n"
|
||||
"This option works particularly well when combined with the second internal bridge over infill option to improve bridging further before solid infill is extruded.");
|
||||
def->sidetext = "%";
|
||||
def->min = 10;
|
||||
def->max = 100;
|
||||
def->max = 125;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionPercent(100));
|
||||
|
||||
def = this->add("bridge_flow", coFloat);
|
||||
def->label = L("Bridge flow ratio");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("Decrease this value slightly (for example 0.9) to reduce the amount of material for bridge, to improve sag.\n\n"
|
||||
def->tooltip = L("This value governs the thickness of the external (visible) bridge layer.\n"
|
||||
"Values above 1.0: Increase the amount of material while maintaining line spacing. This can improve line contact and strength.\n"
|
||||
"Values below 1.0: Reduce the amount of material while adjusting line spacing to maintain contact. This can improve sagging.\n\n"
|
||||
"The actual bridge flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio.");
|
||||
def->min = 0;
|
||||
def->max = 2.0;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloat(1));
|
||||
|
||||
def = this->add("bridge_line_width", coFloatOrPercent);
|
||||
def->label = L("Bridge");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("Bridge line width is expressed either as an absolute value or as a percentage of the active nozzle diameter (percentages are computed from the nozzle diameter).\n"
|
||||
"Recommended to use with a higher Bridge density or Bridge flow ratio.\n\n"
|
||||
"The maximum value is 100% or the nozzle diameter.\n"
|
||||
"If set to 0, the line width will match the Internal solid infill width.");
|
||||
def->sidetext = L("mm or %");
|
||||
def->ratio_over = "nozzle_diameter";
|
||||
def->min = 0;
|
||||
def->max = 100;
|
||||
def->max_literal = 10;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionFloatOrPercent(100., true));
|
||||
|
||||
def = this->add("internal_bridge_flow", coFloat);
|
||||
def->label = L("Internal bridge flow ratio");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("This value governs the thickness of the internal bridge layer. This is the first layer over sparse infill. Decrease this value slightly (for example 0.9) to improve surface quality over sparse infill."
|
||||
"\n\nThe actual internal bridge flow used is calculated by multiplying this value with the bridge flow ratio, the filament flow ratio, and if set, the object's flow ratio.");
|
||||
def->tooltip = L("This value governs the thickness of the internal bridge layer. This is the first layer over sparse infill so increasing it may increase strength and upper layer quality.\n"
|
||||
"Values above 1.0: Increase the amount of material while maintaining line spacing. This can improve line contact and strength.\n"
|
||||
"Values below 1.0: Reduce the amount of material while adjusting line spacing to maintain contact. This can improve sagging.\n\n"
|
||||
"The actual bridge flow used is calculated by multiplying this value with the filament flow ratio, and if set, the object's flow ratio.");
|
||||
def->min = 0;
|
||||
def->max = 2.0;
|
||||
def->mode = comAdvanced;
|
||||
@@ -1888,16 +1932,18 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("thick_bridges", coBool);
|
||||
def->label = L("Thick external bridges");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("If enabled, bridges are more reliable, can bridge longer distances, but may look worse. "
|
||||
"If disabled, bridges look better but are reliable just for shorter bridged distances.");
|
||||
def->tooltip = L("If enabled, bridge extrusion uses a line height equal to the nozzle diameter.\n"
|
||||
"This increases bridge strength and reliability, allowing longer spans, but may worsen appearance.\n"
|
||||
"If disabled, bridges may look better but are generally reliable only for shorter spans.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("thick_internal_bridges", coBool);
|
||||
def->label = L("Thick internal bridges");
|
||||
def->category = L("Quality");
|
||||
def->tooltip = L("If enabled, thick internal bridges will be used. It's usually recommended to have this feature turned on. However, "
|
||||
"consider turning it off if you are using large nozzles.");
|
||||
def->tooltip = L("If enabled, internal bridge extrusion uses a line height equal to the nozzle diameter.\n"
|
||||
"This increases internal bridge strength and reliability when printed over sparse infill, but may worsen appearance.\n"
|
||||
"If disabled, internal bridges may look better but can be less reliable over sparse infill.");
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
@@ -2924,7 +2970,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));
|
||||
|
||||
@@ -10249,8 +10296,8 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
|
||||
error_message.emplace("bridge_flow", L("invalid value ") + std::to_string(cfg.bridge_flow));
|
||||
}
|
||||
|
||||
// --bridge-flow-ratio
|
||||
if (cfg.bridge_flow <= 0) {
|
||||
// --internal-bridge-flow-ratio
|
||||
if (cfg.internal_bridge_flow <= 0) {
|
||||
error_message.emplace("internal_bridge_flow", L("invalid value ") + std::to_string(cfg.internal_bridge_flow));
|
||||
}
|
||||
|
||||
@@ -10308,13 +10355,18 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
|
||||
// extrusion widths
|
||||
{
|
||||
double max_nozzle_diameter = 0.;
|
||||
double min_nozzle_diameter = std::numeric_limits<double>::max();
|
||||
for (double dmr : cfg.nozzle_diameter.values)
|
||||
{
|
||||
max_nozzle_diameter = std::max(max_nozzle_diameter, dmr);
|
||||
min_nozzle_diameter = std::min(min_nozzle_diameter, dmr);
|
||||
}
|
||||
const char *widths[] = {
|
||||
"outer_wall_line_width",
|
||||
"inner_wall_line_width",
|
||||
"sparse_infill_line_width",
|
||||
"internal_solid_infill_line_width",
|
||||
"bridge_line_width",
|
||||
"top_surface_line_width",
|
||||
"support_line_width",
|
||||
"initial_layer_line_width",
|
||||
@@ -10322,8 +10374,13 @@ std::map<std::string, std::string> validate(const FullPrintConfig &cfg, bool und
|
||||
"skeleton_infill_line_width"};
|
||||
for (size_t i = 0; i < sizeof(widths) / sizeof(widths[i]); ++ i) {
|
||||
std::string key(widths[i]);
|
||||
if (cfg.get_abs_value(key, max_nozzle_diameter) > MAX_LINE_WIDTH_MULTIPLIER * max_nozzle_diameter) {
|
||||
error_message.emplace(key, L("too large line width ") + std::to_string(cfg.get_abs_value(key)));
|
||||
double abs_width = cfg.get_abs_value(key, max_nozzle_diameter);
|
||||
double allowed_max = (key == "bridge_line_width") ? min_nozzle_diameter : MAX_LINE_WIDTH_MULTIPLIER * max_nozzle_diameter;
|
||||
if (abs_width > allowed_max) {
|
||||
if (key == "bridge_line_width")
|
||||
error_message.emplace(key, L("Bridge line width must not exceed nozzle diameter: ") + std::to_string(abs_width));
|
||||
else
|
||||
error_message.emplace(key, L("too large line width ") + std::to_string(abs_width));
|
||||
//return std::string("Too Large line width: ") + key;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1080,7 +1080,9 @@ 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))
|
||||
((ConfigOptionFloatOrPercent, bridge_line_width))
|
||||
((ConfigOptionFloat, internal_bridge_flow))
|
||||
((ConfigOptionFloat, bridge_speed))
|
||||
((ConfigOptionFloatOrPercent, internal_bridge_speed))
|
||||
|
||||
@@ -1275,7 +1275,9 @@ 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_line_width"
|
||||
|| opt_key == "bridge_density"
|
||||
|| opt_key == "internal_bridge_density") {
|
||||
steps.emplace_back(posPrepareInfill);
|
||||
@@ -3217,8 +3219,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);
|
||||
|
||||
@@ -2321,13 +2321,14 @@ void TabPrint::build()
|
||||
|
||||
optgroup = page->new_optgroup(L("Line width"), L"param_line_width");
|
||||
optgroup->append_single_option_line("line_width","quality_settings_line_width");
|
||||
optgroup->append_single_option_line("initial_layer_line_width","quality_settings_line_width");
|
||||
optgroup->append_single_option_line("outer_wall_line_width","quality_settings_line_width");
|
||||
optgroup->append_single_option_line("inner_wall_line_width","quality_settings_line_width");
|
||||
optgroup->append_single_option_line("top_surface_line_width","quality_settings_line_width");
|
||||
optgroup->append_single_option_line("sparse_infill_line_width","quality_settings_line_width");
|
||||
optgroup->append_single_option_line("internal_solid_infill_line_width","quality_settings_line_width");
|
||||
optgroup->append_single_option_line("support_line_width","quality_settings_line_width");
|
||||
optgroup->append_single_option_line("initial_layer_line_width","quality_settings_line_width#first-layer");
|
||||
optgroup->append_single_option_line("outer_wall_line_width","quality_settings_line_width#outer-wall");
|
||||
optgroup->append_single_option_line("inner_wall_line_width","quality_settings_line_width#inner-wall");
|
||||
optgroup->append_single_option_line("top_surface_line_width","quality_settings_line_width#top-surface");
|
||||
optgroup->append_single_option_line("sparse_infill_line_width","quality_settings_line_width#sparse-infill");
|
||||
optgroup->append_single_option_line("internal_solid_infill_line_width","quality_settings_line_width#internal-solid-infill");
|
||||
optgroup->append_single_option_line("support_line_width","quality_settings_line_width#support");
|
||||
optgroup->append_single_option_line("bridge_line_width","quality_settings_line_width#bridge");
|
||||
|
||||
optgroup = page->new_optgroup(L("Seam"), L"param_seam");
|
||||
optgroup->append_single_option_line("seam_position", "quality_settings_seam#seam-position");
|
||||
@@ -2427,7 +2428,7 @@ void TabPrint::build()
|
||||
|
||||
optgroup = page->new_optgroup(L("Bridging"), L"param_bridge");
|
||||
optgroup->append_single_option_line("bridge_flow", "quality_settings_bridging#flow-ratio");
|
||||
optgroup->append_single_option_line("internal_bridge_flow", "quality_settings_bridging#flow-ratio");
|
||||
optgroup->append_single_option_line("internal_bridge_flow", "quality_settings_bridging#flow-ratio");
|
||||
optgroup->append_single_option_line("bridge_density", "quality_settings_bridging#bridge-density");
|
||||
optgroup->append_single_option_line("internal_bridge_density", "quality_settings_bridging#bridge-density");
|
||||
optgroup->append_single_option_line("thick_bridges", "quality_settings_bridging#thick-bridges");
|
||||
@@ -2499,6 +2500,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#relative-bridge-angle");
|
||||
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