mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-15 23:12:08 +00:00
Merge branch 'main' into pr/Noisyfox/13712
This commit is contained in:
@@ -23,7 +23,9 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <unordered_set>
|
||||
#include <sstream>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
@@ -237,7 +239,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"bed_temperature_formula",
|
||||
"filament_notes",
|
||||
"process_notes",
|
||||
"printer_notes"
|
||||
"printer_notes",
|
||||
"use_3mf"
|
||||
};
|
||||
|
||||
static std::unordered_set<std::string> steps_ignore;
|
||||
@@ -669,13 +672,13 @@ StringObjectException Print::sequential_print_clearance_valid(const Print &print
|
||||
//juedge the exclude area
|
||||
if (!intersection(exclude_polys, convex_hull_no_offset).empty()) {
|
||||
if (single_object_exception.string.empty()) {
|
||||
single_object_exception.string = (boost::format(L("%1% is too close to exclusion area, there may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
|
||||
single_object_exception.string = (boost::format(L("%1% is too close to exclusion area. There may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
|
||||
// single_object_exception.object = instance.model_instance->get_object();
|
||||
//ORCA: Pass ModelInstance instead of ModelObject
|
||||
single_object_exception.object = instance.model_instance;
|
||||
}
|
||||
else {
|
||||
single_object_exception.string += "\n"+(boost::format(L("%1% is too close to exclusion area, there may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
|
||||
single_object_exception.string += "\n"+(boost::format(L("%1% is too close to exclusion area. There may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
|
||||
single_object_exception.object = nullptr;
|
||||
}
|
||||
//if (polygons) {
|
||||
@@ -1038,7 +1041,7 @@ static StringObjectException layered_print_cleareance_valid(const Print &print,
|
||||
/*if (warning) {
|
||||
warning->string += L("Prime Tower is too close to exclusion area, there may be collisions when printing.\n");
|
||||
}*/
|
||||
return {L("Prime Tower") + L(" is too close to exclusion area, and collisions will be caused.\n")};
|
||||
return {L("Prime Tower") + L(" is too close to an exclusion area, and collisions will be caused.\n")};
|
||||
}
|
||||
if (print_config.enable_wrapping_detection.value && !intersection({wrapping_poly}, convex_hulls_temp).empty()) {
|
||||
return {L("Prime Tower") + L(" is too close to clumping detection area, and collisions will be caused.\n")};
|
||||
@@ -1254,14 +1257,23 @@ StringObjectException Print::check_multi_filament_valid(const Print& print)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Orca: this g92e0 regex is used copied from PrusaSlicer
|
||||
// Matches "G92 E0" with various forms of writing the zero and with an optional comment.
|
||||
boost::regex regex_g92e0 { "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$" };
|
||||
|
||||
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||||
//BBS: refine seq-print validation logic.....FIXME:StringObjectException *warning can only contain one warning, but there might be many warnings, need a vector<StringObjectException>
|
||||
StringObjectException Print::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons) const
|
||||
//BBS: refine seq-print validation logic
|
||||
StringObjectException Print::validate(std::vector<StringObjectException> *warnings, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons) const
|
||||
{
|
||||
auto add_warning = [warnings](StringObjectException w) {
|
||||
w.is_warning = true;
|
||||
if (warnings != nullptr)
|
||||
warnings->push_back(std::move(w));
|
||||
};
|
||||
auto warn = [&](std::string msg, std::string opt_key = "", const ObjectBase* object = nullptr) {
|
||||
StringObjectException w;
|
||||
w.string = std::move(msg);
|
||||
w.opt_key = std::move(opt_key);
|
||||
w.object = object;
|
||||
add_warning(std::move(w));
|
||||
};
|
||||
|
||||
std::vector<unsigned int> extruders = this->extruders();
|
||||
unsigned int nozzles = m_config.nozzle_diameter.size();
|
||||
|
||||
@@ -1276,9 +1288,8 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
if (!ret.string.empty())
|
||||
{
|
||||
ret.type = STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP;
|
||||
if (ret.is_warning && warning != nullptr) {
|
||||
*warning = ret;
|
||||
//return {};
|
||||
if (ret.is_warning) {
|
||||
add_warning(ret);
|
||||
}else
|
||||
return ret;
|
||||
}
|
||||
@@ -1304,32 +1315,26 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
}
|
||||
else {
|
||||
//BBS
|
||||
auto ret = layered_print_cleareance_valid(*this, warning);
|
||||
StringObjectException layer_warning;
|
||||
auto ret = layered_print_cleareance_valid(*this, &layer_warning);
|
||||
if (!ret.string.empty()) {
|
||||
ret.type = STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT;
|
||||
return ret;
|
||||
}
|
||||
if (!layer_warning.string.empty())
|
||||
add_warning(layer_warning);
|
||||
}
|
||||
|
||||
if (m_config.enable_prime_tower) {
|
||||
for (const PrintObject* object : m_objects) {
|
||||
if (object->config().precise_z_height.value && warning != nullptr) {
|
||||
StringObjectException warningtemp;
|
||||
warningtemp.string = L("Enabling both precise Z height and the prime tower may cause slicing errors.");
|
||||
warningtemp.opt_key = "precise_z_height";
|
||||
warningtemp.is_warning = true;
|
||||
*warning = warningtemp;
|
||||
if (object->config().precise_z_height.value) {
|
||||
warn(L("Enabling both precise Z height and the prime tower may cause slicing errors."), "precise_z_height");
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (m_config.enable_wrapping_detection && warning!=nullptr) {
|
||||
StringObjectException warningtemp;
|
||||
warningtemp.string = L("A prime tower is required for clumping detection; otherwise, there may be flaws on the model.");
|
||||
warningtemp.opt_key = "enable_prime_tower";
|
||||
warningtemp.is_warning = true;
|
||||
*warning = warningtemp;
|
||||
}
|
||||
if (m_config.enable_wrapping_detection)
|
||||
warn(L("A prime tower is required for clumping detection; otherwise, there may be flaws on the model."), "enable_prime_tower");
|
||||
}
|
||||
|
||||
if (m_config.spiral_mode) {
|
||||
@@ -1346,7 +1351,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
if (std::any_of(all_regions.begin() + 1, all_regions.end(), [this, ra = all_regions.front()](const auto rb) {
|
||||
return !Layer::is_perimeter_compatible(*this, ra, rb);
|
||||
})) {
|
||||
return {L("The spiral vase mode does not work when an object contains more than one materials."), nullptr, "spiral_mode"};
|
||||
return {L("Spiral (vase) mode does not work when an object contains more than one material."), nullptr, "spiral_mode"};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1425,8 +1430,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|
||||
|| std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1) {
|
||||
// return { L("Different nozzle diameters and different filament diameters may not work well when prime tower is enabled. It's very experimental, please proceed with caucious.") };
|
||||
warning->string = L("Different nozzle diameters and different filament diameters may not work well when the prime tower is enabled. It's very experimental, so please proceed with caution.");
|
||||
warning->opt_key = "nozzle_diameter";
|
||||
warn(L("Different nozzle diameters and different filament diameters may not work well when the prime tower is enabled. It's very experimental, so please proceed with caution."), "nozzle_diameter");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1443,14 +1447,14 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
return { L("The prime tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors.")};
|
||||
|
||||
if ((m_config.print_sequence == PrintSequence::ByObject) && extruders.size() > 1)
|
||||
return { L("The prime tower is not supported in \"By object\" print."), nullptr, "enable_prime_tower" };
|
||||
return { L("A prime tower is not supported in \u201cBy object\u201d print."), nullptr, "enable_prime_tower" };
|
||||
|
||||
// BBS: When prime tower is on, object layer and support layer must be aligned. So support gap should be multiple of object layer height.
|
||||
for (size_t i = 0; i < m_objects.size(); i++) {
|
||||
const PrintObject* object = m_objects[i];
|
||||
const SlicingParameters& slicing_params = object->slicing_parameters();
|
||||
if (object->config().adaptive_layer_height) {
|
||||
return { L("The prime tower is not supported when adaptive layer height is on. It requires that all objects have the same layer height."), object, "adaptive_layer_height" };
|
||||
return { L("A prime tower is not supported when adaptive layer height is on. It requires that all objects have the same layer height."), object, "adaptive_layer_height" };
|
||||
}
|
||||
|
||||
if (!object->config().enable_support)
|
||||
@@ -1458,7 +1462,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
|
||||
double gap_layers = slicing_params.gap_object_support / slicing_params.layer_height;
|
||||
if (gap_layers - (int)gap_layers > EPSILON) {
|
||||
return {L("The prime tower requires \"support gap\" to be multiple of layer height."), object};
|
||||
return {L("A prime tower requires any \u201csupport gap\u201d to be a multiple of layer height."), object};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1471,9 +1475,9 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
const SlicingParameters &slicing_params = object->slicing_parameters();
|
||||
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
||||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
|
||||
return {L("The prime tower requires that all objects have the same layer heights."), object, "initial_layer_print_height"};
|
||||
return {L("A prime tower requires that all objects have the same layer height."), object, "initial_layer_print_height"};
|
||||
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||||
return {L("The prime tower requires that all objects are printed over the same number of raft layers."), object, "raft_layers"};
|
||||
return {L("A prime tower requires that all objects are printed over the same number of raft layers."), object, "raft_layers"};
|
||||
// BBS: support gap can be multiple of object layer height, remove _L()
|
||||
#if 0
|
||||
if (slicing_params0.gap_object_support != slicing_params.gap_object_support ||
|
||||
@@ -1481,7 +1485,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
return {L("The prime tower is only supported for multiple objects if they are printed with the same support_top_z_distance."), object};
|
||||
#endif
|
||||
if (!equal_layering(slicing_params, slicing_params0))
|
||||
return { L("The prime tower requires that all objects are sliced with the same layer heights."), object };
|
||||
return { L("A prime tower requires that all objects are sliced with the same layer height."), object };
|
||||
if (has_custom_layering) {
|
||||
auto &lh = layer_height_profile(i);
|
||||
auto &lh_tallest = layer_height_profile(tallest_object_idx);
|
||||
@@ -1544,13 +1548,13 @@ 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) {
|
||||
err_msg = L("Too large line width");
|
||||
if (extrusion_width_min == 0) {
|
||||
// Default "auto-generated" extrusion width is always valid.
|
||||
} else if (extrusion_width_min <= layer_height) {
|
||||
err_msg = L("Line width too small");
|
||||
return false;
|
||||
} else if (extrusion_width_max > max_nozzle_diameter * MAX_LINE_WIDTH_MULTIPLIER) {
|
||||
err_msg = L("Line width too large");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -1572,7 +1576,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
// BBS
|
||||
#if 0
|
||||
if (this->has_wipe_tower() && object->config().independent_support_layer_height) {
|
||||
return {L("The prime tower requires that support has the same layer height with object."), object, "support_filament"};
|
||||
return {L("A prime tower requires that support has the same layer height as the object."), object, "support_filament"};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1583,22 +1587,15 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
// Orca: use organic as default
|
||||
object->config().support_style == smsDefault) {
|
||||
|
||||
if (warning) {
|
||||
// Orca: check the support wall count and the base pattern
|
||||
if (object->config().tree_support_wall_count > 1 &&
|
||||
object->config().support_base_pattern != SupportMaterialPattern::smpNone &&
|
||||
object->config().support_base_pattern != SupportMaterialPattern::smpDefault) {
|
||||
warning->string = L("For Organic supports, two walls are supported only with the Hollow/Default base pattern.");
|
||||
warning->opt_key = "support_base_pattern";
|
||||
}
|
||||
// Orca: check the support wall count and the base pattern
|
||||
if (object->config().tree_support_wall_count > 1 &&
|
||||
object->config().support_base_pattern != SupportMaterialPattern::smpNone &&
|
||||
object->config().support_base_pattern != SupportMaterialPattern::smpDefault)
|
||||
warn(L("For Organic supports, two walls are supported only with the Hollow/Default base pattern."), "support_base_pattern");
|
||||
|
||||
// Orca: check if the Lightning base pattern selected
|
||||
if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning) {
|
||||
warning->string = L(
|
||||
"The Lightning base pattern is not supported by this support type; Rectilinear will be used instead.");
|
||||
warning->opt_key = "support_base_pattern";
|
||||
}
|
||||
}
|
||||
// Orca: check if the Lightning base pattern selected
|
||||
if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning)
|
||||
warn(L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
|
||||
|
||||
float extrusion_width = std::min(
|
||||
support_material_flow(object).width(),
|
||||
@@ -1610,28 +1607,23 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
if (object->config().tree_support_branch_diameter_organic < object->config().tree_support_tip_diameter)
|
||||
return { L("Organic support branch diameter must not be smaller than support tree tip diameter."), object, "tree_support_branch_diameter_organic" };
|
||||
}
|
||||
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning && warning) {
|
||||
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpLightning) {
|
||||
// Orca: check if the Lightning base pattern selected
|
||||
warning->string = L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead.");
|
||||
warning->opt_key = "support_base_pattern";
|
||||
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpNone && warning) {
|
||||
warn(L("The Lightning base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
|
||||
} else if (object->config().support_base_pattern == SupportMaterialPattern::smpNone) {
|
||||
// Orca: check if the Hollow base pattern selected
|
||||
warning->string = L("The Hollow base pattern is not supported by this support type; Rectilinear will be used instead.");
|
||||
warning->opt_key = "support_base_pattern";
|
||||
warn(L("The Hollow base pattern is not supported by this support type; Rectilinear will be used instead."), "support_base_pattern");
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have custom support data that would not be used?
|
||||
// Notify the user in that case.
|
||||
if (! object->has_support() && warning) {
|
||||
if (! object->has_support()) {
|
||||
for (const ModelVolume* mv : object->model_object()->volumes) {
|
||||
bool has_enforcers = mv->is_support_enforcer() ||
|
||||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||||
if (has_enforcers) {
|
||||
StringObjectException warningtemp;
|
||||
warningtemp.string = L("Support enforcers are used but support is not enabled. Please enable support.");
|
||||
warningtemp.object = object;
|
||||
*warning = warningtemp;
|
||||
warn(L("Support enforcers are used but support is not enabled. Please enable support."), "", object);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1671,30 +1663,83 @@ 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("Line width too small");
|
||||
return { err_msg, object, "bridge_line_width" };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: G92 E0 is not supported when using absolute extruder addressing
|
||||
// This check is copied from PrusaSlicer, the original author is Vojtech Bubnik
|
||||
if(!is_BBL_printer()) {
|
||||
bool before_layer_gcode_resets_extruder =
|
||||
boost::regex_search(m_config.before_layer_change_gcode.value, regex_g92e0);
|
||||
bool layer_gcode_resets_extruder = boost::regex_search(m_config.layer_change_gcode.value, regex_g92e0);
|
||||
if (m_config.use_relative_e_distances) {
|
||||
// See GH issues #6336 #5073
|
||||
if ((m_config.gcode_flavor == gcfMarlinLegacy || m_config.gcode_flavor == gcfMarlinFirmware) &&
|
||||
!before_layer_gcode_resets_extruder && !layer_gcode_resets_extruder)
|
||||
return {L("Relative extruder addressing requires resetting the extruder position at each layer to "
|
||||
"prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode."),
|
||||
nullptr, "before_layer_change_gcode"};
|
||||
} else if (before_layer_gcode_resets_extruder)
|
||||
return {L("\"G92 E0\" was found in before_layer_gcode, which is incompatible with absolute extruder "
|
||||
// This check is modified from PrusaSlicer, the original author is Vojtech Bubnik
|
||||
// Orca: case‑sensitive match for exactly "G92 E0" (uppercase G and E only)
|
||||
// because gcode is case sensitive and G92 e0 satisfies the regex but causes a slicing error
|
||||
// https://github.com/OrcaSlicer/OrcaSlicer/issues/13927
|
||||
|
||||
// Matches any case of "G92 E0" (original pattern)
|
||||
static const boost::regex regex_g92e0 {
|
||||
"^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$"
|
||||
};
|
||||
// Matches only the exact uppercase "G92 E0"
|
||||
static const boost::regex regex_g92e0_correct {
|
||||
"^[ \\t]*G92[ \\t]*E(0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$"
|
||||
};
|
||||
|
||||
const bool before_has_g92_any = boost::regex_search(
|
||||
m_config.before_layer_change_gcode.value, regex_g92e0);
|
||||
const bool layer_has_g92_any = boost::regex_search(
|
||||
m_config.layer_change_gcode.value, regex_g92e0);
|
||||
|
||||
if (m_config.use_relative_e_distances) {
|
||||
// Relative mode: "G92 E0" is required to reset extruder position.
|
||||
const bool before_has_g92_exact = boost::regex_search(
|
||||
m_config.before_layer_change_gcode.value, regex_g92e0_correct);
|
||||
const bool layer_has_g92_exact = boost::regex_search(
|
||||
m_config.layer_change_gcode.value, regex_g92e0_correct);
|
||||
|
||||
// Wrong case found?
|
||||
if (before_has_g92_any && !before_has_g92_exact)
|
||||
return {L("\"G92 E0\" was found in before_layer_change_gcode, but the G or E are not uppercase. "
|
||||
"Please change them to the exact uppercase \"G92 E0\"."),
|
||||
nullptr, "before_layer_change_gcode"};
|
||||
if (layer_has_g92_any && !layer_has_g92_exact)
|
||||
return {L("\"G92 E0\" was found in layer_change_gcode, but the G or E are not uppercase. "
|
||||
"Please change them to the exact uppercase \"G92 E0\"."),
|
||||
nullptr, "layer_change_gcode"};
|
||||
|
||||
// Only Marlin flavours need the reset; BBL printers do not.
|
||||
if ((m_config.gcode_flavor == gcfMarlinLegacy || m_config.gcode_flavor == gcfMarlinFirmware) &&
|
||||
!is_BBL_printer() &&
|
||||
!before_has_g92_exact && !layer_has_g92_exact)
|
||||
return {L("Relative extruder addressing requires resetting the extruder position at each layer to "
|
||||
"prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode."),
|
||||
nullptr, "before_layer_change_gcode"};
|
||||
} else {
|
||||
// Absolute mode: any occurrence of "G92 E0" is incompatible.
|
||||
if (before_has_g92_any)
|
||||
return {L("\"G92 E0\" was found in before_layer_change_gcode, which is incompatible with absolute extruder "
|
||||
"addressing."),
|
||||
nullptr, "before_layer_change_gcode"};
|
||||
else if (layer_gcode_resets_extruder)
|
||||
return {L("\"G92 E0\" was found in layer_gcode, which is incompatible with absolute extruder addressing."),
|
||||
if (layer_has_g92_any)
|
||||
return {L("\"G92 E0\" was found in layer_change_gcode, which is incompatible with absolute extruder "
|
||||
"addressing."),
|
||||
nullptr, "layer_change_gcode"};
|
||||
}
|
||||
}
|
||||
|
||||
const ConfigOptionDef* bed_type_def = print_config_def.get("curr_bed_type");
|
||||
assert(bed_type_def != nullptr);
|
||||
@@ -1730,7 +1775,11 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
}
|
||||
|
||||
// check if print speed/accel/jerk is higher than the maximum speed of the printer
|
||||
if (warning) {
|
||||
if (warnings) {
|
||||
// The motion-ability checks are mutually exclusive (gated on warning_key), so collect the
|
||||
// single one that fires into a local and push it once - separate from the precise-wall and
|
||||
// shrinkage warnings below.
|
||||
StringObjectException motion_warning;
|
||||
try {
|
||||
auto check_extruder = [&](const int extruder_id) {
|
||||
auto check_motion_ability_object_setting = [&](const std::vector<std::string>& keys_to_check, double limit) -> std::string {
|
||||
@@ -1762,7 +1811,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
if (!ignore_jerk_validation) {
|
||||
if (m_default_object_config.default_jerk.get_at(extruder_id) == 1 || m_default_object_config.outer_wall_jerk.get_at(extruder_id) == 1 ||
|
||||
m_default_object_config.inner_wall_jerk.get_at(extruder_id) == 1) {
|
||||
warning->string = L("Setting the jerk speed too low could lead to artifacts on curved surfaces");
|
||||
motion_warning.string = L("Setting the jerk speed too low could lead to artifacts on curved surfaces");
|
||||
if (m_default_object_config.outer_wall_jerk.get_at(extruder_id) == 1)
|
||||
warning_key = "outer_wall_jerk";
|
||||
else if (m_default_object_config.inner_wall_jerk.get_at(extruder_id) == 1)
|
||||
@@ -1770,7 +1819,7 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
else
|
||||
warning_key = "default_jerk";
|
||||
|
||||
warning->opt_key = warning_key;
|
||||
motion_warning.opt_key = warning_key;
|
||||
}
|
||||
|
||||
if (warning_key.empty() && m_default_object_config.default_jerk.get_at(extruder_id) > 0) {
|
||||
@@ -1780,11 +1829,11 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
warning_key.clear();
|
||||
warning_key = check_motion_ability_object_setting(jerk_to_check, max_jerk);
|
||||
if (!warning_key.empty()) {
|
||||
warning->string = L(
|
||||
motion_warning.string = L(
|
||||
"The jerk setting exceeds the printer's maximum jerk (machine_max_jerk_x/machine_max_jerk_y).\n"
|
||||
"Orca will automatically cap the jerk speed to ensure it doesn't surpass the printer's capabilities.\n"
|
||||
"You can adjust the maximum jerk setting in your printer's configuration to get higher speeds.");
|
||||
warning->opt_key = warning_key;
|
||||
motion_warning.opt_key = warning_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1793,10 +1842,10 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
// Orca: Only marlin FW supports max junction deviation. Dont display warning if firmware is not supporting it.
|
||||
const bool support_max_junction_deviation = ( m_config.gcode_flavor == gcfMarlinFirmware);
|
||||
if (warning_key.empty() && m_default_object_config.default_junction_deviation.get_at(extruder_id) > max_junction_deviation && support_max_junction_deviation) {
|
||||
warning->string = L( "Junction deviation setting exceeds the printer's maximum value (machine_max_junction_deviation).\n"
|
||||
motion_warning.string = L( "Junction deviation setting exceeds the printer's maximum value (machine_max_junction_deviation).\n"
|
||||
"Orca will automatically cap the junction deviation to ensure it doesn't surpass the printer's capabilities.\n"
|
||||
"You can adjust the machine_max_junction_deviation value in your printer's configuration to get higher limits.");
|
||||
warning->opt_key = warning_key;
|
||||
motion_warning.opt_key = "default_junction_deviation";
|
||||
}
|
||||
|
||||
// check acceleration
|
||||
@@ -1831,12 +1880,12 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
};
|
||||
warning_key = check_motion_ability_object_setting(accel_to_check, max_accel);
|
||||
if (!warning_key.empty()) {
|
||||
warning->string = L("The acceleration setting exceeds the printer's maximum acceleration "
|
||||
motion_warning.string = L("The acceleration setting exceeds the printer's maximum acceleration "
|
||||
"(machine_max_acceleration_extruding).\nOrca will "
|
||||
"automatically cap the acceleration speed to ensure it doesn't surpass the printer's "
|
||||
"capabilities.\nYou can adjust the "
|
||||
"machine_max_acceleration_extruding value in your printer's configuration to get higher speeds.");
|
||||
warning->opt_key = warning_key;
|
||||
motion_warning.opt_key = warning_key;
|
||||
}
|
||||
if (support_travel_acc) {
|
||||
const auto max_travel = m_config.machine_max_acceleration_travel.values[0];
|
||||
@@ -1846,13 +1895,13 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
};
|
||||
warning_key = check_motion_ability_object_setting(accel_to_check, max_travel);
|
||||
if (!warning_key.empty()) {
|
||||
warning->string = L(
|
||||
motion_warning.string = L(
|
||||
"The travel acceleration setting exceeds the printer's maximum travel acceleration "
|
||||
"(machine_max_acceleration_travel).\nOrca will "
|
||||
"automatically cap the travel acceleration speed to ensure it doesn't surpass the printer's "
|
||||
"capabilities.\nYou can adjust the "
|
||||
"machine_max_acceleration_travel value in your printer's configuration to get higher speeds.");
|
||||
warning->opt_key = warning_key;
|
||||
motion_warning.opt_key = warning_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1869,30 +1918,45 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
|
||||
// if (warning_key.empty() && m_config.travel_speed > max_speed)
|
||||
// warning_key = "travel_speed";
|
||||
// if (!warning_key.empty()) {
|
||||
// warning->string = L(
|
||||
// motion_warning.string = L(
|
||||
// "The speed setting exceeds the printer's maximum speed (machine_max_speed_x/machine_max_speed_y).\nOrca will "
|
||||
// "automatically cap the print speed to ensure it doesn't surpass the printer's capabilities.\nYou can adjust the "
|
||||
// "maximum speed setting in your printer's configuration to get higher speeds.");
|
||||
// warning->opt_key = warning_key;
|
||||
// motion_warning.opt_key = warning_key;
|
||||
// }
|
||||
// }
|
||||
};
|
||||
check_extruder(0); // TODO: check used extruder variants
|
||||
|
||||
// check wall sequence and precise outer wall
|
||||
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter) {
|
||||
warning->string = L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences.");
|
||||
warning->opt_key = "precise_outer_wall";
|
||||
if (m_default_region_config.precise_outer_wall && m_default_region_config.wall_sequence != WallSequence::InnerOuter)
|
||||
warn(L("The precise wall option will be ignored for outer-inner or inner-outer-inner wall sequences."), "precise_outer_wall");
|
||||
|
||||
// check adaptive pressure advance model
|
||||
for (unsigned int extruder_id : extruders) {
|
||||
if (m_config.adaptive_pressure_advance.get_at(extruder_id) &&
|
||||
m_config.enable_pressure_advance.get_at(extruder_id)) {
|
||||
|
||||
const std::string pa_model = m_config.adaptive_pressure_advance_model.get_at(extruder_id);
|
||||
if (!pa_model.empty()) {
|
||||
std::string validation_error = AdaptivePAProcessor::validate_adaptive_pa_model(pa_model);
|
||||
if (!validation_error.empty()) {
|
||||
warn(L("The Adaptive Pressure Advance model for one or more extruders may contain invalid values."),
|
||||
"adaptive_pressure_advance_model");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
|
||||
}
|
||||
if (!motion_warning.string.empty())
|
||||
add_warning(motion_warning);
|
||||
}
|
||||
if (!this->has_same_shrinkage_compensations()){
|
||||
warning->string = L("Filament shrinkage will not be used because filament shrinkage for the used filaments does not match.");
|
||||
warning->opt_key = "";
|
||||
}
|
||||
if (!this->has_same_shrinkage_compensations())
|
||||
warn(L("Filament shrinkage will not be used because filament shrinkage for the used filaments does not match."));
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1975,7 +2039,7 @@ Flow Print::brim_flow() const
|
||||
frPerimeter,
|
||||
// Flow::new_from_config_width takes care of the percent to value substitution
|
||||
width,
|
||||
(float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().wall_filament-1),
|
||||
(float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().outer_wall_filament_id-1),
|
||||
(float)this->skirt_first_layer_height());
|
||||
}
|
||||
|
||||
@@ -2398,7 +2462,10 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
start_time = (long long)Slic3r::Utils::get_current_time_utc();
|
||||
|
||||
m_skirt.clear();
|
||||
m_skirt_groups.clear();
|
||||
m_skirt_convex_hull.clear();
|
||||
m_objectBrimAreas.clear();
|
||||
m_supportBrimAreas.clear();
|
||||
m_first_layer_convex_hull.points.clear();
|
||||
for (PrintObject *object : m_objects) object->m_skirt.clear();
|
||||
|
||||
@@ -2492,7 +2559,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
if (this->has_brim()) {
|
||||
Polygons islands_area;
|
||||
make_brim(*this, this->make_try_cancel(), islands_area, m_brimMap,
|
||||
m_supportBrimMap, objPrintVec, printExtruders);
|
||||
m_supportBrimMap, objPrintVec, printExtruders, &m_objectBrimAreas, &m_supportBrimAreas);
|
||||
for (Polygon& poly_ex : islands_area)
|
||||
poly_ex.douglas_peucker(SCALED_RESOLUTION);
|
||||
for (Polygon &poly : union_(this->first_layer_islands(), islands_area))
|
||||
@@ -2589,7 +2656,8 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor
|
||||
gcode.do_export(this, path.c_str(), result, thumbnail_cb);
|
||||
gcode.export_layer_filaments(result);
|
||||
//BBS
|
||||
result->conflict_result = m_conflict_result;
|
||||
if (result != nullptr)
|
||||
result->conflict_result = m_conflict_result;
|
||||
return path.c_str();
|
||||
}
|
||||
|
||||
@@ -2613,11 +2681,13 @@ void Print::_make_skirt()
|
||||
skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
|
||||
}
|
||||
|
||||
// Collect points from all layers contained in skirt height.
|
||||
Points points;
|
||||
struct ObjectSkirtHull {
|
||||
PrintObject* object;
|
||||
Polygon hull;
|
||||
};
|
||||
|
||||
// BBS
|
||||
std::map<PrintObject*, Polygon> object_convex_hulls;
|
||||
// Orca: build one local occupied hull per object from object and support geometry up to skirt height.
|
||||
std::vector<ObjectSkirtHull> object_convex_hulls;
|
||||
for (PrintObject *object : m_objects) {
|
||||
Points object_points;
|
||||
// Get object layers up to skirt_height_z.
|
||||
@@ -2635,30 +2705,13 @@ void Print::_make_skirt()
|
||||
layer->support_fills.collect_points(object_points);
|
||||
}
|
||||
|
||||
object_convex_hulls.insert({ object, Slic3r::Geometry::convex_hull(object_points) });
|
||||
|
||||
// Repeat points for each object copy.
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
Points copy_points = object_points;
|
||||
for (Point &pt : copy_points)
|
||||
pt += instance.shift;
|
||||
append(points, copy_points);
|
||||
}
|
||||
object_convex_hulls.push_back({ object, Slic3r::Geometry::convex_hull(object_points) });
|
||||
}
|
||||
|
||||
// Include the wipe tower.
|
||||
append(points, this->first_layer_wipe_tower_corners());
|
||||
|
||||
// Unless draft shield is enabled, include all brims as well.
|
||||
if (config().draft_shield == dsDisabled)
|
||||
append(points, m_first_layer_convex_hull.points);
|
||||
|
||||
if (points.size() < 3)
|
||||
// At least three points required for a convex hull.
|
||||
if (object_convex_hulls.empty())
|
||||
return;
|
||||
|
||||
this->throw_if_canceled();
|
||||
Polygon convex_hull = Slic3r::Geometry::convex_hull(points);
|
||||
|
||||
// Skirt may be printed on several layers, having distinct layer heights,
|
||||
// but loops must be aligned so can't vary width/spacing
|
||||
@@ -2680,13 +2733,13 @@ void Print::_make_skirt()
|
||||
}
|
||||
}
|
||||
|
||||
// Initial offset of the brim inner edge from the object (possible with a support & raft).
|
||||
// The skirt will touch the brim if the brim is extruded.
|
||||
auto distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
||||
// Draw outlines from outside to inside.
|
||||
// Initial skirt centerline offset from the occupied outline.
|
||||
// The skirt will touch the occupied outline if skirt_distance is zero.
|
||||
// Generate loops inward to outward; callers reverse them before G-code export.
|
||||
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
|
||||
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
|
||||
if (m_config.skirt_type == stCombined) {
|
||||
auto append_skirt_loops_for_hull = [&](const Polygon& hull, ExtrusionEntityCollection& dst, bool collect_skirt_hull) {
|
||||
float distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
||||
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
|
||||
for (size_t i = m_config.skirt_loops, extruder_idx = 0; i > 0; -- i) {
|
||||
this->throw_if_canceled();
|
||||
// Offset the skirt outside.
|
||||
@@ -2694,8 +2747,8 @@ void Print::_make_skirt()
|
||||
// Generate the skirt centerline.
|
||||
Polygon loop;
|
||||
{
|
||||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
||||
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
// Orca: the hull already represents the occupied outline used for this skirt.
|
||||
Polygons loops = offset(hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||
if (loops.empty())
|
||||
break;
|
||||
@@ -2711,7 +2764,7 @@ void Print::_make_skirt()
|
||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||
)));
|
||||
eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
|
||||
m_skirt.append(eloop);
|
||||
dst.append(eloop);
|
||||
if (m_config.min_skirt_length.value > 0) {
|
||||
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
||||
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
||||
@@ -2727,69 +2780,147 @@ void Print::_make_skirt()
|
||||
++ extruder_idx;
|
||||
}
|
||||
} else {
|
||||
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
|
||||
// The skirt length is not limited, extrude the skirt with the 1st extruder only.
|
||||
}
|
||||
}
|
||||
} else {
|
||||
m_skirt.clear();
|
||||
}
|
||||
// Brims were generated inside out, reverse to print the outmost contour first.
|
||||
m_skirt.reverse();
|
||||
|
||||
// Remember the outer edge of the last skirt line extruded as m_skirt_convex_hull.
|
||||
for (Polygon &poly : offset(convex_hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
|
||||
append(m_skirt_convex_hull, std::move(poly.points));
|
||||
if (collect_skirt_hull)
|
||||
for (Polygon &poly : offset(hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
|
||||
append(m_skirt_convex_hull, std::move(poly.points));
|
||||
};
|
||||
|
||||
if (m_config.skirt_type == stPerObject) {
|
||||
// BBS
|
||||
for (auto obj_cvx_hull : object_convex_hulls) {
|
||||
double object_skirt_distance = float(scale_(m_config.skirt_distance.value - spacing/2.));
|
||||
PrintObject* object = obj_cvx_hull.first;
|
||||
object->m_skirt.clear();
|
||||
extruded_length.assign(extruded_length.size(), 0.);
|
||||
for (size_t i = m_config.skirt_loops.value, extruder_idx = 0; i > 0; -- i) {
|
||||
object_skirt_distance += float(scale_(spacing));
|
||||
Polygon loop;
|
||||
{
|
||||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
||||
Polygons loops = offset(obj_cvx_hull.second, object_skirt_distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||||
if (loops.empty())
|
||||
break;
|
||||
loop = loops.front();
|
||||
}
|
||||
m_skirt.clear();
|
||||
m_skirt_groups.clear();
|
||||
|
||||
// Extrude the skirt loop.
|
||||
ExtrusionLoop eloop(elrSkirt);
|
||||
eloop.paths.emplace_back(ExtrusionPath(
|
||||
ExtrusionPath(
|
||||
erSkirt,
|
||||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||||
flow.width(),
|
||||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||||
)));
|
||||
eloop.paths.back().polyline = Polyline3(loop.split_at_first_point());
|
||||
object->m_skirt.append(std::move(eloop));
|
||||
if (m_config.min_skirt_length.value > 0) {
|
||||
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
||||
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
||||
if (extruded_length[extruder_idx] < m_config.min_skirt_length.value) {
|
||||
// Not extruded enough yet with the current extruder. Add another loop.
|
||||
if (i == 1)
|
||||
++ i;
|
||||
} else {
|
||||
assert(extruded_length[extruder_idx] >= m_config.min_skirt_length.value);
|
||||
// Enough extruded with the current extruder. Extrude with the next one,
|
||||
// until the prescribed number of skirt loops is extruded.
|
||||
if (extruder_idx + 1 < extruders.size())
|
||||
++ extruder_idx;
|
||||
}
|
||||
} else {
|
||||
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
|
||||
}
|
||||
if (m_config.skirt_type == stPerObject && m_config.print_sequence == PrintSequence::ByObject) {
|
||||
for (const ObjectSkirtHull& object_hull : object_convex_hulls) {
|
||||
object_hull.object->m_skirt.clear();
|
||||
append_skirt_loops_for_hull(object_hull.hull, object_hull.object->m_skirt, false);
|
||||
object_hull.object->m_skirt.reverse();
|
||||
}
|
||||
} else if (m_config.skirt_type == stCombined || m_config.skirt_type == stPerObject) {
|
||||
struct SkirtGroupItem {
|
||||
Points occupied_points;
|
||||
bool emits_skirt;
|
||||
};
|
||||
|
||||
// Orca: group items represent occupied first-layer areas. Object items emit skirts;
|
||||
// obstacle-only items, such as wipe tower, only force nearby object groups to merge.
|
||||
std::vector<SkirtGroupItem> group_items;
|
||||
const coord_t grouping_offset = scale_(m_config.skirt_distance.value + m_config.skirt_loops.value * spacing);
|
||||
for (const ObjectSkirtHull& object_hull : object_convex_hulls) {
|
||||
PrintObject* object = object_hull.object;
|
||||
Points occupied_points;
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
Points copy_points = object_hull.hull.points;
|
||||
for (Point &pt : copy_points)
|
||||
pt += instance.shift;
|
||||
append(occupied_points, copy_points);
|
||||
}
|
||||
|
||||
auto append_brim_points = [&occupied_points](const ExPolygons& areas) {
|
||||
for (const ExPolygon& area : areas)
|
||||
append(occupied_points, area.contour.points);
|
||||
};
|
||||
if (auto it = m_objectBrimAreas.find(object->id()); it != m_objectBrimAreas.end())
|
||||
append_brim_points(it->second);
|
||||
if (auto it = m_supportBrimAreas.find(object->id()); it != m_supportBrimAreas.end())
|
||||
append_brim_points(it->second);
|
||||
if (occupied_points.size() < 3)
|
||||
continue;
|
||||
|
||||
// Orca: include the object's brim/support-brim footprint before checking skirt collisions.
|
||||
group_items.push_back({ std::move(occupied_points), true });
|
||||
}
|
||||
|
||||
// Orca: the wipe tower contributes occupied area, but does not emit a skirt by itself.
|
||||
Points wipe_tower_points = this->first_layer_wipe_tower_corners();
|
||||
if (wipe_tower_points.size() >= 3)
|
||||
group_items.push_back({ std::move(wipe_tower_points), false });
|
||||
|
||||
std::vector<size_t> parent(group_items.size());
|
||||
std::iota(parent.begin(), parent.end(), 0);
|
||||
// Orca: union-find keeps collision merging local without repeatedly rebuilding item lists.
|
||||
auto find_parent = [&parent](size_t idx) {
|
||||
while (parent[idx] != idx) {
|
||||
parent[idx] = parent[parent[idx]];
|
||||
idx = parent[idx];
|
||||
}
|
||||
return idx;
|
||||
};
|
||||
auto unite = [&parent, &find_parent](size_t a, size_t b) {
|
||||
a = find_parent(a);
|
||||
b = find_parent(b);
|
||||
if (a != b)
|
||||
parent[b] = a;
|
||||
};
|
||||
|
||||
// Orca: combined skirt is the same grouping model with all items forced into one group.
|
||||
if (m_config.skirt_type == stCombined && !group_items.empty())
|
||||
for (size_t i = 1; i < group_items.size(); ++i)
|
||||
unite(0, i);
|
||||
|
||||
auto build_grouped_points = [&]() {
|
||||
struct GroupData {
|
||||
Points points;
|
||||
bool emits_skirt = false;
|
||||
};
|
||||
|
||||
std::map<size_t, GroupData> grouped;
|
||||
for (size_t i = 0; i < group_items.size(); ++i) {
|
||||
GroupData& group = grouped[find_parent(i)];
|
||||
append(group.points, group_items[i].occupied_points);
|
||||
group.emits_skirt = group.emits_skirt || group_items[i].emits_skirt;
|
||||
}
|
||||
return grouped;
|
||||
};
|
||||
|
||||
bool groups_changed = m_config.skirt_type == stPerObject;
|
||||
while (groups_changed) {
|
||||
groups_changed = false;
|
||||
auto grouped_points = build_grouped_points();
|
||||
std::vector<std::pair<size_t, Polygon>> group_envelopes;
|
||||
for (const auto& [root, group] : grouped_points) {
|
||||
if (group.points.size() < 3)
|
||||
continue;
|
||||
|
||||
// Orca: emitting groups are expanded to their final skirt reach; obstacle groups are not.
|
||||
Polygon envelope = Geometry::convex_hull(group.points);
|
||||
if (group.emits_skirt) {
|
||||
// Orca: merge groups when a skirt envelope intersects another group or obstacle.
|
||||
Polygons envelopes = offset(envelope, grouping_offset, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
if (envelopes.empty())
|
||||
continue;
|
||||
envelope = std::move(envelopes.front());
|
||||
}
|
||||
group_envelopes.emplace_back(root, std::move(envelope));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < group_envelopes.size(); ++i) {
|
||||
for (size_t j = i + 1; j < group_envelopes.size(); ++j) {
|
||||
const size_t root_i = find_parent(group_envelopes[i].first);
|
||||
const size_t root_j = find_parent(group_envelopes[j].first);
|
||||
if (root_i != root_j && !intersection(group_envelopes[i].second, group_envelopes[j].second).empty()) {
|
||||
unite(root_i, root_j);
|
||||
groups_changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto grouped_points = build_grouped_points();
|
||||
for (auto& [_, group] : grouped_points) {
|
||||
if (!group.emits_skirt || group.points.size() < 3)
|
||||
continue;
|
||||
// Orca: after merging, use the occupied outline directly; do not add skirt distance twice.
|
||||
ExtrusionEntityCollection group_skirt;
|
||||
append_skirt_loops_for_hull(Geometry::convex_hull(group.points), group_skirt, true);
|
||||
if (!group_skirt.empty()) {
|
||||
group_skirt.reverse();
|
||||
// Orca: keep m_skirt as a flattened compatibility mirror for preview/extents.
|
||||
m_skirt.append(group_skirt.entities);
|
||||
m_skirt_groups.push_back(std::move(group_skirt));
|
||||
}
|
||||
object->m_skirt.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3502,6 +3633,24 @@ std::string Print::output_filename(const std::string &filename_base) const
|
||||
config.set_key_value("plate_number", new ConfigOptionString(get_plate_number_formatted()));
|
||||
config.set_key_value("model_name", new ConfigOptionString(get_model_name()));
|
||||
|
||||
// the same type of filament contains multiple names, support exporting according to the filament name
|
||||
auto full_print_config = this->full_print_config();
|
||||
const ConfigOptionStrings* filament_settings_id = full_print_config.option<ConfigOptionStrings>("filament_settings_id");
|
||||
std::string filament_name = "";
|
||||
auto extruders = this->extruders(true);
|
||||
if(!extruders.empty()) {
|
||||
// first extruder is the default extruder
|
||||
int extruder_id = extruders.front();
|
||||
if(filament_settings_id->values.size() > extruder_id) {
|
||||
filament_name = filament_settings_id->values[extruder_id];
|
||||
}
|
||||
}
|
||||
size_t end_pos = filament_name.find_first_of("@");
|
||||
if (end_pos != std::string::npos) {
|
||||
filament_name = filament_name.substr(0, end_pos);
|
||||
}
|
||||
config.set_key_value("filament_name", new ConfigOptionString(filament_name));
|
||||
|
||||
return this->PrintBase::output_filename(m_config.filename_format.value, ".gcode", filename_base, &config);
|
||||
}
|
||||
|
||||
@@ -3594,9 +3743,12 @@ DynamicConfig PrintStatistics::config() const
|
||||
config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
|
||||
config.set_key_value("total_toolchanges", new ConfigOptionInt(this->total_toolchanges));
|
||||
config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
|
||||
config.set_key_value("extruded_weight_total", new ConfigOptionFloat(this->total_weight));
|
||||
config.set_key_value("extruded_volume_total", new ConfigOptionFloat(this->total_extruded_volume));
|
||||
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost));
|
||||
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament));
|
||||
config.set_key_value("initial_tool", new ConfigOptionInt(static_cast<int>(this->initial_tool)));
|
||||
config.set_key_value("initial_extruder", new ConfigOptionInt(static_cast<int>(this->initial_tool)));
|
||||
return config;
|
||||
}
|
||||
|
||||
@@ -3605,8 +3757,8 @@ DynamicConfig PrintStatistics::placeholders()
|
||||
DynamicConfig config;
|
||||
for (const std::string key : {
|
||||
"print_time", "normal_print_time", "silent_print_time",
|
||||
"used_filament", "extruded_volume", "total_cost", "total_weight",
|
||||
"initial_tool", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"})
|
||||
"used_filament", "extruded_volume", "extruded_volume_total", "total_cost", "total_weight", "extruded_weight_total",
|
||||
"initial_tool", "initial_extruder", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"})
|
||||
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
|
||||
return config;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user