diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index c88649b267..7550ba8097 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -1422,8 +1422,10 @@ static std::vector get_path_of_change_filament(const Print& print) // We want to rotate and shift all extrusions (gcode postprocessing) and starting and ending position float alpha = m_wipe_tower_rotation / 180.f * float(M_PI); + // The rib-wall offset is tower-local, so it rotates with the tower (unlike the BBL + // tower in append_tcr, which never rotates). Priming lines are absolute bed moves. auto transform_wt_pt = [&alpha, this](const Vec2f &pt) -> Vec2f { - Vec2f out = Eigen::Rotation2Df(alpha) * pt; + Vec2f out = Eigen::Rotation2Df(alpha) * (pt + m_rib_offset); out += m_wipe_tower_pos; return out; }; @@ -1435,7 +1437,7 @@ static std::vector get_path_of_change_filament(const Print& print) end_pos = transform_wt_pt(end_pos); } - Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : m_wipe_tower_pos; + Vec2f wipe_tower_offset = tcr.priming ? Vec2f::Zero() : Vec2f(m_wipe_tower_pos + Eigen::Rotation2Df(alpha) * m_rib_offset); float wipe_tower_rotation = tcr.priming ? 0.f : alpha; Vec2f plate_origin_2d(m_plate_origin(0), m_plate_origin(1)); diff --git a/src/libslic3r/GCode/PrintExtents.cpp b/src/libslic3r/GCode/PrintExtents.cpp index 4a65ae5ae4..9008adaa9a 100644 --- a/src/libslic3r/GCode/PrintExtents.cpp +++ b/src/libslic3r/GCode/PrintExtents.cpp @@ -143,7 +143,8 @@ BoundingBoxf get_wipe_tower_extrusions_extents(const Print &print, const coordf_ double wipe_tower_y = print.config().wipe_tower_y.get_at(plate_idx) + plate_origin(1); Transform2d trafo = Eigen::Translation2d(wipe_tower_x, wipe_tower_y) * - Eigen::Rotation2Dd(Geometry::deg2rad(print.config().wipe_tower_rotation_angle.value)); + Eigen::Rotation2Dd(Geometry::deg2rad(print.config().wipe_tower_rotation_angle.value)) * + Eigen::Translation2d(print.wipe_tower_data().rib_offset.cast()); // tower-local rib-wall shift, zero unless rib BoundingBoxf bbox; for (const std::vector &tool_changes : print.wipe_tower_data().tool_changes) { diff --git a/src/libslic3r/GCode/WipeTower2.cpp b/src/libslic3r/GCode/WipeTower2.cpp index 34b385d4be..601992f515 100644 --- a/src/libslic3r/GCode/WipeTower2.cpp +++ b/src/libslic3r/GCode/WipeTower2.cpp @@ -2228,15 +2228,21 @@ void WipeTower2::plan_toolchange(float z_par, float layer_height_par, unsigned i return; // this is an actual toolchange - let's calculate depth to reserve on the wipe tower - float width = m_wipe_tower_width - 3*m_perimeter_width; + const bool first_layer_plan = (m_plan.size() - 1) == m_first_layer_idx; + m_plan.back().tool_changes.push_back(set_toolchange(old_tool, new_tool, layer_height_par, wipe_volume, first_layer_plan)); +} + +WipeTower2::WipeTowerInfo::ToolChange WipeTower2::set_toolchange(size_t old_tool, size_t new_tool, float layer_height, float wipe_volume, bool first_layer_plan) +{ + float width = m_wipe_tower_width - 3*m_perimeter_width; float length_to_extrude = volume_to_length(0.25f * std::accumulate(m_filpar[old_tool].ramming_speed.begin(), m_filpar[old_tool].ramming_speed.end(), 0.f), m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator, - layer_height_par); + layer_height); // Orca: Set ramming depth to 0 if ramming is disabled. float ramming_depth = m_enable_filament_ramming ? ((int(length_to_extrude / width) + 1) * (m_perimeter_width * m_filpar[old_tool].ramming_line_width_multiplicator * m_filpar[old_tool].ramming_step_multiplicator) * m_extra_spacing_ramming) : 0; float first_wipe_line = - (width*((length_to_extrude / width)-int(length_to_extrude / width)) - width); - float first_wipe_volume = length_to_volume(first_wipe_line, m_perimeter_width * m_extra_flow, layer_height_par); + float first_wipe_volume = length_to_volume(first_wipe_line, m_perimeter_width * m_extra_flow, layer_height); // ORCA: Keep wipe-depth planning consistent with toolchange_Wipe(). // ORCA: On the first layer, toolchange_Wipe() advances purge rows using @@ -2245,12 +2251,11 @@ void WipeTower2::plan_toolchange(float z_par, float layer_height_par, unsigned i // ORCA: float dy = (is_first_layer() ? m_extra_flow : m_extra_spacing_wipe) * m_perimeter_width; // ORCA: Use the same spacing here so reserved depth matches consumed depth // ORCA: and first-layer purge segments do not leave visible gaps. - const bool first_layer_plan = (m_plan.size() - 1) == m_first_layer_idx; const float planning_spacing = first_layer_plan ? m_extra_flow : m_extra_spacing_wipe; - float wiping_depth = get_wipe_depth(wipe_volume - first_wipe_volume, layer_height_par, m_perimeter_width, m_extra_flow, planning_spacing, width); - - m_plan.back().tool_changes.push_back(WipeTowerInfo::ToolChange(old_tool, new_tool, ramming_depth + wiping_depth, ramming_depth, first_wipe_line, wipe_volume)); + float wiping_depth = get_wipe_depth(wipe_volume - first_wipe_volume, layer_height, m_perimeter_width, m_extra_flow, planning_spacing, width); + + return WipeTowerInfo::ToolChange(old_tool, new_tool, ramming_depth + wiping_depth, ramming_depth, first_wipe_line, wipe_volume); } @@ -2378,9 +2383,35 @@ void WipeTower2::generate(std::vector> } #endif - m_rib_length = std::max({m_rib_length, sqrt(m_wipe_tower_depth * m_wipe_tower_depth + m_wipe_tower_width * m_wipe_tower_width)}); + if (m_wall_type == (int)wtwRib) { + // Rib wall: force a square tower like WipeTower::plan_tower_new(), ignoring the + // configured prime_tower_width (the GUI greys it out in rib mode). The planned depths + // already include the extra-spacing factors, so sqrt(depth * width) preserves the + // purge area. Replan every toolchange for the new width, then re-derive the depths. + float max_depth = 0.f; + for (const auto& current_plan : m_plan) + max_depth = std::max(max_depth, current_plan.depth); + if (max_depth > EPSILON) { + m_wipe_tower_width = align_ceil(std::sqrt(max_depth * m_wipe_tower_width), m_perimeter_width); + for (size_t idx = 0; idx < m_plan.size(); ++idx) + for (auto& toolchange : m_plan[idx].tool_changes) + toolchange = set_toolchange(toolchange.old_tool, toolchange.new_tool, + m_plan[idx].height, toolchange.wipe_volume, + idx == m_first_layer_idx); + plan_tower(); + } + + // Like WipeTower::plan_tower_new(): extend the ribs instead of the tower when the + // tower is smaller than the height-based stability minimum. + const float min_depth = WipeTower::get_limit_depth_by_height(m_wipe_tower_height); + if (m_wipe_tower_depth + EPSILON < min_depth) + m_rib_length = std::max(m_rib_length, min_depth * (float)std::sqrt(2.f)); + } + + const float diagonal = std::sqrt(m_wipe_tower_depth * m_wipe_tower_depth + m_wipe_tower_width * m_wipe_tower_width); + m_rib_length = std::max(m_rib_length, diagonal); m_rib_length += m_extra_rib_length; - m_rib_length = std::max(0.f, m_rib_length); + m_rib_length = std::max(diagonal, m_rib_length); // a negative extra length must not shrink the ribs below the diagonal m_rib_width = std::min(m_rib_width, std::min(m_wipe_tower_depth, m_wipe_tower_width) / 2.f); // Ensure that the rib wall of the wipetower are attached to the infill. @@ -2532,10 +2563,12 @@ Polygon WipeTower2::generate_support_rib_wall(WipeTowerWriter2& insert_skip_polygon = wall_polygon; } writer.generate_path(result_wall, feedrate, retract_length, retract_speed, m_used_fillet); - //if (m_cur_layer_id == 0) { - // BoundingBox bbox = get_extents(result_wall); - // m_rib_offset = Vec2f(-unscaled(bbox.min.x()), -unscaled(bbox.min.y())); - //} + // Tower-local shift that puts the rib wall's protruding first-layer min corner at the + // configured tower position, like WipeTower::generate_support_wall_new(). + if (rib_wall && is_first_layer()) { + BoundingBox bbox = get_extents(result_wall); + m_rib_offset = Vec2f(-unscaled(bbox.min.x()), -unscaled(bbox.min.y())); + } return insert_skip_polygon; } diff --git a/src/libslic3r/GCode/WipeTower2.hpp b/src/libslic3r/GCode/WipeTower2.hpp index 7060eefa4a..6169e8c392 100644 --- a/src/libslic3r/GCode/WipeTower2.hpp +++ b/src/libslic3r/GCode/WipeTower2.hpp @@ -69,9 +69,9 @@ public: const float brim = m_wipe_tower_brim_width_real; return BoundingBoxf(Vec2d(-brim, -brim), Vec2d(double(m_wipe_tower_width) + brim, double(m_wipe_tower_depth) + brim)); } - // WT2 doesn't currently compute a rib-origin compensation like WipeTower (m_rib_offset), - // so expose a zero offset for consistency purposes (to maintain API parity). - Vec2f get_rib_offset() const { return Vec2f::Zero(); } + // Tower-local shift that puts the rib wall's first-layer min corner at the configured + // tower position, like WipeTower::get_rib_offset(). Zero unless the rib wall is used. + Vec2f get_rib_offset() const { return m_rib_offset; } float get_rib_width() const { return m_rib_width; } float get_rib_length() const { return m_rib_length; } @@ -231,6 +231,7 @@ private: float m_rib_width = 10; float m_extra_rib_length = 0; float m_rib_length = 0; + Vec2f m_rib_offset = Vec2f::Zero(); bool m_enable_arc_fitting = false; @@ -372,6 +373,10 @@ private: float spacing); Polygon generate_rib_polygon(const WipeTower::box_coordinates& wt_box); + + // Computes the depth reserved for a toolchange (shared by plan_toolchange() and the + // rib-wall square-tower replanning in generate()). + WipeTowerInfo::ToolChange set_toolchange(size_t old_tool, size_t new_tool, float layer_height, float wipe_volume, bool first_layer_plan); }; diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index ecc2f39c88..371f1e68ea 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -4333,7 +4333,12 @@ void Print::_make_wipe_tower() wipe_tower.get_rib_width(), wipe_tower.get_rib_length(), config().wipe_tower_fillet_wall.value); const Vec3d origin = Vec3d::Zero(); - m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_wipe_tower_height(), + // FakeWipeTower::pos is a bed-frame translation applied after rotation + // (getFakeExtrusionPathsFromWipeTower2 rotates about the local origin), so the + // tower-local rib offset must be rotated into the bed frame first. + m_fake_wipe_tower.rib_offset = Eigen::Rotation2Df(Geometry::deg2rad((float)config().wipe_tower_rotation_angle.value)) * + wipe_tower.get_rib_offset(); + m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position() + m_fake_wipe_tower.rib_offset, wipe_tower.width(), wipe_tower.get_wipe_tower_height(), config().initial_layer_print_height, m_wipe_tower_data.depth, m_wipe_tower_data.z_and_depth_pairs, m_wipe_tower_data.brim_width, config().wipe_tower_rotation_angle, config().wipe_tower_cone_angle,