Merge branch '2.2.3' into dev_upgrade_alves

# Conflicts:
#	localization/i18n/zh_CN/Snapmaker_Orca_zh_CN.po
This commit is contained in:
alves
2026-01-27 10:36:39 +08:00
34 changed files with 4878 additions and 10081 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -185,6 +185,8 @@ public:
// Wheter or not this volume is outside print volume.
bool is_outside : 1;
bool partly_inside : 1;
// Snapmaker: Whether or not this volume is too close to boundary for spiral lift
bool near_boundary_for_spiral_lift : 1;
// Wheter or not this volume has been generated from a modifier
bool is_modifier : 1;
// Wheter or not this volume has been generated from the wipe tower
@@ -516,6 +518,8 @@ public:
// returns the containment state in the given out_state, if non-null
bool check_outside_state(const Slic3r::BuildVolume& build_volume, ModelInstanceEPrintVolumeState* out_state) const;
void reset_outside_state();
// Snapmaker: 检查是否有任何 volume 靠近边界(螺旋抬升风险)
bool is_any_volume_near_boundary_for_spiral_lift() const;
void update_colors_by_extruder(const DynamicPrintConfig *config, bool is_update_alpha = true);

View File

@@ -2399,100 +2399,6 @@ void GCodeViewer::load_toolpaths(const GCodeProcessorResult& gcode_result, const
{
//BBS: use convex_hull for toolpath outside check
m_contained_in_bed = build_volume.all_paths_inside(gcode_result, m_paths_bounding_box);
// BBS: Enhanced Travel move checking with smart filtering
// Skip initial setup moves (G28, G29) and only check moves during actual printing
if (m_contained_in_bed) {
// Find first extrusion move to determine where actual printing starts
size_t first_print_move = 0;
for (size_t i = 0; i < gcode_result.moves.size(); ++i) {
if (gcode_result.moves[i].type == EMoveType::Extrude &&
gcode_result.moves[i].position.z() > 0.1) { // Above first layer
first_print_move = i;
break;
}
}
// Only check moves after printing starts (skip G28/G29 initialization)
if (first_print_move > 0) {
bool has_travel_violations = false;
int violation_count = 0;
// Use same epsilon as BuildVolume::all_paths_inside() for consistency
static constexpr const double epsilon = BuildVolume::BedEpsilon;
// Clear existing violations and populate with detailed info
auto& gcode_result_writable = const_cast<GCodeProcessorResult&>(gcode_result);
gcode_result_writable.boundary_violations.clear();
for (size_t i = first_print_move; i < gcode_result.moves.size(); ++i) {
const auto& move = gcode_result.moves[i];
// Only check Travel moves (Extrude already checked by all_paths_inside)
if (move.type == EMoveType::Travel) {
// Quick rectangle bed check with tolerance
auto bbox = build_volume.bounding_volume();
if (move.position.x() < bbox.min.x() - epsilon ||
move.position.x() > bbox.max.x() + epsilon ||
move.position.y() < bbox.min.y() - epsilon ||
move.position.y() > bbox.max.y() + epsilon) {
// Create detailed violation info
GCodeProcessorResult::BoundaryViolationInfo violation;
violation.violation_type = BoundaryValidator::ViolationType::TravelMove;
violation.position = Vec3d(move.position.x(), move.position.y(), move.position.z());
violation.print_z = move.position.z();
violation.component_name = "Travel";
// Determine which boundary was exceeded
double dist_x_min = bbox.min.x() - move.position.x();
double dist_x_max = move.position.x() - bbox.max.x();
double dist_y_min = bbox.min.y() - move.position.y();
double dist_y_max = move.position.y() - bbox.max.y();
if (dist_x_min > 0) {
violation.direction = BoundaryValidator::BoundaryDirection::X_Min;
violation.distance_out = dist_x_min;
} else if (dist_x_max > 0) {
violation.direction = BoundaryValidator::BoundaryDirection::X_Max;
violation.distance_out = dist_x_max;
} else if (dist_y_min > 0) {
violation.direction = BoundaryValidator::BoundaryDirection::Y_Min;
violation.distance_out = dist_y_min;
} else if (dist_y_max > 0) {
violation.direction = BoundaryValidator::BoundaryDirection::Y_Max;
violation.distance_out = dist_y_max;
}
gcode_result_writable.boundary_violations.push_back(violation);
violation_count++;
if (violation_count <= 3) { // Log first 3
std::string dir_str;
switch (violation.direction) {
case BoundaryValidator::BoundaryDirection::X_Min: dir_str = "X_min"; break;
case BoundaryValidator::BoundaryDirection::X_Max: dir_str = "X_max"; break;
case BoundaryValidator::BoundaryDirection::Y_Min: dir_str = "Y_min"; break;
case BoundaryValidator::BoundaryDirection::Y_Max: dir_str = "Y_max"; break;
default: dir_str = "Unknown"; break;
}
BOOST_LOG_TRIVIAL(warning) << "Travel move #" << i
<< " outside bounds: " << violation.component_name << " " << dir_str
<< " at pos=(" << move.position.x()
<< ", " << move.position.y() << ", " << move.position.z() << ")";
}
has_travel_violations = true;
}
}
}
if (has_travel_violations) {
BOOST_LOG_TRIVIAL(warning) << "Found " << violation_count
<< " Travel moves outside build volume";
m_contained_in_bed = false;
}
}
}
if (m_contained_in_bed) {
//PartPlateList& partplate_list = wxGetApp().plater()->get_partplate_list();
//PartPlate* plate = partplate_list.get_curr_plate();

View File

@@ -849,12 +849,6 @@ public:
//BBS: add only gcode mode
bool is_only_gcode_in_preview() const { return m_only_gcode_in_preview; }
// Snapmaker: Get boundary violations from gcode_result
const std::vector<GCodeProcessorResult::BoundaryViolationInfo>& get_boundary_violations() const {
static const std::vector<GCodeProcessorResult::BoundaryViolationInfo> empty_violations;
return (m_gcode_result != nullptr) ? m_gcode_result->boundary_violations : empty_violations;
}
EViewType get_view_type() const { return m_view_type; }
void set_view_type(EViewType type, bool reset_feature_type_visible = true) {
if (type == EViewType::Count)

View File

@@ -2823,6 +2823,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
if (printer_technology != ptSLA || !contained_min_one)
_set_warning_notification(EWarning::SlaSupportsOutside, false);
// Snapmaker: 螺旋抬升边界警告 - 无论模型是否超出边界都检测
if (contained_min_one) {
_set_warning_notification(EWarning::SpiralLiftNearBoundary, _is_any_volume_near_boundary_for_spiral_lift());
} else {
_set_warning_notification(EWarning::SpiralLiftNearBoundary, false);
}
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS,
contained_min_one && !m_model->objects.empty() && !partlyOut));
}
@@ -2830,6 +2837,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
_set_warning_notification(EWarning::ObjectOutside, false);
_set_warning_notification(EWarning::ObjectClashed, false);
_set_warning_notification(EWarning::SlaSupportsOutside, false);
_set_warning_notification(EWarning::SpiralLiftNearBoundary, false); // Snapmaker: 清空警告
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
}
}
@@ -9687,102 +9695,7 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
}
case EWarning::ObjectOutside: text = _u8L("An object is laid over the plate boundaries."); break;
case EWarning::ToolHeightOutside: text = _u8L("A G-code path goes beyond the max print height."); error = ErrorType::SLICING_ERROR; break;
case EWarning::ToolpathOutside: {
error = ErrorType::SLICING_ERROR;
// Snapmaker: Enhanced boundary violation reporting with detailed information
static std::string prevBoundaryText;
text = prevBoundaryText;
// Helper function to get localized violation type name
auto get_localized_type_string = [](BoundaryValidator::ViolationType type) -> std::string {
switch (type) {
case BoundaryValidator::ViolationType::TravelMove: return _u8L("Travel Move");
case BoundaryValidator::ViolationType::ExtrudeMove: return _u8L("Extrude Move");
case BoundaryValidator::ViolationType::SpiralLift: return _u8L("Spiral Lift");
case BoundaryValidator::ViolationType::LazyLift: return _u8L("Lazy Lift");
case BoundaryValidator::ViolationType::WipeTower: return _u8L("Wipe Tower");
case BoundaryValidator::ViolationType::Skirt: return _u8L("Skirt");
case BoundaryValidator::ViolationType::Brim: return _u8L("Brim");
case BoundaryValidator::ViolationType::Support: return _u8L("Support");
case BoundaryValidator::ViolationType::ArcMove: return _u8L("Arc Move");
default: return _u8L("Unknown");
}
};
// Helper function to get localized direction string
auto get_localized_direction_string = [](BoundaryValidator::BoundaryDirection dir) -> std::string {
switch (dir) {
case BoundaryValidator::BoundaryDirection::X_Min: return _u8L("beyond X minimum");
case BoundaryValidator::BoundaryDirection::X_Max: return _u8L("beyond X maximum");
case BoundaryValidator::BoundaryDirection::Y_Min: return _u8L("beyond Y minimum");
case BoundaryValidator::BoundaryDirection::Y_Max: return _u8L("beyond Y maximum");
case BoundaryValidator::BoundaryDirection::Z_Max: return _u8L("above Z maximum");
case BoundaryValidator::BoundaryDirection::Radius: return _u8L("beyond bed radius");
default: return _u8L("outside boundaries");
}
};
// Try to get detailed violation information from gcode_result
const auto& violations = m_gcode_viewer.get_boundary_violations();
if (!violations.empty()) {
// Group violations by type for better summary
std::map<BoundaryValidator::ViolationType, int> violation_counts;
std::map<BoundaryValidator::ViolationType, std::string> type_names;
for (const auto& v : violations) {
violation_counts[v.violation_type]++;
if (type_names.find(v.violation_type) == type_names.end()) {
type_names[v.violation_type] = get_localized_type_string(v.violation_type);
}
}
// Build detailed message
std::string msg = _u8L("G-code boundary violations detected:\n\n");
int total_count = 0;
for (const auto& [type, count] : violation_counts) {
msg += "" + type_names[type] + ": " + std::to_string(count) + " " + _u8L("violation(s)") + "\n";
total_count += count;
}
if (total_count > 1) {
msg += "\n" + _u8L("Total") + ": " + std::to_string(total_count) + " " + _u8L("violations");
}
// Show details of first few violations
if (!violations.empty()) {
msg += "\n\n" + _u8L("Details") + ":\n";
int show_count = std::min((int)violations.size(), 5);
for (int i = 0; i < show_count; ++i) {
const auto& v = violations[i];
// Build localized description
std::string desc;
if (!v.component_name.empty()) {
desc += v.component_name + " - ";
}
desc += get_localized_type_string(v.violation_type);
desc += " " + get_localized_direction_string(v.direction);
msg += " " + std::to_string(i + 1) + ". " + desc;
if (v.distance_out > 0.001) {
msg += " (" + (boost::format(_u8L("%.2f mm out")) % v.distance_out).str() + ")";
}
if (v.print_z > 0) {
msg += " " + _u8L("at Z") + "=" + (boost::format("%.1f") % v.print_z).str();
}
msg += "\n";
}
if ((int)violations.size() > show_count) {
msg += " " + _u8L("... and more");
}
}
text = msg;
prevBoundaryText = text;
} else {
// Fallback to generic message if no detailed info available
text = _u8L("A G-code path goes beyond the plate boundaries.");
}
break;
}
case EWarning::ToolpathOutside: text = _u8L("A G-code path goes beyond the plate boundaries."); error = ErrorType::SLICING_ERROR; break;
// BBS: remove _u8L() for SLA
case EWarning::SlaSupportsOutside: text = ("SLA supports outside the print area were detected."); error = ErrorType::PLATER_ERROR; break;
case EWarning::SomethingNotShown: text = _u8L("Only the object being edited is visible."); break;
@@ -9791,6 +9704,11 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state)
"Please solve the problem by moving it totally on or off the plate, and confirming that the height is within the build volume.");
error = ErrorType::PLATER_ERROR;
break;
// Snapmaker: 螺旋抬升靠近边界警告
case EWarning::SpiralLiftNearBoundary:
text = _u8L("Model too close to bed boundary. Disable spiral lifting or keep at least 3.5mm gap to avoid collision.");
error = ErrorType::SLICING_SERIOUS_WARNING;
break;
}
//BBS: this may happened when exit the app, plater is null
if (!wxGetApp().plater())
@@ -9845,6 +9763,12 @@ bool GLCanvas3D::_is_any_volume_outside() const
return false;
}
// Snapmaker: 检查是否有任何 volume 靠近边界(螺旋抬升风险)
bool GLCanvas3D::_is_any_volume_near_boundary_for_spiral_lift() const
{
return m_volumes.is_any_volume_near_boundary_for_spiral_lift();
}
void GLCanvas3D::_update_selection_from_hover()
{
bool ctrl_pressed = wxGetKeyState(WXK_CONTROL);

View File

@@ -381,7 +381,8 @@ class GLCanvas3D
SomethingNotShown,
ObjectClashed,
GCodeConflict,
ToolHeightOutside
ToolHeightOutside,
SpiralLiftNearBoundary // Snapmaker: 螺旋抬升靠近边界警告
};
class RenderStats
@@ -1238,6 +1239,8 @@ private:
void _set_warning_notification(EWarning warning, bool state);
bool _is_any_volume_outside() const;
// Snapmaker: 检查是否有任何 volume 靠近边界(螺旋抬升风险)
bool _is_any_volume_near_boundary_for_spiral_lift() const;
// updates the selection from the content of m_hover_volume_idxs
void _update_selection_from_hover();