diff --git a/src/libslic3r/BeltGCode.cpp b/src/libslic3r/BeltGCode.cpp new file mode 100644 index 0000000000..dc1b6ca439 --- /dev/null +++ b/src/libslic3r/BeltGCode.cpp @@ -0,0 +1,119 @@ +#include "BeltGCode.hpp" +#include "BeltGCodeWriter.hpp" +#include "BeltTransform.hpp" +#include "Print.hpp" + +#include + +namespace Slic3r { + +void BeltGCode::init_belt_writer(Print &print, bool is_bbl_printers) +{ + if (!print.config().belt_printer.value) + return; + + auto belt_writer = std::make_unique(); + belt_writer->set_is_bbl_machine(is_bbl_printers); + belt_writer->set_belt_angle(print.config().belt_printer_angle.value); + belt_writer->set_axis_remap( + int(print.config().belt_gcode_remap_x.value), + int(print.config().belt_gcode_remap_y.value), + int(print.config().belt_gcode_remap_z.value)); + BoundingBoxf bbox_bed(print.config().printable_area.values); + belt_writer->set_build_volume_max(Vec3d(bbox_bed.max.x(), bbox_bed.max.y(), + print.config().printable_height.value)); + belt_writer->set_belt_back_transform(print.config()); + m_writer = std::move(belt_writer); + + // Per-axis origin snap config. + m_origin_snap[0] = print.config().belt_origin_snap_x.value; + m_origin_snap[1] = print.config().belt_origin_snap_y.value; + m_origin_snap[2] = print.config().belt_origin_snap_z.value; + m_origin_snap_offset[0] = print.config().belt_origin_offset_x.value; + m_origin_snap_offset[1] = print.config().belt_origin_offset_y.value; + m_origin_snap_offset[2] = print.config().belt_origin_offset_z.value; +} + +void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print) +{ + if (!print.config().belt_printer.value) + return; + + file.write_format("; belt_printer_angle = %.1f\n", print.config().belt_printer_angle.value); + // Shear configs + const auto &full_cfg = print.full_print_config(); + file.write_format("; belt_shear_x = %s\n", full_cfg.opt_serialize("belt_shear_x").c_str()); + file.write_format("; belt_shear_x_angle = %.1f\n", print.config().belt_shear_x_angle.value); + file.write_format("; belt_shear_x_from = %s\n", full_cfg.opt_serialize("belt_shear_x_from").c_str()); + file.write_format("; belt_shear_y = %s\n", full_cfg.opt_serialize("belt_shear_y").c_str()); + file.write_format("; belt_shear_y_angle = %.1f\n", print.config().belt_shear_y_angle.value); + file.write_format("; belt_shear_y_from = %s\n", full_cfg.opt_serialize("belt_shear_y_from").c_str()); + file.write_format("; belt_shear_z = %s\n", full_cfg.opt_serialize("belt_shear_z").c_str()); + file.write_format("; belt_shear_z_angle = %.1f\n", print.config().belt_shear_z_angle.value); + file.write_format("; belt_shear_z_from = %s\n", full_cfg.opt_serialize("belt_shear_z_from").c_str()); + // Scale configs + file.write_format("; belt_scale_x = %s\n", full_cfg.opt_serialize("belt_scale_x").c_str()); + file.write_format("; belt_scale_x_angle = %.1f\n", print.config().belt_scale_x_angle.value); + file.write_format("; belt_scale_y = %s\n", full_cfg.opt_serialize("belt_scale_y").c_str()); + file.write_format("; belt_scale_y_angle = %.1f\n", print.config().belt_scale_y_angle.value); + file.write_format("; belt_scale_z = %s\n", full_cfg.opt_serialize("belt_scale_z").c_str()); + file.write_format("; belt_scale_z_angle = %.1f\n", print.config().belt_scale_z_angle.value); + // Pre-slice remap configs + file.write_format("; belt_preslice_remap_x = %s\n", full_cfg.opt_serialize("belt_preslice_remap_x").c_str()); + file.write_format("; belt_preslice_remap_y = %s\n", full_cfg.opt_serialize("belt_preslice_remap_y").c_str()); + file.write_format("; belt_preslice_remap_z = %s\n", full_cfg.opt_serialize("belt_preslice_remap_z").c_str()); +} + +void BeltGCode::on_set_origin(const PrintObject *obj, const Point &inst_shift) +{ + if (!m_origin_snap[0] && !m_origin_snap[1] && !m_origin_snap[2]) + return; + + auto *belt_writer = dynamic_cast(m_writer.get()); + if (!belt_writer) + return; + + // Clear existing snap so to_machine_coords gives raw machine coords for bbox computation. + for (int a = 0; a < 3; ++a) + belt_writer->set_origin_snap(a, false, 0., 0.); + + // Reconstruct the belt pipeline transform for this object. + Transform3d belt = BeltTransformPipeline::build_forward_transform(m_config); + + // Z-shift + double zs = (obj->belt_min_z() < 0.) ? -obj->belt_min_z() : 0.; + if (zs > 0.) { + Transform3d zsh = Transform3d::Identity(); + zsh.matrix()(2, 3) = zs; + belt = zsh * belt; + } + + // Full transform: belt * trafo_centered + Transform3d full = belt * obj->trafo_centered(); + + // Instance shift in slicer space + global Z offset + Vec3d shift(unscale(inst_shift.x()), + unscale(inst_shift.y()), + obj->belt_global_z_offset()); + + // Compute this instance's machine-space bbox min + BoundingBoxf3 bb = obj->model_object()->raw_bounding_box(); + Vec3d mn = bb.min.cast(), mx = bb.max.cast(); + Vec3d inst_min(std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()); + for (int i = 0; i < 8; ++i) { + Vec3d c((i & 1) ? mx.x() : mn.x(), + (i & 2) ? mx.y() : mn.y(), + (i & 4) ? mx.z() : mn.z()); + Vec3d mc = belt_writer->to_machine_coords(full * c + shift); + for (int a = 0; a < 3; ++a) + inst_min[a] = std::min(inst_min[a], mc[a]); + } + + // Update writer snap for each enabled axis + for (int a = 0; a < 3; ++a) + belt_writer->set_origin_snap(a, m_origin_snap[a], m_origin_snap_offset[a], inst_min[a]); +} + +} // namespace Slic3r diff --git a/src/libslic3r/BeltGCode.hpp b/src/libslic3r/BeltGCode.hpp new file mode 100644 index 0000000000..682deb7d65 --- /dev/null +++ b/src/libslic3r/BeltGCode.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "GCode.hpp" + +namespace Slic3r { + +// Belt-printer-specific GCode export. +// +// Inherits from GCode and overrides virtual hooks to: +// - Create a BeltGCodeWriter instead of a plain GCodeWriter +// - Write belt configuration to the G-code header +// - Update per-axis origin snap when switching instances +// - Disable arc fitting (G2/G3 not supported on belt printers) +class BeltGCode : public GCode +{ +protected: + void init_belt_writer(Print &print, bool is_bbl_printers) override; + void write_belt_header(GCodeOutputStream &file, const Print &print) override; + void on_set_origin(const PrintObject *obj, const Point &inst_shift) override; + bool should_disable_arc_fitting() const override { return true; } + +private: + // Per-axis origin snap config (set during init, used in on_set_origin). + bool m_origin_snap[3] = {false, false, false}; + double m_origin_snap_offset[3] = {0., 0., 0.}; +}; + +} // namespace Slic3r diff --git a/src/libslic3r/BeltGCodeWriter.cpp b/src/libslic3r/BeltGCodeWriter.cpp new file mode 100644 index 0000000000..645bf85a5b --- /dev/null +++ b/src/libslic3r/BeltGCodeWriter.cpp @@ -0,0 +1,243 @@ +#include "BeltGCodeWriter.hpp" +#include "Geometry.hpp" + +namespace Slic3r { + +// ---- Belt configuration --------------------------------------------------- + +void BeltGCodeWriter::set_belt_angle(double angle_deg) +{ + m_belt_angle_rad = Geometry::deg2rad(angle_deg); +} + +void BeltGCodeWriter::set_axis_remap(int rx, int ry, int rz) +{ + m_remap_x = rx; + m_remap_y = ry; + m_remap_z = rz; +} + +void BeltGCodeWriter::set_build_volume_max(const Vec3d &max) +{ + m_build_vol_max = max; +} + +void BeltGCodeWriter::set_belt_back_transform(const PrintConfig &config) +{ + m_belt_back_transform.init_from_config(config); +} + +void BeltGCodeWriter::set_origin_snap(int axis, bool enable, double offset, double bbox_min) +{ + if (axis >= 0 && axis < 3) { + m_origin_snap[axis] = enable; + m_origin_offset[axis] = offset; + m_origin_bbox_min[axis] = bbox_min; + } +} + +Vec3d BeltGCodeWriter::to_machine_coords(const Vec3d &pos) const +{ + // Step 1: Undo the shear/scale applied during slicing. + Vec3d p = m_belt_back_transform.apply(pos); + // Step 2: Apply axis remapping for the machine's coordinate convention. + // BeltRemapAxis: 0-2 = +X/+Y/+Z, 3-5 = -X/-Y/-Z, 6-8 = Rev X/Y/Z + auto remap = [this, &p](int r) -> double { + int axis = r % 3; + if (r < 3) return p[axis]; + if (r < 6) return -p[axis]; + return m_build_vol_max[axis] - p[axis]; + }; + Vec3d result = { remap(m_remap_x), remap(m_remap_y), remap(m_remap_z) }; + // Per-axis origin snap: shift so bbox min on each enabled axis = offset. + for (int i = 0; i < 3; ++i) + if (m_origin_snap[i]) + result[i] -= (m_origin_bbox_min[i] - m_origin_offset[i]); + return result; +} + +// ---- Overridden movement methods ------------------------------------------ + +std::string BeltGCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment) +{ + m_pos(0) = point(0); + m_pos(1) = point(1); + + this->set_current_position_clear(true); + Vec2d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset }; + + // Belt printer: transform to machine coordinates (XY travel also needs Z due to YZ rotation) + Vec3d machine = to_machine_coords(Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z())); + + GCodeG1Formatter w; + w.emit_xyz(machine); + auto speed = m_is_first_layer + ? this->config.get_abs_value("initial_layer_travel_speed") : this->config.travel_speed.value; + w.emit_f(speed * 60.0); + w.emit_comment(GCodeWriter::full_gcode_comment, comment); + return w.string(); +} + +std::string BeltGCodeWriter::lazy_lift(LiftType lift_type, bool spiral_vase) +{ + // Belt printer: force NormalLift since SpiralLift and SlopeLift compute + // slope angles that don't account for the YZ coordinate rotation. + return GCodeWriter::lazy_lift(LiftType::NormalLift, spiral_vase); +} + +std::string BeltGCodeWriter::eager_lift(const LiftType type) +{ + // Belt printer: force NormalLift (SpiralLift/SlopeLift don't account for YZ rotation). + return GCodeWriter::eager_lift(LiftType::NormalLift); +} + +std::string BeltGCodeWriter::_travel_to_z(double z, const std::string &comment) +{ + m_pos(2) = z; + + double speed = this->config.travel_speed_z.value; + if (speed == 0.) { + speed = m_is_first_layer ? this->config.get_abs_value("initial_layer_travel_speed") + : this->config.travel_speed.value; + } + + // Belt printer: a Z-only move in slicing frame needs to emit both Y and Z in machine coords. + Vec3d machine = to_machine_coords(Vec3d(m_pos.x() - m_x_offset, m_pos.y() - m_y_offset, z)); + + GCodeG1Formatter w; + w.emit_xyz(machine); + w.emit_f(speed * 60.0); + w.emit_comment(GCodeWriter::full_gcode_comment, comment); + return w.string(); +} + +std::string BeltGCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment, bool force_no_extrusion) +{ + m_pos(0) = point(0); + m_pos(1) = point(1); + if (std::abs(dE) <= std::numeric_limits::epsilon()) + force_no_extrusion = true; + + if (!force_no_extrusion) + filament()->extrude(dE); + + Vec2d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset }; + + // Belt printer: transform and emit XYZ (Y and Z are coupled) + Vec3d machine = to_machine_coords(Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z())); + + GCodeG1Formatter w; + w.emit_xyz(machine); + if (!force_no_extrusion) + w.emit_e(filament()->E()); + w.emit_comment(GCodeWriter::full_gcode_comment, comment); + return w.string(); +} + +std::string BeltGCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment, bool force_no_extrusion) +{ + m_pos = point; + m_lifted = 0; + if (!force_no_extrusion) + filament()->extrude(dE); + + Vec3d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset, point(2) }; + point_on_plate = to_machine_coords(point_on_plate); + + GCodeG1Formatter w; + w.emit_xyz(point_on_plate); + if (!force_no_extrusion) + w.emit_e(filament()->E()); + w.emit_comment(GCodeWriter::full_gcode_comment, comment); + return w.string(); +} + +std::string BeltGCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment, bool force_z) +{ + // Belt-specific override of travel_to_xyz. + // Key differences from base: + // 1. All coordinates go through to_machine_coords() + // 2. Always emit full XYZ (can't split XY and Z due to coupling) + // 3. Lift type forced to NormalLift (handled by lazy_lift/eager_lift overrides) + + Vec3d dest_point = point; + auto travel_speed = + m_is_first_layer ? this->config.get_abs_value("initial_layer_travel_speed") : this->config.travel_speed.value; + + // Handle pending z_hop + if (std::abs(m_to_lift) > EPSILON) { + assert(std::abs(m_lifted) < EPSILON); + if ((!this->is_current_position_clear() || m_pos != dest_point) && + m_to_lift + m_pos(2) > point(2)) { + m_lifted = m_to_lift + m_pos(2) - point(2); + dest_point(2) = m_to_lift + m_pos(2); + } + m_to_lift = 0.; + + std::string slop_move; + Vec3d source = { m_pos(0) - m_x_offset, m_pos(1) - m_y_offset, m_pos(2) }; + Vec3d target = { dest_point(0) - m_x_offset, dest_point(1) - m_y_offset, dest_point(2) }; + Vec3d delta = target - source; + Vec2d delta_no_z = { delta(0), delta(1) }; + + if (delta(2) > 0 && delta_no_z.norm() != 0.0f) { + // Belt: SpiralLift and SlopeLift are disabled (lazy_lift forces NormalLift), + // but handle NormalLift and fallthrough. + if (m_to_lift_type == LiftType::SlopeLift && + this->is_current_position_clear() && + atan2(delta(2), delta_no_z.norm()) < this->filament()->travel_slope()) { + Vec2d temp = delta_no_z.normalized() * delta(2) / tan(this->filament()->travel_slope()); + Vec3d slope_top_point = Vec3d(temp(0), temp(1), delta(2)) + source; + slope_top_point = to_machine_coords(slope_top_point); + GCodeG1Formatter w0; + w0.emit_xyz(slope_top_point); + w0.emit_f(travel_speed * 60.0); + w0.emit_comment(GCodeWriter::full_gcode_comment, comment); + slop_move = w0.string(); + } + else if (m_to_lift_type == LiftType::NormalLift) { + slop_move = _travel_to_z(target.z(), "normal lift Z"); + } + } + + std::string xy_z_move; + { + Vec3d emit_target = to_machine_coords(target); + GCodeG1Formatter w0; + // Belt mode: always emit full XYZ since Y and Z are coupled + w0.emit_xyz(emit_target); + w0.emit_f(travel_speed * 60.0); + w0.emit_comment(GCodeWriter::full_gcode_comment, comment); + xy_z_move = w0.string(); + } + m_pos = dest_point; + this->set_current_position_clear(true); + return slop_move + xy_z_move; + } + else if (!force_z && !this->will_move_z(point(2))) { + double nominal_z = m_pos(2) - m_lifted; + m_lifted -= (point(2) - nominal_z); + if (std::abs(m_lifted) < EPSILON) + m_lifted = 0.; + this->set_current_position_clear(true); + return this->travel_to_xy(to_2d(point)); + } + else { + m_lifted = 0; + } + + Vec3d point_on_plate = { dest_point(0) - m_x_offset, dest_point(1) - m_y_offset, dest_point(2) }; + point_on_plate = to_machine_coords(point_on_plate); + + // Belt mode: always emit full XYZ + GCodeG1Formatter w; + w.emit_xyz(point_on_plate); + w.emit_f(this->config.travel_speed.value * 60.0); + w.emit_comment(GCodeWriter::full_gcode_comment, comment); + + m_pos = dest_point; + this->set_current_position_clear(true); + return w.string(); +} + +} // namespace Slic3r diff --git a/src/libslic3r/BeltGCodeWriter.hpp b/src/libslic3r/BeltGCodeWriter.hpp new file mode 100644 index 0000000000..727135b578 --- /dev/null +++ b/src/libslic3r/BeltGCodeWriter.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include "GCodeWriter.hpp" +#include "GCode/BeltBackTransform.hpp" + +namespace Slic3r { + +// Belt-printer-specific GCode writer. +// +// Inherits from GCodeWriter and overrides movement methods to apply +// coordinate transformation (back-transform, axis remap, origin snap) +// and emit coupled XYZ moves (Y and Z are coupled due to belt tilt). +class BeltGCodeWriter : public GCodeWriter +{ +public: + BeltGCodeWriter() : GCodeWriter() {} + + // Belt configuration + void set_belt_angle(double angle_deg); + bool is_belt_printer() const { return m_belt_angle_rad != 0.; } + void set_axis_remap(int rx, int ry, int rz); + void set_build_volume_max(const Vec3d &max); + void set_belt_back_transform(const PrintConfig &config); + void set_origin_snap(int axis, bool enable, double offset, double bbox_min); + Vec3d to_machine_coords(const Vec3d &pos) const; + + // Overridden movement methods + std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string()) override; + std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string(), bool force_z = false) override; + std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false) override; + std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false) override; + std::string lazy_lift(LiftType lift_type = LiftType::NormalLift, bool spiral_vase = false) override; + std::string eager_lift(const LiftType type) override; + +protected: + std::string _travel_to_z(double z, const std::string &comment) override; + +private: + double m_belt_angle_rad = 0.; + int m_remap_x = 0; + int m_remap_y = 1; + int m_remap_z = 2; + Vec3d m_build_vol_max = Vec3d::Zero(); + BeltBackTransform m_belt_back_transform; + bool m_origin_snap[3] = {false, false, false}; + double m_origin_offset[3] = {0., 0., 0.}; + double m_origin_bbox_min[3] = {0., 0., 0.}; +}; + +} // namespace Slic3r diff --git a/src/libslic3r/BeltSliceStrategy.cpp b/src/libslic3r/BeltSliceStrategy.cpp new file mode 100644 index 0000000000..b3adeec64a --- /dev/null +++ b/src/libslic3r/BeltSliceStrategy.cpp @@ -0,0 +1,62 @@ +#include "BeltSliceStrategy.hpp" + +#include + +namespace Slic3r { + +std::unique_ptr BeltSliceStrategy::create(const PrintConfig &config) +{ + if (!config.belt_printer.value) + return nullptr; + return std::unique_ptr(new BeltSliceStrategy(config)); +} + +BeltSliceStrategy::BeltSliceStrategy(const PrintConfig &config) +{ + m_has_remap = BeltTransformPipeline::has_preslice_remap(config); + if (m_has_remap) + m_pre_remap = BeltTransformPipeline::build_preslice_remap(config); + + m_shear = BeltTransformPipeline::build_shear_matrix(config, &m_has_shear); + m_scale = BeltTransformPipeline::build_scale_matrix(config, &m_has_scale); +} + +void BeltSliceStrategy::apply_to_trafo(Transform3d &trafo, + const ModelVolumePtrs &model_volumes, + double *out_belt_min_z) const +{ + // Step 1: Pre-slice axis remap. + if (m_has_remap) + trafo = m_pre_remap * trafo; + + // Step 2: Shear + scale. + if (m_has_shear || m_has_scale) { + Transform3d belt_xform = Transform3d::Identity(); + belt_xform.linear() = m_scale * m_shear; + trafo = belt_xform * trafo; + } + + // Step 3: Z-shift — detect if mesh clips below build plate after transforms. + if (m_has_remap || m_has_shear || m_has_scale) { + double min_z = std::numeric_limits::max(); + for (const ModelVolume *mv : model_volumes) { + if (!mv->is_model_part()) continue; + for (const stl_vertex &v : mv->mesh().its.vertices) { + Vec3d pt = trafo * v.cast(); + min_z = std::min(min_z, pt.z()); + } + } + double belt_z_shift_val = (min_z < 0. && min_z != std::numeric_limits::max()) ? -min_z : 0.; + BOOST_LOG_TRIVIAL(warning) << "Belt Z-shift: min_z=" << min_z + << " z_shift=" << belt_z_shift_val; + if (belt_z_shift_val > 0.) { + Transform3d z_shift = Transform3d::Identity(); + z_shift.matrix()(2, 3) = belt_z_shift_val; + trafo = z_shift * trafo; + } + if (out_belt_min_z) + *out_belt_min_z = (min_z != std::numeric_limits::max()) ? min_z : 0.; + } +} + +} // namespace Slic3r diff --git a/src/libslic3r/BeltSliceStrategy.hpp b/src/libslic3r/BeltSliceStrategy.hpp new file mode 100644 index 0000000000..bf19243fd2 --- /dev/null +++ b/src/libslic3r/BeltSliceStrategy.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "libslic3r.h" +#include "Point.hpp" +#include "BeltTransform.hpp" +#include "PrintConfig.hpp" +#include "Model.hpp" + +#include +#include + +namespace Slic3r { + +// Belt printer pre-slice transform strategy. +// +// Encapsulates the pre-remap, shear, scale, and Z-shift transforms +// that are applied to model geometry before slicing on belt printers. +// Used by PrintObjectSlice.cpp to isolate belt-specific logic from +// the slicing pipeline. +class BeltSliceStrategy +{ +public: + // Create a strategy if belt_printer is enabled; returns nullptr otherwise. + static std::unique_ptr create(const PrintConfig &config); + + // Apply belt transforms to the slicing trafo. + // Modifies trafo in-place: trafo = z_shift * scale * shear * pre_remap * trafo + // Scans all model_part volumes to detect minimum Z and add z-shift if needed. + // Sets *out_belt_min_z to the minimum Z of the mesh after transforms. + void apply_to_trafo(Transform3d &trafo, + const ModelVolumePtrs &model_volumes, + double *out_belt_min_z) const; + +private: + explicit BeltSliceStrategy(const PrintConfig &config); + + bool m_has_remap = false; + bool m_has_shear = false; + bool m_has_scale = false; + Transform3d m_pre_remap = Transform3d::Identity(); + Matrix3d m_shear = Matrix3d::Identity(); + Matrix3d m_scale = Matrix3d::Identity(); +}; + +} // namespace Slic3r diff --git a/src/libslic3r/BeltTransform.cpp b/src/libslic3r/BeltTransform.cpp new file mode 100644 index 0000000000..db7eea7d80 --- /dev/null +++ b/src/libslic3r/BeltTransform.cpp @@ -0,0 +1,246 @@ +#include "BeltTransform.hpp" +#include "Model.hpp" + +#include + +namespace Slic3r { + +// ---- Matrix builders ------------------------------------------------------ + +Transform3d BeltTransformPipeline::build_preslice_remap(const PrintConfig &config) +{ + Transform3d pre_remap = Transform3d::Identity(); + if (!has_preslice_remap(config)) + return pre_remap; + + int pre_rx = int(config.belt_preslice_remap_x.value); + int pre_ry = int(config.belt_preslice_remap_y.value); + int pre_rz = int(config.belt_preslice_remap_z.value); + + // Each remap value selects a source axis and sign. + auto remap_column = [](int r) -> Vec3d { + int axis = r % 3; + Vec3d col = Vec3d::Zero(); + if (r < 3) col[axis] = 1.0; // +axis + else if (r < 6) col[axis] = -1.0; // -axis + else col[axis] = -1.0; // Rev: max - pos = -(pos - max) + return col; + }; + + Matrix3d remap_lin; + remap_lin.col(0) = remap_column(pre_rx); + remap_lin.col(1) = remap_column(pre_ry); + remap_lin.col(2) = remap_column(pre_rz); + pre_remap.linear() = remap_lin; + + // Translation for Rev modes (needs build volume extents). + if (pre_rx >= 6 || pre_ry >= 6 || pre_rz >= 6) { + BoundingBoxf bbox_bed(config.printable_area.values); + Vec3d vol_max(bbox_bed.max.x(), bbox_bed.max.y(), + config.printable_height.value); + Vec3d remap_trans = Vec3d::Zero(); + auto add_rev = [&](int r, int out) { + if (r >= 6) remap_trans[out] = vol_max[r % 3]; + }; + add_rev(pre_rx, 0); + add_rev(pre_ry, 1); + add_rev(pre_rz, 2); + pre_remap.translation() = remap_trans; + } + + return pre_remap; +} + +Matrix3d BeltTransformPipeline::build_shear_matrix(const PrintConfig &config, bool *has_shear_out) +{ + struct AxisShear { BeltShearMode mode; double angle; int from; }; + AxisShear axes[3] = { + { config.belt_shear_x.value, config.belt_shear_x_angle.value, int(config.belt_shear_x_from.value) }, + { config.belt_shear_y.value, config.belt_shear_y_angle.value, int(config.belt_shear_y_from.value) }, + { config.belt_shear_z.value, config.belt_shear_z_angle.value, int(config.belt_shear_z_from.value) }, + }; + + Matrix3d shear = Matrix3d::Identity(); + bool active = false; + for (int row = 0; row < 3; ++row) { + if (axes[row].mode != BeltShearMode::None) { + double factor = compute_shear_factor(axes[row].mode, axes[row].angle); + if (std::abs(factor) > EPSILON) { + shear(row, axes[row].from) += factor; + active = true; + } + } + } + if (has_shear_out) *has_shear_out = active; + return shear; +} + +Matrix3d BeltTransformPipeline::build_scale_matrix(const PrintConfig &config, bool *has_scale_out) +{ + double sx = compute_scale_factor(config.belt_scale_x.value, config.belt_scale_x_angle.value); + double sy = compute_scale_factor(config.belt_scale_y.value, config.belt_scale_y_angle.value); + double sz = compute_scale_factor(config.belt_scale_z.value, config.belt_scale_z_angle.value); + + bool active = (std::abs(sx - 1.) > EPSILON || + std::abs(sy - 1.) > EPSILON || + std::abs(sz - 1.) > EPSILON); + + Matrix3d scale = Matrix3d::Identity(); + if (active) { + scale(0, 0) = sx; + scale(1, 1) = sy; + scale(2, 2) = sz; + } + if (has_scale_out) *has_scale_out = active; + return scale; +} + +Transform3d BeltTransformPipeline::build_forward_transform(const PrintConfig &config) +{ + Transform3d pre_remap = build_preslice_remap(config); + bool shear_active = false; + Matrix3d shear = build_shear_matrix(config, &shear_active); + bool scale_active = false; + Matrix3d scale = build_scale_matrix(config, &scale_active); + + // Pipeline: scale * shear * pre_remap + Transform3d combined = Transform3d::Identity(); + combined.linear() = scale * shear; + combined = combined * pre_remap; + return combined; +} + +// ---- Bounding box remap --------------------------------------------------- + +BoundingBoxf3 BeltTransformPipeline::remap_bbox(const BoundingBoxf3 &bb, const PrintConfig &config) +{ + int pre_rx = int(config.belt_preslice_remap_x.value); + int pre_ry = int(config.belt_preslice_remap_y.value); + int pre_rz = int(config.belt_preslice_remap_z.value); + + if (pre_rx == int(BeltRemapAxis::PosX) && + pre_ry == int(BeltRemapAxis::PosY) && + pre_rz == int(BeltRemapAxis::PosZ)) + return bb; // Identity remap. + + auto remap_coord = [](int r, const Vec3d &v) -> double { + int axis = r % 3; + if (r < 3) return v[axis]; + return -v[axis]; + }; + + Vec3d mn = bb.min.cast(), mx = bb.max.cast(); + BoundingBoxf3 rbb; + for (int i = 0; i < 8; ++i) { + Vec3d c((i & 1) ? mx.x() : mn.x(), + (i & 2) ? mx.y() : mn.y(), + (i & 4) ? mx.z() : mn.z()); + Vec3d rc(remap_coord(pre_rx, c), remap_coord(pre_ry, c), remap_coord(pre_rz, c)); + if (i == 0) rbb = BoundingBoxf3(rc, rc); + else rbb.merge(rc); + } + return rbb; +} + +BoundingBoxf3 BeltTransformPipeline::remap_bbox(const ModelObject &model_object, const PrintConfig &config) +{ + return remap_bbox(model_object.raw_bounding_box(), config); +} + +// ---- Belt floor parameters ------------------------------------------------ + +// Shared implementation for both PrintConfig and DynamicPrintConfig. +// Template avoids duplicating the math for the two config types. +namespace { + +template +BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl( + const Config &config, const BoundingBoxf3 &bb, double original_height) +{ + BeltTransformPipeline::BeltHeightResult result; + result.object_height = original_height; + + // Extract Z-axis shear/scale config. + BeltShearMode z_shear_mode; + double z_shear_angle; + BeltScaleMode z_scale_mode; + double z_scale_angle; + int z_shear_from; + + if constexpr (std::is_same_v) { + z_shear_mode = config.belt_shear_z.value; + z_shear_angle = config.belt_shear_z_angle.value; + z_scale_mode = config.belt_scale_z.value; + z_scale_angle = config.belt_scale_z_angle.value; + z_shear_from = int(config.belt_shear_z_from.value); + } else { + // DynamicPrintConfig path + auto get_shear = [&](const char *key) { + auto *opt = config.template option>(key); + return opt ? opt->value : BeltShearMode::None; + }; + auto get_scale = [&](const char *key) { + auto *opt = config.template option>(key); + return opt ? opt->value : BeltScaleMode::None; + }; + auto get_float = [&](const char *key) { + auto *opt = config.template option(key); + return opt ? opt->value : 45.0; + }; + auto get_axis = [&](const char *key) { + auto *opt = config.template option>(key); + return opt ? int(opt->value) : 1; + }; + z_shear_mode = get_shear("belt_shear_z"); + z_shear_angle = get_float("belt_shear_z_angle"); + z_scale_mode = get_scale("belt_scale_z"); + z_scale_angle = get_float("belt_scale_z_angle"); + z_shear_from = get_axis("belt_shear_z_from"); + } + + bool has_z_shear = z_shear_mode != BeltShearMode::None; + bool has_z_scale = z_scale_mode != BeltScaleMode::None; + + if (!has_z_shear && !has_z_scale) + return result; + + double shear_factor = has_z_shear + ? BeltTransformPipeline::compute_shear_factor(z_shear_mode, z_shear_angle) : 0.; + double scale_z = BeltTransformPipeline::compute_scale_factor(z_scale_mode, z_scale_angle); + + if (has_z_shear && std::abs(shear_factor) > EPSILON) { + int from = z_shear_from; + double min_rz = std::numeric_limits::max(); + double max_rz = std::numeric_limits::lowest(); + for (double vz : {bb.min.z(), bb.max.z()}) + for (double vs : {bb.min(from), bb.max(from)}) { + double new_z = scale_z * (vz + shear_factor * vs); + min_rz = std::min(min_rz, new_z); + max_rz = std::max(max_rz, new_z); + } + result.object_height = max_rz - min_rz; + result.floor_params.shear_factor = shear_factor; + result.floor_params.from_axis = from; + result.floor_params.z_shift = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.); + } else { + result.object_height = original_height * scale_z; + } + + return result; +} + +} // anonymous namespace + +BeltTransformPipeline::BeltHeightResult BeltTransformPipeline::compute_belt_height_and_floor( + const PrintConfig &config, const BoundingBoxf3 &remapped_bbox, double original_height) +{ + return compute_belt_height_and_floor_impl(config, remapped_bbox, original_height); +} + +BeltTransformPipeline::BeltHeightResult BeltTransformPipeline::compute_belt_height_and_floor( + const DynamicPrintConfig &config, const BoundingBoxf3 &remapped_bbox, double original_height) +{ + return compute_belt_height_and_floor_impl(config, remapped_bbox, original_height); +} + +} // namespace Slic3r diff --git a/src/libslic3r/BeltTransform.hpp b/src/libslic3r/BeltTransform.hpp new file mode 100644 index 0000000000..b9a60473e5 --- /dev/null +++ b/src/libslic3r/BeltTransform.hpp @@ -0,0 +1,146 @@ +#pragma once + +#include "libslic3r.h" +#include "Point.hpp" +#include "BoundingBox.hpp" +#include "PrintConfig.hpp" +#include "Geometry.hpp" + +#include + +namespace Slic3r { + +class ModelObject; + +// Shared belt-printer transform math. +// +// The pre-slice pipeline applied in PrintObjectSlice.cpp is: +// trafo_out = z_shift * scale * shear * pre_remap * trafo_in +// +// This class provides the building blocks so every call site uses the +// same implementation. z_shift is object-dependent (computed from mesh +// vertex bounds) and is NOT included in build_forward_transform(). +class BeltTransformPipeline +{ +public: + // ---- Pure math helpers ------------------------------------------------ + + static double compute_shear_factor(BeltShearMode mode, double angle_deg) + { + double angle_rad = Geometry::deg2rad(angle_deg); + double sin_a = std::sin(angle_rad); + double cos_a = std::cos(angle_rad); + switch (mode) { + case BeltShearMode::PosCot: return (sin_a > EPSILON) ? cos_a / sin_a : 0.; + case BeltShearMode::NegCot: return (sin_a > EPSILON) ? -cos_a / sin_a : 0.; + case BeltShearMode::PosTan: return (cos_a > EPSILON) ? sin_a / cos_a : 0.; + case BeltShearMode::NegTan: return (cos_a > EPSILON) ? -sin_a / cos_a : 0.; + default: return 0.; + } + } + + static double compute_scale_factor(BeltScaleMode mode, double angle_deg) + { + if (mode == BeltScaleMode::None) return 1.; + double angle_rad = Geometry::deg2rad(angle_deg); + double sin_a = std::sin(angle_rad); + double cos_a = std::cos(angle_rad); + switch (mode) { + case BeltScaleMode::InvSin: return (sin_a > EPSILON) ? 1. / sin_a : 1.; + case BeltScaleMode::InvCos: return (cos_a > EPSILON) ? 1. / cos_a : 1.; + case BeltScaleMode::Sin: return sin_a; + case BeltScaleMode::Cos: return cos_a; + default: return 1.; + } + } + + // ---- Identity checks -------------------------------------------------- + + static bool has_preslice_remap(const PrintConfig &config) + { + return int(config.belt_preslice_remap_x.value) != int(BeltRemapAxis::PosX) || + int(config.belt_preslice_remap_y.value) != int(BeltRemapAxis::PosY) || + int(config.belt_preslice_remap_z.value) != int(BeltRemapAxis::PosZ); + } + + // Overload accepting DynamicPrintConfig (used in static slicing_parameters). + static bool has_preslice_remap(const DynamicPrintConfig &config) + { + auto get_int = [&](const char *key) -> int { + auto *opt = config.option>(key); + return opt ? int(opt->value) : 0; + }; + return get_int("belt_preslice_remap_x") != int(BeltRemapAxis::PosX) || + get_int("belt_preslice_remap_y") != int(BeltRemapAxis::PosY) || + get_int("belt_preslice_remap_z") != int(BeltRemapAxis::PosZ); + } + + static bool has_shear(const PrintConfig &config) + { + return config.belt_shear_x.value != BeltShearMode::None || + config.belt_shear_y.value != BeltShearMode::None || + config.belt_shear_z.value != BeltShearMode::None; + } + + static bool has_scale(const PrintConfig &config) + { + double sx = compute_scale_factor(config.belt_scale_x.value, config.belt_scale_x_angle.value); + double sy = compute_scale_factor(config.belt_scale_y.value, config.belt_scale_y_angle.value); + double sz = compute_scale_factor(config.belt_scale_z.value, config.belt_scale_z_angle.value); + return std::abs(sx - 1.) > EPSILON || + std::abs(sy - 1.) > EPSILON || + std::abs(sz - 1.) > EPSILON; + } + + // ---- Matrix builders -------------------------------------------------- + + // Build the pre-slice axis remap transform (includes Rev-mode translation). + static Transform3d build_preslice_remap(const PrintConfig &config); + + // Build the 3x3 shear matrix. Returns Identity if no shear is active. + // Also sets has_shear_out if non-null. + static Matrix3d build_shear_matrix(const PrintConfig &config, bool *has_shear_out = nullptr); + + // Build the 3x3 diagonal scale matrix. Returns Identity if no scale. + // Also sets has_scale_out if non-null. + static Matrix3d build_scale_matrix(const PrintConfig &config, bool *has_scale_out = nullptr); + + // Combined forward transform: scale * shear * pre_remap. + // Does NOT include the per-object Z-shift. + static Transform3d build_forward_transform(const PrintConfig &config); + + // ---- Bounding box remap ----------------------------------------------- + + // Remap a bounding box through the pre-slice axis remap. + // Returns the original bbox if remap is identity. + static BoundingBoxf3 remap_bbox(const BoundingBoxf3 &bb, const PrintConfig &config); + static BoundingBoxf3 remap_bbox(const ModelObject &model_object, const PrintConfig &config); + + // ---- Belt floor parameters -------------------------------------------- + + struct BeltFloorParams { + double shear_factor = 0.0; + int from_axis = 1; + double z_shift = 0.0; + }; + + // Result of computing belt height + floor params. + struct BeltHeightResult { + double object_height; // Effective object height after shear/scale + BeltFloorParams floor_params; + }; + + // Compute effective object height and belt floor parameters from config + // and pre-remapped bounding box. original_height is the input height + // (bb.size().z() or model_object.max_z()). + static BeltHeightResult compute_belt_height_and_floor( + const PrintConfig &config, const BoundingBoxf3 &remapped_bbox, + double original_height); + + // Overload for DynamicPrintConfig (used by static slicing_parameters). + static BeltHeightResult compute_belt_height_and_floor( + const DynamicPrintConfig &config, const BoundingBoxf3 &remapped_bbox, + double original_height); +}; + +} // namespace Slic3r diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index c2d92ee800..a3e813b1da 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -79,6 +79,14 @@ set(lisbslic3r_sources BoundingBox.hpp BridgeDetector.cpp BridgeDetector.hpp + BeltGCode.cpp + BeltGCode.hpp + BeltGCodeWriter.cpp + BeltGCodeWriter.hpp + BeltSliceStrategy.cpp + BeltSliceStrategy.hpp + BeltTransform.cpp + BeltTransform.hpp Brim.cpp BrimEarsPoint.hpp Brim.hpp @@ -417,6 +425,8 @@ set(lisbslic3r_sources SlicingAdaptive.hpp Slicing.cpp Slicing.hpp + Support/BeltFloorContext.cpp + Support/BeltFloorContext.hpp Support/SupportCommon.cpp Support/SupportCommon.hpp Support/SupportLayer.hpp diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index ac04c6dd5b..a330fad689 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -8,6 +8,7 @@ #include "Exception.hpp" #include "ExtrusionEntity.hpp" #include "EdgeGrid.hpp" +#include "BeltTransform.hpp" #include "Geometry.hpp" #include "Geometry/ConvexHull.hpp" #include "GCode/PrintExtents.hpp" @@ -795,7 +796,7 @@ static std::vector get_path_of_change_filament(const Print& print) //BBS: if needed, write the gcode_label_objects_end then priming tower, if the retract, didn't did it. std::string object_end_label_temp; - gcodegen.m_writer.add_object_end_labels(object_end_label_temp); + gcodegen.m_writer->add_object_end_labels(object_end_label_temp); // Process the custom change_filament_gcode. If it is empty, provide a simple Tn command to change the filament. // Otherwise, leave control to the user completely. @@ -839,7 +840,7 @@ static std::vector get_path_of_change_filament(const Print& print) // config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); // BBS { - GCodeWriter& gcode_writer = gcodegen.m_writer; + GCodeWriter& gcode_writer = *gcodegen.m_writer; FullPrintConfig& full_config = gcodegen.m_config; // set volumetric speed of outer wall ,ignore per obejct,just use default setting @@ -1573,8 +1574,8 @@ static std::vector get_path_of_change_filament(const Print& print) const std::vector ColorPrintColors::Colors = { "#C0392B", "#E67E22", "#F1C40F", "#27AE60", "#1ABC9C", "#2980B9", "#9B59B6" }; -#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer.filament()->extruder_id()) -#define FILAMENT_CONFIG(OPT) m_config.OPT.get_at(m_writer.filament()->id()) +#define EXTRUDER_CONFIG(OPT) m_config.OPT.get_at(m_writer->filament()->extruder_id()) +#define FILAMENT_CONFIG(OPT) m_config.OPT.get_at(m_writer->filament()->id()) void GCode::PlaceholderParserIntegration::reset() { @@ -1687,7 +1688,7 @@ void GCode::PlaceholderParserIntegration::validate_output_vector_variables() // Collect pairs of object_layer + support_layer sorted by print_z. // object_layer & support_layer are considered to be on the same print_z, if they are not further than EPSILON. -std::vector GCode::collect_layers_to_print(const PrintObject& object) +std::vector GCode::collect_layers_to_print(const PrintObject& object, bool skip_empty_first_layer) { std::vector layers_to_print; layers_to_print.reserve(object.layers().size() + object.support_layers().size()); @@ -1753,8 +1754,7 @@ std::vector GCode::collect_layers_to_print(const PrintObjec // an error. In global shear mode the object may also start above // Z=0 on the tilted belt surface. if (layers_to_print.size() == 1u) { - bool skip_empty_check = object.print()->config().belt_printer.value; - if (!has_extrusions && !skip_empty_check) + if (!has_extrusions && !skip_empty_first_layer) throw Slic3r::SlicingError(_(L("One object has an empty first layer and can't be printed. Please Cut the bottom or enable supports.")), object.id().id); } @@ -1827,7 +1827,7 @@ std::vector>> GCode::collec for (size_t i = 0; i < print.objects().size(); ++i) { try { - per_object[i] = collect_layers_to_print(*print.objects()[i]); + per_object[i] = collect_layers_to_print(*print.objects()[i], print.config().belt_printer.value); } catch (const Slic3r::SlicingError &e) { errors.push_back(e); continue; @@ -2016,7 +2016,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu BOOST_LOG_TRIVIAL(info) << boost::format("Will export G-code to %1% soon")%path; GCodeProcessor::s_IsBBLPrinter = print->is_BBL_printer(); - m_writer.set_is_bbl_machine(print->is_BBL_printer()); + m_writer->set_is_bbl_machine(print->is_BBL_printer()); print->set_started(psGCodeExport); // check if any custom gcode contains keywords used by the gcode processor to @@ -2113,7 +2113,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.result().support_traditional_timelapse = m_support_traditional_timelapse; bool activate_long_retraction_when_cut = false; - for (const auto& filament : m_writer.extruders()) + for (const auto& filament : m_writer->extruders()) activate_long_retraction_when_cut |= ( m_config.long_retractions_when_cut.get_at(filament.id()) && m_config.retraction_distances_when_cut.get_at(filament.id()) > 0 @@ -2144,7 +2144,7 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu m_processor.finalize(true); // DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics); - DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics, print->config()); + DoExport::update_print_estimated_stats(m_processor, m_writer->extruders(), print->m_print_statistics, print->config()); if (result != nullptr) { *result = std::move(m_processor.extract_result()); // set the filename to the correct value @@ -2404,7 +2404,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato PROFILE_FUNC(); m_print = &print; - m_timelapse_pos_picker.init(&print,m_writer.get_xy_offset().cast()); + m_timelapse_pos_picker.init(&print,m_writer->get_xy_offset().cast()); // modifies m_silent_time_estimator_enabled DoExport::init_gcode_processor(print.config(), m_processor, m_silent_time_estimator_enabled); @@ -2420,29 +2420,10 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_fan_mover.release(); - m_writer.set_is_bbl_machine(is_bbl_printers); + m_writer->set_is_bbl_machine(is_bbl_printers); - // Belt printer: initialize coordinate transformation and axis remap on the writer. - if (print.config().belt_printer.value) { - m_writer.set_belt_angle(print.config().belt_printer_angle.value); - m_writer.set_axis_remap( - int(print.config().belt_gcode_remap_x.value), - int(print.config().belt_gcode_remap_y.value), - int(print.config().belt_gcode_remap_z.value)); - // Build volume extents for Rev remap mode. - BoundingBoxf bbox_bed(print.config().printable_area.values); - m_writer.set_build_volume_max(Vec3d(bbox_bed.max.x(), bbox_bed.max.y(), print.config().printable_height.value)); - // Initialize the back-transform that undoes slicing shear/scale. - m_writer.set_belt_back_transform(print.config()); - // Per-axis origin snap: store config; actual snap is computed - // per-instance in update_origin_snap() called from set_origin(). - m_origin_snap[0] = print.config().belt_origin_snap_x.value; - m_origin_snap[1] = print.config().belt_origin_snap_y.value; - m_origin_snap[2] = print.config().belt_origin_snap_z.value; - m_origin_snap_offset[0] = print.config().belt_origin_offset_x.value; - m_origin_snap_offset[1] = print.config().belt_origin_offset_y.value; - m_origin_snap_offset[2] = print.config().belt_origin_offset_z.value; - } + // Belt printer: initialize belt-specific writer via virtual hook. + this->init_belt_writer(print, is_bbl_printers); // How many times will be change_layer() called? // change_layer() in turn increments the progress bar status. @@ -2533,31 +2514,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write_format("; HEADER_BLOCK_START\n"); // Write information on the generator. file.write_format("; generated by %s on %s\n", Slic3r::header_slic3r_generated().c_str(), Slic3r::Utils::local_timestamp().c_str()); - // Belt printer: embed angle and transform configs in header for G-code processor detection. - if (print.config().belt_printer.value) { - file.write_format("; belt_printer_angle = %.1f\n", print.config().belt_printer_angle.value); - // Shear configs - const auto &full_cfg = print.full_print_config(); - file.write_format("; belt_shear_x = %s\n", full_cfg.opt_serialize("belt_shear_x").c_str()); - file.write_format("; belt_shear_x_angle = %.1f\n", print.config().belt_shear_x_angle.value); - file.write_format("; belt_shear_x_from = %s\n", full_cfg.opt_serialize("belt_shear_x_from").c_str()); - file.write_format("; belt_shear_y = %s\n", full_cfg.opt_serialize("belt_shear_y").c_str()); - file.write_format("; belt_shear_y_angle = %.1f\n", print.config().belt_shear_y_angle.value); - file.write_format("; belt_shear_y_from = %s\n", full_cfg.opt_serialize("belt_shear_y_from").c_str()); - file.write_format("; belt_shear_z = %s\n", full_cfg.opt_serialize("belt_shear_z").c_str()); - file.write_format("; belt_shear_z_angle = %.1f\n", print.config().belt_shear_z_angle.value); - file.write_format("; belt_shear_z_from = %s\n", full_cfg.opt_serialize("belt_shear_z_from").c_str()); - // Scale configs - file.write_format("; belt_scale_x = %s\n", full_cfg.opt_serialize("belt_scale_x").c_str()); - file.write_format("; belt_scale_x_angle = %.1f\n", print.config().belt_scale_x_angle.value); - file.write_format("; belt_scale_y = %s\n", full_cfg.opt_serialize("belt_scale_y").c_str()); - file.write_format("; belt_scale_y_angle = %.1f\n", print.config().belt_scale_y_angle.value); - file.write_format("; belt_scale_z = %s\n", full_cfg.opt_serialize("belt_scale_z").c_str()); - file.write_format("; belt_scale_z_angle = %.1f\n", print.config().belt_scale_z_angle.value); - file.write_format("; belt_preslice_remap_x = %s\n", full_cfg.opt_serialize("belt_preslice_remap_x").c_str()); - file.write_format("; belt_preslice_remap_y = %s\n", full_cfg.opt_serialize("belt_preslice_remap_y").c_str()); - file.write_format("; belt_preslice_remap_z = %s\n", full_cfg.opt_serialize("belt_preslice_remap_z").c_str()); - } + // Belt printer: embed angle and transform configs in header via virtual hook. + this->write_belt_header(file, print); if (is_bbl_printers) file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Estimated_Printing_Time_Placeholder).c_str()); //BBS: total layer number @@ -2804,13 +2762,13 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Disable fan. if (m_config.auxiliary_fan.value && print.config().close_fan_the_first_x_layers.get_at(initial_extruder_id)) { - file.write(m_writer.set_fan(0)); + file.write(m_writer->set_fan(0)); //BBS: disable additional fan - file.write(m_writer.set_additional_fan(0)); + file.write(m_writer->set_additional_fan(0)); } // Update output variables after the extruders were initialized. - m_placeholder_parser_integration.init(m_writer); + m_placeholder_parser_integration.init(*m_writer); // Let the start-up script prime the 1st printing tool. @@ -2883,7 +2841,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato { BoundingBoxf bbox_bed(print.config().printable_area.values); - Vec2f plate_offset = m_writer.get_xy_offset(); + Vec2f plate_offset = m_writer->get_xy_offset(); this->placeholder_parser().set("print_bed_min", new ConfigOptionFloats({ bbox_bed.min.x(), bbox_bed.min.y()})); this->placeholder_parser().set("print_bed_max", new ConfigOptionFloats({ bbox_bed.max.x(), bbox_bed.max.y()})); this->placeholder_parser().set("print_bed_size", new ConfigOptionFloats({ bbox_bed.size().x(), bbox_bed.size().y() })); @@ -2985,7 +2943,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } bool activate_chamber_temp_control = false; auto max_chamber_temp = 0; - for (const auto &extruder : m_writer.extruders()) { + for (const auto &extruder : m_writer->extruders()) { activate_chamber_temp_control |= m_config.activate_chamber_temp_control.get_at(extruder.id()); max_chamber_temp = std::max(max_chamber_temp, m_config.chamber_temperature.get_at(extruder.id())); } @@ -2993,7 +2951,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato BedType curr_bed_type = m_config.curr_bed_type; int min_temperature_vitrification = std::numeric_limits::max(); - for (const auto& extruder : m_writer.extruders()) + for (const auto& extruder : m_writer->extruders()) min_temperature_vitrification = std::min(min_temperature_vitrification, m_config.temperature_vitrification.get_at(extruder.id())); @@ -3107,18 +3065,18 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato if (activate_chamber_temp_control && max_chamber_temp > 0){ int temp_out =0; if(!custom_gcode_sets_temperature(machine_start_gcode,141,191,false,temp_out)) - file.write(m_writer.set_chamber_temperature(max_chamber_temp, true)); // set chamber_temperature + file.write(m_writer->set_chamber_temperature(max_chamber_temp, true)); // set chamber_temperature } // Write the custom start G-code file.writeln(machine_start_gcode); //BBS: gcode writer doesn't know where the real position of extruder is after inserting custom gcode - m_writer.set_current_position_clear(false); + m_writer->set_current_position_clear(false); m_start_gcode_filament = GCodeProcessor::get_gcode_last_filament(machine_start_gcode); if (is_bbl_printers) { - m_writer.init_extruder(initial_non_support_extruder_id); + m_writer->init_extruder(initial_non_support_extruder_id); // add the missing filament start gcode in machine start gcode { DynamicConfig config; @@ -3131,7 +3089,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } // Orca: add missing PA settings for initial filament if (m_config.enable_pressure_advance.get_at(initial_non_support_extruder_id)) { - file.write(m_writer.set_pressure_advance(m_config.pressure_advance.get_at(initial_non_support_extruder_id))); + file.write(m_writer->set_pressure_advance(m_config.pressure_advance.get_at(initial_non_support_extruder_id))); // Orca: Adaptive PA // Reset Adaptive PA processor last PA value m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(initial_non_support_extruder_id)); @@ -3157,14 +3115,14 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Orca: when activate_air_filtration is set on any extruder, find and set the highest during_print_exhaust_fan_speed bool activate_air_filtration = false; int during_print_exhaust_fan_speed = 0; - for (const auto &extruder : m_writer.extruders()) { + for (const auto &extruder : m_writer->extruders()) { activate_air_filtration |= m_config.activate_air_filtration.get_at(extruder.id()); if (m_config.activate_air_filtration.get_at(extruder.id())) during_print_exhaust_fan_speed = std::max(during_print_exhaust_fan_speed, m_config.during_print_exhaust_fan_speed.get_at(extruder.id())); } if (activate_air_filtration) - file.write(m_writer.set_exhaust_fan(during_print_exhaust_fan_speed, true)); + file.write(m_writer->set_exhaust_fan(during_print_exhaust_fan_speed, true)); print.throw_if_canceled(); @@ -3180,7 +3138,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_seam_placer.init(print, throw_if_canceled_func); // BBS: get path for change filament - if (m_writer.multiple_extruders) { + if (m_writer->multiple_extruders) { std::vector points = get_path_of_change_filament(print); if (points.size() == 3) { travel_point_1 = points[0]; @@ -3212,12 +3170,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato std::string gcode; gcode += ";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change) + "\n"; if ((print.default_object_config().outer_wall_acceleration.value > 0 && print.default_object_config().outer_wall_acceleration.value > 0)) { - gcode += m_writer.set_print_acceleration((unsigned int)floor(print.default_object_config().outer_wall_acceleration.value + 0.5)); + gcode += m_writer->set_print_acceleration((unsigned int)floor(print.default_object_config().outer_wall_acceleration.value + 0.5)); } if (print.default_object_config().outer_wall_jerk.value > 0) { double jerk = print.default_object_config().outer_wall_jerk.value; - gcode += m_writer.set_jerk_xy(jerk); + gcode += m_writer->set_jerk_xy(jerk); } auto params = print.calib_params(); @@ -3261,8 +3219,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } print.throw_if_canceled(); this->set_origin(unscale((*print_object_instance_sequential_active)->shift)); - this->update_origin_snap((*print_object_instance_sequential_active)->print_object, - (*print_object_instance_sequential_active)->shift); + this->on_set_origin((*print_object_instance_sequential_active)->print_object, + (*print_object_instance_sequential_active)->shift); // BBS: prime extruder if extruder change happens before this object instance bool prime_extruder = false; @@ -3272,7 +3230,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_enable_cooling_markers = false; // we're not filtering these moves through CoolingBuffer m_avoid_crossing_perimeters.use_external_mp_once(); // BBS. change tool before moving to origin point. - if (m_writer.need_toolchange(initial_extruder_id)) { + if (m_writer->need_toolchange(initial_extruder_id)) { const PrintObjectConfig& object_config = object.config(); coordf_t initial_layer_print_height = print.config().initial_layer_print_height.value; @@ -3285,7 +3243,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato } else { file.write(this->retract()); } - file.write(m_writer.travel_to_z(m_max_layer_z + m_writer.config.z_hop.get_at(initial_extruder_id))); + file.write(m_writer->travel_to_z(m_max_layer_z + m_writer->config.z_hop.get_at(initial_extruder_id))); file.write(this->travel_to(Point(0, 0), erNone, "move to origin position for next object")); m_enable_cooling_markers = true; // Disable motion planner when traveling to first object point. @@ -3308,7 +3266,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // and export G-code into file. tool_ordering.cal_most_used_extruder(print.config()); m_printed_objects.emplace_back(&object); - this->process_layers(print, tool_ordering, collect_layers_to_print(object), *print_object_instance_sequential_active - object.instances().data(), file, + this->process_layers(print, tool_ordering, collect_layers_to_print(object, print.config().belt_printer.value), *print_object_instance_sequential_active - object.instances().data(), file, prime_extruder); { // save the flush statitics stored in tool ordering by object @@ -3325,7 +3283,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato { const auto plr_mode = print.config().enable_power_loss_recovery.value; if (m_second_layer_things_done && plr_mode == PowerLossRecoveryMode::Enable) { - file.write(m_writer.enable_power_loss_recovery(PowerLossRecoveryMode::Disable)); + file.write(m_writer->enable_power_loss_recovery(PowerLossRecoveryMode::Disable)); } } ++ finished_objects; @@ -3346,7 +3304,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato m_wipe_tower->set_wipe_tower_bbx(print.get_wipe_tower_bbx()); m_wipe_tower->set_rib_offset(print.get_rib_offset()); //BBS - file.write(m_writer.travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height")); + file.write(m_writer->travel_to_z(initial_layer_print_height + m_config.z_offset.value, "Move to the first layer height")); if (wipe_tower_type == WipeTowerType::Type2 && print.config().single_extruder_multi_material_priming) { file.write(m_wipe_tower->prime(*this)); @@ -3405,7 +3363,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // Orca: disable power loss recovery if (m_second_layer_things_done && print.config().enable_power_loss_recovery.value == PowerLossRecoveryMode::Enable) { - file.write(m_writer.enable_power_loss_recovery(PowerLossRecoveryMode::Disable)); + file.write(m_writer->enable_power_loss_recovery(PowerLossRecoveryMode::Disable)); } if (m_wipe_tower) // Purge the extruder, pull out the active filament. @@ -3419,14 +3377,14 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato // if needed, write the gcode_label_objects_end { std::string gcode; - m_writer.add_object_change_labels(gcode); + m_writer->add_object_change_labels(gcode); file.write(gcode); } - file.write(m_writer.set_fan(0)); + file.write(m_writer->set_fan(0)); //BBS: make sure the additional fan is closed when end if(m_config.auxiliary_fan.value) - file.write(m_writer.set_additional_fan(0)); + file.write(m_writer->set_additional_fan(0)); if (is_bbl_printers) { //BBS: close spaghetti detector //Note: M981 is also used to tell xcam the last layer is finished, so we need always send it even if spaghetti option is disabled. @@ -3442,12 +3400,12 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); //BBS - config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position()(2) - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer->get_position()(2) - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); if (print.config().single_extruder_multi_material) { // Process the filament_end_gcode for the active filament only. - int extruder_id = m_writer.filament()->id(); + int extruder_id = m_writer->filament()->id(); config.set_key_value("filament_extruder_id", new ConfigOptionInt(extruder_id)); file.writeln(this->placeholder_parser_process("filament_end_gcode", print.config().filament_end_gcode.get_at(extruder_id), extruder_id, &config)); } else { @@ -3457,20 +3415,20 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.writeln(this->placeholder_parser_process("filament_end_gcode", end_gcode, extruder_id, &config)); } } - file.writeln(this->placeholder_parser_process("machine_end_gcode", print.config().machine_end_gcode, m_writer.filament()->id(), &config)); + file.writeln(this->placeholder_parser_process("machine_end_gcode", print.config().machine_end_gcode, m_writer->filament()->id(), &config)); } - file.write(m_writer.update_progress(m_layer_count, m_layer_count, true)); // 100% - file.write(m_writer.postamble()); + file.write(m_writer->update_progress(m_layer_count, m_layer_count, true)); // 100% + file.write(m_writer->postamble()); if (activate_chamber_temp_control && max_chamber_temp > 0) - file.write(m_writer.set_chamber_temperature(0, false)); //close chamber_temperature + file.write(m_writer->set_chamber_temperature(0, false)); //close chamber_temperature if (activate_air_filtration) { int complete_print_exhaust_fan_speed = 0; - for (const auto& extruder : m_writer.extruders()) + for (const auto& extruder : m_writer->extruders()) if (m_config.activate_air_filtration.get_at(extruder.id())) complete_print_exhaust_fan_speed = std::max(complete_print_exhaust_fan_speed, m_config.complete_print_exhaust_fan_speed.get_at(extruder.id())); - file.write(m_writer.set_exhaust_fan(complete_print_exhaust_fan_speed, true)); + file.write(m_writer->set_exhaust_fan(complete_print_exhaust_fan_speed, true)); } // adds tags for time estimators file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str()); @@ -3482,7 +3440,7 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato file.write(DoExport::update_print_stats_and_format_filament_stats( // Const inputs has_wipe_tower, print.wipe_tower_data(), - m_writer.extruders(), + m_writer->extruders(), // Modifies print.m_print_statistics)); print.m_print_statistics.initial_tool = initial_extruder_id; @@ -3656,7 +3614,7 @@ void GCode::process_layers( ); const auto fan_mover = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string { + [&fan_mover = this->m_fan_mover, &config = this->config(), &writer = *this->m_writer](std::string in)->std::string { CNumericLocalesSetter locales_setter; @@ -3756,7 +3714,7 @@ void GCode::process_layers( ); const auto fan_mover = tbb::make_filter(slic3r_tbb_filtermode::serial_in_order, - [&fan_mover = this->m_fan_mover, &config = this->config(), &writer = this->m_writer](std::string in)->std::string { + [&fan_mover = this->m_fan_mover, &config = this->config(), &writer = *this->m_writer](std::string in)->std::string { if (config.fan_speedup_time.value != 0 || config.fan_kickstart.value > 0) { if (fan_mover.get() == nullptr) @@ -3824,21 +3782,21 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std PlaceholderParserIntegration &ppi = m_placeholder_parser_integration; try { - ppi.update_from_gcodewriter(m_writer); + ppi.update_from_gcodewriter(*m_writer); std::string output = ppi.parser.process(templ, current_filament_id, config_override, &ppi.output_config, &ppi.context); ppi.validate_output_vector_variables(); if (const std::vector &pos = ppi.opt_position->values; ppi.position != pos) { // Update G-code writer. - m_writer.set_position({ pos[0], pos[1], pos[2] }); + m_writer->set_position({ pos[0], pos[1], pos[2] }); this->set_last_pos(this->gcode_to_point({ pos[0], pos[1] })); } - for (const Extruder &e : m_writer.extruders()) { + for (const Extruder &e : m_writer->extruders()) { unsigned int eid = e.id(); assert(eid < ppi.num_extruders); if ( eid < ppi.num_extruders) { - if (! m_writer.config.use_relative_e_distances && ! is_approx(ppi.e_position[eid], ppi.opt_e_position->values[eid])) + if (! m_writer->config.use_relative_e_distances && ! is_approx(ppi.e_position[eid], ppi.opt_e_position->values[eid])) const_cast(e).set_position(ppi.opt_e_position->values[eid]); if (! is_approx(ppi.e_retracted[eid], ppi.opt_e_retracted->values[eid]) || ! is_approx(ppi.e_restart_extra[eid], ppi.opt_e_restart_extra->values[eid])) @@ -3963,9 +3921,9 @@ void GCode::_print_first_layer_bed_temperature(GCodeOutputStream &file, Print &p temp = temp_by_gcode; #endif - // Always call m_writer.set_bed_temperature() so it will set the internal "current" state of the bed temp as if + // Always call m_writer->set_bed_temperature() so it will set the internal "current" state of the bed temp as if // the custom start G-code emited these. - std::string set_temp_gcode = m_writer.set_bed_temperature(bed_temp, wait); + std::string set_temp_gcode = m_writer->set_bed_temperature(bed_temp, wait); if (! temp_set_by_gcode) file.write(set_temp_gcode); } @@ -3985,14 +3943,14 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr int temp = print.config().nozzle_temperature_initial_layer.get_at(first_printing_extruder_id); if (temp_by_gcode >= 0 && temp_by_gcode < 1000) temp = temp_by_gcode; - m_writer.set_temperature(temp, wait, first_printing_extruder_id); + m_writer->set_temperature(temp, wait, first_printing_extruder_id); } else { // Custom G-code does not set the extruder temperature. Do it now. if (print.config().single_extruder_multi_material.value) { // Set temperature of the first printing extruder only. int temp = print.config().nozzle_temperature_initial_layer.get_at(first_printing_extruder_id); if (temp > 0) - file.write(m_writer.set_temperature(temp, wait, first_printing_extruder_id)); + file.write(m_writer->set_temperature(temp, wait, first_printing_extruder_id)); } else { // Set temperatures of all the printing extruders. for (unsigned int tool_id : print.extruders()) { @@ -4004,7 +3962,7 @@ void GCode::_print_first_layer_extruder_temperatures(GCodeOutputStream &file, Pr temp = print.config().idle_temperature.get_at(tool_id); } if (temp > 0) - file.write(m_writer.set_temperature(temp, wait, tool_id)); + file.write(m_writer->set_temperature(temp, wait, tool_id)); } } } @@ -4427,7 +4385,7 @@ LayerResult GCode::process_layer( //support is attached above the object, and support layers has independent layer height, then the lowest support //interface layer id is 0. bool first_layer = (layer.id() == 0 && abs(layer.bottom_z()) < EPSILON); - m_writer.set_is_first_layer(first_layer); + m_writer->set_is_first_layer(first_layer); unsigned int first_extruder_id = layer_tools.extruders.front(); // Initialize config with the 1st object to be printed at this layer. @@ -4477,7 +4435,7 @@ LayerResult GCode::process_layer( config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); gcode += this->placeholder_parser_process("before_layer_change_gcode", - print.config().before_layer_change_gcode.value, m_writer.filament()->id(), &config) + print.config().before_layer_change_gcode.value, m_writer->filament()->id(), &config) + "\n"; } @@ -4512,7 +4470,7 @@ LayerResult GCode::process_layer( config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); gcode += this->placeholder_parser_process("timelapse_gcode", - print.config().time_lapse_gcode.value, m_writer.filament()->id(), &config) + print.config().time_lapse_gcode.value, m_writer->filament()->id(), &config) + "\n"; } @@ -4522,7 +4480,7 @@ LayerResult GCode::process_layer( config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); gcode += this->placeholder_parser_process("layer_change_gcode", - print.config().layer_change_gcode.value, m_writer.filament()->id(), &config) + print.config().layer_change_gcode.value, m_writer->filament()->id(), &config) + "\n"; config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); } @@ -4562,7 +4520,7 @@ LayerResult GCode::process_layer( case CalibMode::Calib_Input_shaping_freq: { if (m_layer_index == 1){ gcode += writer().set_input_shaping('A', print.calib_params().start, 0.f, print.calib_params().shaper_type); - if (m_writer.get_gcode_flavor() == gcfKlipper) { + if (m_writer->get_gcode_flavor() == gcfKlipper) { // Disable minimum cruise ratio to ensure consistent motion for calibration gcode += "SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0\n"; } @@ -4578,7 +4536,7 @@ LayerResult GCode::process_layer( } case CalibMode::Calib_Input_shaping_damp: { if (m_layer_index == 1){ - if (m_writer.get_gcode_flavor() == gcfKlipper) { + if (m_writer->get_gcode_flavor() == gcfKlipper) { // Disable minimum cruise ratio to ensure consistent motion for calibration gcode += "SET_VELOCITY_LIMIT MINIMUM_CRUISE_RATIO=0\n"; } @@ -4590,7 +4548,7 @@ LayerResult GCode::process_layer( break; } case CalibMode::Calib_Cornering: { - if (m_writer.get_gcode_flavor() == gcfMarlinFirmware && + if (m_writer->get_gcode_flavor() == gcfMarlinFirmware && !m_config.machine_max_junction_deviation.values.empty() && m_config.machine_max_junction_deviation.values.front() > 0) { gcode += writer().set_junction_deviation(this->interpolate_value_across_layers(print.calib_params().start, print.calib_params().end)); @@ -4605,22 +4563,22 @@ LayerResult GCode::process_layer( if (first_layer) { // Orca: we don't need to optimize the Klipper as only set once if (m_config.default_acceleration.value > 0 && m_config.initial_layer_acceleration.value > 0) { - gcode += m_writer.set_print_acceleration((unsigned int)floor(m_config.initial_layer_acceleration.value + 0.5)); + gcode += m_writer->set_print_acceleration((unsigned int)floor(m_config.initial_layer_acceleration.value + 0.5)); } if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) { - gcode += m_writer.set_jerk_xy(m_config.initial_layer_jerk.value); + gcode += m_writer->set_jerk_xy(m_config.initial_layer_jerk.value); } - if (m_writer.get_gcode_flavor() == gcfMarlinFirmware && m_config.default_junction_deviation.value > 0) { - gcode += m_writer.set_junction_deviation(m_config.default_junction_deviation.value); + if (m_writer->get_gcode_flavor() == gcfMarlinFirmware && m_config.default_junction_deviation.value > 0) { + gcode += m_writer->set_junction_deviation(m_config.default_junction_deviation.value); } } if (!first_layer && !m_second_layer_things_done) { // Orca: set power loss recovery const auto plr_mode = print.config().enable_power_loss_recovery.value; - gcode += m_writer.enable_power_loss_recovery(plr_mode); + gcode += m_writer->enable_power_loss_recovery(plr_mode); if (print.is_BBL_printer()) { // BBS: open first layer inspection at second layer @@ -4635,23 +4593,23 @@ LayerResult GCode::process_layer( // Reset acceleration at sencond layer // Orca: only set once, don't need to call set_accel_and_jerk if (m_config.default_acceleration.value > 0 && m_config.initial_layer_acceleration.value > 0) { - gcode += m_writer.set_print_acceleration((unsigned int) floor(m_config.default_acceleration.value + 0.5)); + gcode += m_writer->set_print_acceleration((unsigned int) floor(m_config.default_acceleration.value + 0.5)); } if (m_config.default_jerk.value > 0 && m_config.initial_layer_jerk.value > 0) { - gcode += m_writer.set_jerk_xy(m_config.default_jerk.value); + gcode += m_writer->set_jerk_xy(m_config.default_jerk.value); } // Transition from 1st to 2nd layer. Adjust nozzle temperatures as prescribed by the nozzle dependent // nozzle_temperature_initial_layer vs. temperature settings. - for (const Extruder& extruder : m_writer.extruders()) { + for (const Extruder& extruder : m_writer->extruders()) { if ((print.config().single_extruder_multi_material.value || m_ooze_prevention.enable) && - extruder.id() != m_writer.filament()->id()) + extruder.id() != m_writer->filament()->id()) // In single extruder multi material mode, set the temperature for the current extruder only. continue; int temperature = print.config().nozzle_temperature.get_at(extruder.id()); if (temperature > 0 && temperature != print.config().nozzle_temperature_initial_layer.get_at(extruder.id())) - gcode += m_writer.set_temperature(temperature, false, extruder.id()); + gcode += m_writer->set_temperature(temperature, false, extruder.id()); } // BBS @@ -4660,14 +4618,14 @@ LayerResult GCode::process_layer( bed_temp = get_highest_bed_temperature(false,print); else bed_temp = get_bed_temperature(first_extruder_id, false, m_config.curr_bed_type); - gcode += m_writer.set_bed_temperature(bed_temp); + gcode += m_writer->set_bed_temperature(bed_temp); // Mark the temperature transition from 1st to 2nd layer to be finished. m_second_layer_things_done = true; } if (single_object_instance_idx == size_t(-1)) { // Normal (non-sequential) print. - gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, layer_tools.custom_gcode, m_writer.filament()->id(), first_extruder_id, print.config()); + gcode += ProcessLayer::emit_custom_gcode_per_print_z(*this, layer_tools.custom_gcode, m_writer->filament()->id(), first_extruder_id, print.config()); } // BBS: get next extruder according to flush and soluble @@ -4958,9 +4916,9 @@ LayerResult GCode::process_layer( auto insert_timelapse_gcode = [this, print_z, &print, &most_used_extruder, &layer_object_label_ids,&printed_objects = std::as_const(m_printed_objects)]() -> std::string { PosPickCtx ctx; - ctx.curr_pos = { (coord_t)(scale_(m_writer.get_position().x())),(coord_t)(scale_(m_writer.get_position().y())) }; + ctx.curr_pos = { (coord_t)(scale_(m_writer->get_position().x())),(coord_t)(scale_(m_writer->get_position().y())) }; ctx.curr_layer = this->layer(); - ctx.curr_extruder_id = m_writer.filament()->extruder_id(); + ctx.curr_extruder_id = m_writer->filament()->extruder_id(); ctx.picture_extruder_id = most_used_extruder; if (m_config.print_sequence == PrintSequence::ByObject) ctx.printed_objects = printed_objects; @@ -4978,17 +4936,17 @@ LayerResult GCode::process_layer( config.set_key_value("timelapse_pos_x", new ConfigOptionInt((int)timelapse_pos.x())); config.set_key_value("timelapse_pos_y", new ConfigOptionInt((int)timelapse_pos.y())); config.set_key_value("has_timelapse_safe_pos", new ConfigOptionBool(timelapse_pos != DefaultTimelapsePos)); - timelapse_gcode = this->placeholder_parser_process("timelapse_gcode", print.config().time_lapse_gcode.value, m_writer.filament()->id(), &config) + "\n"; + timelapse_gcode = this->placeholder_parser_process("timelapse_gcode", print.config().time_lapse_gcode.value, m_writer->filament()->id(), &config) + "\n"; } if (!timelapse_gcode.empty()) { - m_writer.set_current_position_clear(false); + m_writer->set_current_position_clear(false); double temp_z_after_tool_change; if (GCodeProcessor::get_last_z_from_gcode(timelapse_gcode, temp_z_after_tool_change)) { - Vec3d pos = m_writer.get_position(); + Vec3d pos = m_writer->get_position(); pos(2) = temp_z_after_tool_change; - m_writer.set_position(pos); + m_writer->set_position(pos); } } @@ -5034,16 +4992,16 @@ LayerResult GCode::process_layer( config.set_key_value("layer_z", new ConfigOptionFloat(print_z)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("most_used_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(most_used_extruder))); - config.set_key_value("curr_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(m_writer.filament()->extruder_id()))); - wrapping_gcode = this->placeholder_parser_process("wrapping_detection_gcode", print.config().wrapping_detection_gcode.value, m_writer.filament()->id(), &config) +"\n"; + config.set_key_value("curr_physical_extruder_id", new ConfigOptionInt(m_config.physical_extruder_map.get_at(m_writer->filament()->extruder_id()))); + wrapping_gcode = this->placeholder_parser_process("wrapping_detection_gcode", print.config().wrapping_detection_gcode.value, m_writer->filament()->id(), &config) +"\n"; } - m_writer.set_current_position_clear(false); + m_writer->set_current_position_clear(false); double temp_z_after_tool_change; if (GCodeProcessor::get_last_z_from_gcode(wrapping_gcode, temp_z_after_tool_change)) { - Vec3d pos = m_writer.get_position(); + Vec3d pos = m_writer->get_position(); pos(2) = temp_z_after_tool_change; - m_writer.set_position(pos); + m_writer->set_position(pos); } return wrapping_gcode; }; @@ -5109,7 +5067,7 @@ LayerResult GCode::process_layer( if (should_insert) { gcode += this->retract(false, false, auto_lift_type, true); - m_writer.add_object_change_labels(gcode); + m_writer->add_object_change_labels(gcode); gcode += insert_timelapse_gcode(); has_insert_timelapse_gcode = true; @@ -5126,12 +5084,12 @@ LayerResult GCode::process_layer( } else { if (need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode && - m_writer.need_toolchange(extruder_id) && + m_writer->need_toolchange(extruder_id) && m_config.nozzle_diameter.values.size() == 2 && writer().filament() && (get_extruder_id(writer().filament()->id()) == most_used_extruder)) { gcode += this->retract(false, false, auto_lift_type, true); - m_writer.add_object_change_labels(gcode); + m_writer->add_object_change_labels(gcode); gcode += insert_timelapse_gcode(); has_insert_timelapse_gcode = true; @@ -5248,19 +5206,19 @@ LayerResult GCode::process_layer( // exclude objects if (m_enable_exclude_object) { if (is_BBL_Printer()) { - m_writer.set_object_start_str( + m_writer->set_object_start_str( std::string("; start printing object, unique label id: ") + std::to_string(instance_to_print.label_object_id) + "\n" + "M624 " + _encode_label_ids_to_base64({instance_to_print.label_object_id}) + "\n"); } else { const auto gflavor = print.config().gcode_flavor.value; if (gflavor == gcfKlipper) { - m_writer.set_object_start_str(std::string("EXCLUDE_OBJECT_START NAME=") + + m_writer->set_object_start_str(std::string("EXCLUDE_OBJECT_START NAME=") + get_instance_name(&instance_to_print.print_object, inst.id) + "\n"); } else if (gflavor == gcfMarlinLegacy || gflavor == gcfMarlinFirmware || gflavor == gcfRepRapFirmware) { std::string str = std::string("M486 S") + std::to_string(inst.unique_id) + "\n"; - m_writer.set_object_start_str(str); + m_writer->set_object_start_str(str); } } } @@ -5278,7 +5236,7 @@ LayerResult GCode::process_layer( m_avoid_crossing_perimeters.use_external_mp_once(); m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); - this->update_origin_snap(&instance_to_print.print_object, offset); + this->on_set_origin(&instance_to_print.print_object, offset); if (instance_to_print.object_by_extruder.support != nullptr) { m_layer = layers[instance_to_print.layer_id].support_layer; m_object_layer_over_raft = false; @@ -5302,7 +5260,7 @@ LayerResult GCode::process_layer( m_avoid_crossing_perimeters.use_external_mp_once(); m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); - this->update_origin_snap(&instance_to_print.print_object, offset); + this->on_set_origin(&instance_to_print.print_object, offset); ExtrusionEntityCollection support_eec; // BBS @@ -5351,7 +5309,7 @@ LayerResult GCode::process_layer( m_avoid_crossing_perimeters.use_external_mp_once(); m_last_obj_copy = this_object_copy; this->set_origin(unscale(offset)); - this->update_origin_snap(&instance_to_print.print_object, offset); + this->on_set_origin(&instance_to_print.print_object, offset); //FIXME the following code prints regions in the order they are defined, the path is not optimized in any way. auto has_infill = [](const std::vector &by_region) { @@ -5388,20 +5346,20 @@ LayerResult GCode::process_layer( } // exclude objects // Don't set m_gcode_label_objects_end if you don't had to write the m_gcode_label_objects_start. - if (!m_writer.is_object_start_str_empty()) { - m_writer.set_object_start_str(""); + if (!m_writer->is_object_start_str_empty()) { + m_writer->set_object_start_str(""); } else if (m_enable_exclude_object) { if (is_BBL_Printer()) { - m_writer.set_object_end_str(std::string("; stop printing object, unique label id: ") + + m_writer->set_object_end_str(std::string("; stop printing object, unique label id: ") + std::to_string(instance_to_print.label_object_id) + "\n" + "M625\n"); } else { const auto gflavor = print.config().gcode_flavor.value; if (gflavor == gcfKlipper) { - m_writer.set_object_end_str(std::string("EXCLUDE_OBJECT_END NAME=") + + m_writer->set_object_end_str(std::string("EXCLUDE_OBJECT_END NAME=") + get_instance_name(&instance_to_print.print_object, inst.id) + "\n"); } else if (gflavor == gcfMarlinLegacy || gflavor == gcfMarlinFirmware || gflavor == gcfRepRapFirmware) { - m_writer.set_object_end_str(std::string("M486 S-1\n")); + m_writer->set_object_end_str(std::string("M486 S-1\n")); } } } @@ -5453,7 +5411,7 @@ LayerResult GCode::process_layer( if (FILAMENT_CONFIG(retract_when_changing_layer)) { gcode += this->retract(false, false, auto_lift_type, true); } - m_writer.add_object_change_labels(gcode); + m_writer->add_object_change_labels(gcode); gcode += insert_timelapse_gcode(); } @@ -5465,7 +5423,7 @@ LayerResult GCode::process_layer( void GCode::apply_print_config(const PrintConfig &print_config) { - m_writer.apply_print_config(print_config); + m_writer->apply_print_config(print_config); m_config.apply(print_config); m_scaled_resolution = scaled(print_config.resolution.value); m_enable_exclude_object = m_config.exclude_object; @@ -5555,7 +5513,7 @@ void GCode::append_full_config(const Print &print, std::string &str) void GCode::set_extruders(const std::vector &extruder_ids) { - m_writer.set_extruders(extruder_ids); + m_writer->set_extruders(extruder_ids); // enable wipe path generation if any extruder has wipe enabled m_wipe.enable = false; @@ -5578,139 +5536,15 @@ void GCode::set_origin(const Vec2d &pointf) m_origin = pointf; } -void GCode::update_origin_snap(const PrintObject *obj, const Point &inst_shift) -{ - if (!m_origin_snap[0] && !m_origin_snap[1] && !m_origin_snap[2]) - return; - - // Clear existing snap so to_machine_coords gives raw machine coords for bbox computation. - for (int a = 0; a < 3; ++a) - m_writer.set_origin_snap(a, false, 0., 0.); - - // Reconstruct the belt pipeline transform for this object (same as - // PrintObjectSlice.cpp: z_shift * scale * shear * pre_remap). - const auto &cfg = m_config; - Transform3d belt = Transform3d::Identity(); - - // Pre-slice remap - int pre_rx = int(cfg.belt_preslice_remap_x.value); - int pre_ry = int(cfg.belt_preslice_remap_y.value); - int pre_rz = int(cfg.belt_preslice_remap_z.value); - if (pre_rx != int(BeltRemapAxis::PosX) || - pre_ry != int(BeltRemapAxis::PosY) || - pre_rz != int(BeltRemapAxis::PosZ)) { - auto remap_col = [](int r) -> Vec3d { - int a = r % 3; Vec3d c = Vec3d::Zero(); - c[a] = (r < 3) ? 1.0 : -1.0; - return c; - }; - Matrix3d lin; - lin.col(0) = remap_col(pre_rx); - lin.col(1) = remap_col(pre_ry); - lin.col(2) = remap_col(pre_rz); - Transform3d pre = Transform3d::Identity(); - pre.linear() = lin; - if (pre_rx >= 6 || pre_ry >= 6 || pre_rz >= 6) { - BoundingBoxf bb(cfg.printable_area.values); - Vec3d vm(bb.max.x(), bb.max.y(), cfg.printable_height.value); - Vec3d tr = Vec3d::Zero(); - if (pre_rx >= 6) tr[0] = vm[pre_rx % 3]; - if (pre_ry >= 6) tr[1] = vm[pre_ry % 3]; - if (pre_rz >= 6) tr[2] = vm[pre_rz % 3]; - pre.translation() = tr; - } - belt = pre * belt; - } - - // Shear - auto shear_f = [](BeltShearMode m, double a) -> double { - double r = Geometry::deg2rad(a), s = std::sin(r), c = std::cos(r); - switch (m) { - case BeltShearMode::PosCot: return (s > EPSILON) ? c/s : 0.; - case BeltShearMode::NegCot: return (s > EPSILON) ? -c/s : 0.; - case BeltShearMode::PosTan: return (c > EPSILON) ? s/c : 0.; - case BeltShearMode::NegTan: return (c > EPSILON) ? -s/c : 0.; - default: return 0.; - } - }; - struct AS { BeltShearMode m; double a; int f; }; - AS axes[3] = { - {cfg.belt_shear_x.value, cfg.belt_shear_x_angle.value, int(cfg.belt_shear_x_from.value)}, - {cfg.belt_shear_y.value, cfg.belt_shear_y_angle.value, int(cfg.belt_shear_y_from.value)}, - {cfg.belt_shear_z.value, cfg.belt_shear_z_angle.value, int(cfg.belt_shear_z_from.value)}, - }; - Transform3d shear = Transform3d::Identity(); - for (int i = 0; i < 3; ++i) - if (axes[i].m != BeltShearMode::None) { - double f = shear_f(axes[i].m, axes[i].a); - if (std::abs(f) > EPSILON) - shear.matrix()(i, axes[i].f) += f; - } - - // Scale - auto scale_f = [](BeltScaleMode m, double a) -> double { - if (m == BeltScaleMode::None) return 1.; - double r = Geometry::deg2rad(a), s = std::sin(r), c = std::cos(r); - switch (m) { - case BeltScaleMode::InvSin: return (s > EPSILON) ? 1./s : 1.; - case BeltScaleMode::InvCos: return (c > EPSILON) ? 1./c : 1.; - case BeltScaleMode::Sin: return s; - case BeltScaleMode::Cos: return c; - default: return 1.; - } - }; - Transform3d sc = Transform3d::Identity(); - sc.matrix()(0,0) = scale_f(cfg.belt_scale_x.value, cfg.belt_scale_x_angle.value); - sc.matrix()(1,1) = scale_f(cfg.belt_scale_y.value, cfg.belt_scale_y_angle.value); - sc.matrix()(2,2) = scale_f(cfg.belt_scale_z.value, cfg.belt_scale_z_angle.value); - - belt = sc * shear * belt; - - // Z-shift - double zs = (obj->belt_min_z() < 0.) ? -obj->belt_min_z() : 0.; - if (zs > 0.) { - Transform3d zsh = Transform3d::Identity(); - zsh.matrix()(2, 3) = zs; - belt = zsh * belt; - } - - // Full transform: belt * trafo_centered - Transform3d full = belt * obj->trafo_centered(); - - // Instance shift in slicer space + global Z offset - Vec3d shift(unscale(inst_shift.x()), - unscale(inst_shift.y()), - obj->belt_global_z_offset()); - - // Compute this instance's machine-space bbox min - BoundingBoxf3 bb = obj->model_object()->raw_bounding_box(); - Vec3d mn = bb.min.cast(), mx = bb.max.cast(); - Vec3d inst_min(std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::max()); - for (int i = 0; i < 8; ++i) { - Vec3d c((i & 1) ? mx.x() : mn.x(), - (i & 2) ? mx.y() : mn.y(), - (i & 4) ? mx.z() : mn.z()); - Vec3d mc = m_writer.to_machine_coords(full * c + shift); - for (int a = 0; a < 3; ++a) - inst_min[a] = std::min(inst_min[a], mc[a]); - } - - // Update writer snap for each enabled axis - for (int a = 0; a < 3; ++a) - m_writer.set_origin_snap(a, m_origin_snap[a], m_origin_snap_offset[a], inst_min[a]); -} - std::string GCode::preamble() { - std::string gcode = m_writer.preamble(); + std::string gcode = m_writer->preamble(); /* Perform a *silent* move to z_offset: we need this to initialize the Z position of our writer object so that any initial lift taking place before the first layer change will raise the extruder from the correct initial Z instead of 0. */ - m_writer.travel_to_z(m_config.z_offset.value); + m_writer->travel_to_z(m_config.z_offset.value); return gcode; } @@ -5721,28 +5555,28 @@ std::string GCode::change_layer(coordf_t print_z) std::string gcode; if (m_layer_count > 0) // Increment a progress bar indicator. - gcode += m_writer.update_progress(++ m_layer_index, m_layer_count); + gcode += m_writer->update_progress(++ m_layer_index, m_layer_count); //BBS coordf_t z = print_z + m_config.z_offset.value; // in unscaled coordinates - if (FILAMENT_CONFIG(retract_when_changing_layer) && m_writer.will_move_z(z)) { + if (FILAMENT_CONFIG(retract_when_changing_layer) && m_writer->will_move_z(z)) { LiftType lift_type = this->to_lift_type(ZHopType(FILAMENT_CONFIG(z_hop_types))); //BBS: force to use SpiralLift when change layer if lift type is auto gcode += this->retract(false, false, ZHopType(FILAMENT_CONFIG(z_hop_types)) == ZHopType::zhtAuto ? LiftType::SpiralLift : lift_type); } - m_writer.add_object_change_labels(gcode); + m_writer->add_object_change_labels(gcode); if (m_spiral_vase) { //BBS: force to normal lift immediately in spiral vase mode std::ostringstream comment; comment << "move to next layer (" << m_layer_index << ")"; - gcode += m_writer.travel_to_z(z, comment.str()); + gcode += m_writer->travel_to_z(z, comment.str()); } m_need_change_layer_lift_z = true; m_nominal_z = z; - m_writer.get_position().z() = z; + m_writer->get_position().z() = z; // forget last wiping path as wiping after raising Z is pointless // BBS. Dont forget wiping path to reduce stringing. @@ -6044,7 +5878,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou //Point pt = ((nd * nd >= l2) ? (p1+v*0.4): (p1 + 0.2 * v * (nd / sqrt(l2)))).cast(); pt.rotate(angle, paths.front().polyline.points.front()); // generate the travel move - gcode += m_writer.extrude_to_xy(this->point_to_gcode(pt), 0,"move inwards before travel",true); + gcode += m_writer->extrude_to_xy(this->point_to_gcode(pt), 0,"move inwards before travel",true); } return gcode; @@ -6367,7 +6201,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, bool slope_need_z_travel = false; if (sloped != nullptr && !sloped->is_flat()) { auto target_z = get_sloped_z(sloped->slope_begin.z_ratio); - slope_need_z_travel = m_writer.will_move_z(target_z); + slope_need_z_travel = m_writer->will_move_z(target_z); } // Move to first point of extrusion path // path is 2D. But in slope lift case, lift z is done in travel_to function. @@ -6390,7 +6224,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // if needed, write the gcode_label_objects_end then gcode_label_objects_start // should be already done by travel_to, but just in case - m_writer.add_object_change_labels(gcode); + m_writer->add_object_change_labels(gcode); // compensate retraction gcode += this->unretract(); @@ -6444,12 +6278,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } } - if (m_writer.get_gcode_flavor() == gcfKlipper) { - gcode += m_writer.set_accel_and_jerk(acceleration_i, jerk); + if (m_writer->get_gcode_flavor() == gcfKlipper) { + gcode += m_writer->set_accel_and_jerk(acceleration_i, jerk); } else { - gcode += m_writer.set_print_acceleration(acceleration_i); - gcode += m_writer.set_jerk_xy(jerk); + gcode += m_writer->set_print_acceleration(acceleration_i); + gcode += m_writer->set_jerk_xy(jerk); } // calculate effective extrusion length per distance unit (e_per_mm) @@ -6494,8 +6328,8 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } // Effective extrusion length per distance unit = (filament_flow_ratio/cross_section) * mm3_per_mm / print flow ratio - // m_writer.extruder()->e_per_mm3() below is (filament flow ratio / cross-sectional area) - double e_per_mm = m_writer.filament()->e_per_mm3() * _mm3_per_mm; + // m_writer->extruder()->e_per_mm3() below is (filament flow ratio / cross-sectional area) + double e_per_mm = m_writer->filament()->e_per_mm3() * _mm3_per_mm; e_per_mm /= filament_flow_ratio; // set speed @@ -6721,7 +6555,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index + 1)); config.set_key_value("layer_z", new ConfigOptionFloat(m_layer == nullptr ? m_last_height : m_layer->print_z)); gcode += this->placeholder_parser_process("change_extrusion_role_gcode", - m_config.change_extrusion_role_gcode.value, m_writer.filament()->id(), &config) + m_config.change_extrusion_role_gcode.value, m_writer->filament()->id(), &config) + "\n"; } @@ -6776,7 +6610,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, if (m_multi_flow_segment_path_average_mm3_per_mm > 0) { sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(), - m_writer.filament()->id(), + m_writer->filament()->id(), m_multi_flow_segment_path_average_mm3_per_mm, acceleration_i, ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)), @@ -6789,7 +6623,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // to issue a zero flow PA change command for this sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(), - m_writer.filament()->id(), + m_writer->filament()->id(), _mm3_per_mm, acceleration_i, ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)), @@ -6895,7 +6729,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(), - m_writer.filament()->id(), + m_writer->filament()->id(), _mm3_per_mm, acceleration_i, ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)), @@ -6910,7 +6744,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(), - m_writer.filament()->id(), + m_writer->filament()->id(), _mm3_per_mm, acceleration_i, ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)), @@ -6925,7 +6759,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // ORCA: End of adaptive PA code segment } - gcode += m_writer.set_speed(F, "", comment); + gcode += m_writer->set_speed(F, "", comment); { if (m_enable_cooling_markers) { if (enable_overhang_bridge_fan) { @@ -6943,7 +6777,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } // BBS: use G1 if not enable arc fitting or has no arc fitting result or in spiral_mode mode or we are doing sloped extrusion // Attention: G2 and G3 is not supported in spiral_mode mode - if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr || m_config.belt_printer) { + if (!m_config.enable_arc_fitting || path.polyline.fitting_result.empty() || m_config.spiral_mode || sloped != nullptr || this->should_disable_arc_fitting()) { double path_length = 0.; double total_length = sloped == nullptr ? 0. : path.polyline.length() * SCALING_FACTOR; for (const Line& line : path.polyline.lines()) { @@ -6963,7 +6797,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } if (sloped == nullptr) { // Normal extrusion - gcode += m_writer.extrude_to_xy( + gcode += m_writer->extrude_to_xy( this->point_to_gcode(line.b), dE, GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion()); @@ -6972,7 +6806,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length); Vec2d dest2d = this->point_to_gcode(line.b); Vec3d dest3d(dest2d(0), dest2d(1), get_sloped_z(z_ratio)); - gcode += m_writer.extrude_to_xyz( + gcode += m_writer->extrude_to_xyz( dest3d, dE * e_ratio, GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion()); @@ -7002,7 +6836,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, line_length); } } - gcode += m_writer.extrude_to_xy( + gcode += m_writer->extrude_to_xy( this->point_to_gcode(line.b), dE, GCodeWriter::full_gcode_comment ? tempDescription : "", path.is_force_no_extrusion()); @@ -7025,7 +6859,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, tempDescription += Slic3r::format(" | Old Flow Value: %0.5f Length: %0.5f",oldE, arc_length); } } - gcode += m_writer.extrude_arc_to_xy( + gcode += m_writer->extrude_arc_to_xy( this->point_to_gcode(arc.end_point), center_offset, dE, @@ -7053,7 +6887,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, Polyline l(p); total_length = l.length() * SCALING_FACTOR; } - gcode += m_writer.set_speed(last_set_speed, "", comment); + gcode += m_writer->set_speed(last_set_speed, "", comment); Vec2d prev = this->point_to_gcode_quantized(new_points[0].p); bool pre_fan_enabled = false; bool cur_fan_enabled = false; @@ -7103,7 +6937,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(), - m_writer.filament()->id(), + m_writer->filament()->id(), _mm3_per_mm, acceleration_i, ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)), @@ -7118,7 +6952,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } sprintf(buf, ";%sT%u MM3MM:%g ACCEL:%u BR:%d RC:%d OV:%d\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::PA_Change).c_str(), - m_writer.filament()->id(), + m_writer->filament()->id(), _mm3_per_mm, acceleration_i, ((path.role() == erBridgeInfill) ||(path.role() == erOverhangPerimeter)), @@ -7135,10 +6969,10 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, // Ignore small speed variations - emit speed change if the delta between current and new is greater than 60mm/min / 1mm/sec // Reset speed to F if delta to F is less than 1mm/sec if ((std::abs(last_set_speed - new_speed) > 60)) { - gcode += m_writer.set_speed(new_speed, "", comment); + gcode += m_writer->set_speed(new_speed, "", comment); last_set_speed = new_speed; } else if ((std::abs(F - new_speed) <= 60)) { - gcode += m_writer.set_speed(F, "", comment); + gcode += m_writer->set_speed(F, "", comment); last_set_speed = F; } auto dE = e_per_mm * line_length; @@ -7152,12 +6986,12 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description, } if (sloped == nullptr) { // Normal extrusion - gcode += m_writer.extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : ""); + gcode += m_writer->extrude_to_xy(p, dE, GCodeWriter::full_gcode_comment ? tempDescription : ""); } else { // Sloped extrusion const auto [z_ratio, e_ratio] = sloped->interpolate(path_length / total_length); Vec3d dest3d(p(0), p(1), get_sloped_z(z_ratio)); - gcode += m_writer.extrude_to_xyz(dest3d, dE * e_ratio, GCodeWriter::full_gcode_comment ? tempDescription : ""); + gcode += m_writer->extrude_to_xyz(dest3d, dE * e_ratio, GCodeWriter::full_gcode_comment ? tempDescription : ""); } prev = p; @@ -7298,18 +7132,18 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string } } - if (m_writer.get_gcode_flavor() == gcfKlipper) { - gcode += m_writer.set_accel_and_jerk(acceleration_to_set, jerk_to_set); + if (m_writer->get_gcode_flavor() == gcfKlipper) { + gcode += m_writer->set_accel_and_jerk(acceleration_to_set, jerk_to_set); } else { - gcode += m_writer.set_travel_acceleration(acceleration_to_set); - gcode += m_writer.set_jerk_xy(jerk_to_set); + gcode += m_writer->set_travel_acceleration(acceleration_to_set); + gcode += m_writer->set_jerk_xy(jerk_to_set); } // if a retraction would be needed, try to use reduce_crossing_wall to plan a // multi-hop travel path inside the configuration space if (m_config.reduce_crossing_wall && !m_avoid_crossing_perimeters.disabled_once() - && m_writer.is_current_position_clear()) + && m_writer->is_current_position_clear()) //BBS: don't generate detour travel paths when current position is unclea { travel = m_avoid_crossing_perimeters.travel_to(*this, point, &could_be_wipe_disabled); @@ -7356,7 +7190,7 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string // if needed, write the gcode_label_objects_end then gcode_label_objects_start - m_writer.add_object_change_labels(gcode); + m_writer->add_object_change_labels(gcode); // use G1 because we rely on paths being straight (G0 may make round paths) if (travel.size() >= 2) { @@ -7364,14 +7198,14 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string if (false/*m_spiral_vase*/) { // No lazy z lift for spiral vase mode for (size_t i = 1; i < travel.size(); ++i) { - gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment); + gcode += m_writer->travel_to_xy(this->point_to_gcode(travel.points[i]), comment); } } else { if (travel.size() == 2) { // No extra movements emitted by avoid_crossing_perimeters, simply move to the end point with z change const auto& dest2d = this->point_to_gcode(travel.points.back()); Vec3d dest3d(dest2d(0), dest2d(1), z == DBL_MAX ? m_nominal_z : z); - gcode += m_writer.travel_to_xyz(dest3d, comment, m_need_change_layer_lift_z); + gcode += m_writer->travel_to_xyz(dest3d, comment, m_need_change_layer_lift_z); m_need_change_layer_lift_z = false; } else { // Extra movements emitted by avoid_crossing_perimeters, lift the z to normal height at the beginning, then apply the z @@ -7381,16 +7215,16 @@ std::string GCode::travel_to(const Point& point, ExtrusionRole role, std::string // Lift to normal z at beginning Vec2d dest2d = this->point_to_gcode(travel.points[i]); Vec3d dest3d(dest2d(0), dest2d(1), m_nominal_z); - gcode += m_writer.travel_to_xyz(dest3d, comment, m_need_change_layer_lift_z); + gcode += m_writer->travel_to_xyz(dest3d, comment, m_need_change_layer_lift_z); m_need_change_layer_lift_z = false; } else if (z != DBL_MAX && i == travel.size() - 1) { // Apply z_ratio for the very last point Vec2d dest2d = this->point_to_gcode(travel.points[i]); Vec3d dest3d(dest2d(0), dest2d(1), z); - gcode += m_writer.travel_to_xyz(dest3d, comment); + gcode += m_writer->travel_to_xyz(dest3d, comment); } else { // For all points in between, no z change - gcode += m_writer.travel_to_xy(this->point_to_gcode(travel.points[i]), comment); + gcode += m_writer->travel_to_xy(this->point_to_gcode(travel.points[i]), comment); } } } @@ -7493,7 +7327,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp if (clipped_travel.length() > travel_len_thresh) clipped_travel.points.back() = clipped_travel.points.front()+(clipped_travel.points.back() - clipped_travel.points.front()) * (travel_len_thresh / clipped_travel.length()); //BBS: translate to current plate coordinate system - clipped_travel.translate(Point::new_scale(double(m_origin.x() - m_writer.get_xy_offset().x()), double(m_origin.y() - m_writer.get_xy_offset().y()))); + clipped_travel.translate(Point::new_scale(double(m_origin.x() - m_writer->get_xy_offset().x()), double(m_origin.y() - m_writer->get_xy_offset().y()))); //BBS: force to retract when leave from external perimeter for a long travel //Better way is judging whether the travel move direction is same with last extrusion move. @@ -7545,13 +7379,13 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li { std::string gcode; - if (m_writer.filament() == nullptr) + if (m_writer->filament() == nullptr) return gcode; // wipe (if it's enabled for this extruder and we have a stored wipe path and no-zero wipe distance) if (FILAMENT_CONFIG(wipe) && m_wipe.has_path() && scale_(FILAMENT_CONFIG(wipe_distance)) > SCALED_EPSILON) { Wipe::RetractionValues wipeRetractions = m_wipe.calculateWipeRetractionLengths(*this, toolchange); - gcode += toolchange ? m_writer.retract_for_toolchange(true,wipeRetractions.retractLengthBeforeWipe) : m_writer.retract(true, wipeRetractions.retractLengthBeforeWipe); + gcode += toolchange ? m_writer->retract_for_toolchange(true,wipeRetractions.retractLengthBeforeWipe) : m_writer->retract(true, wipeRetractions.retractLengthBeforeWipe); gcode += m_wipe.wipe(*this,wipeRetractions.retractLengthDuringWipe, toolchange, is_last_retraction); } @@ -7561,14 +7395,14 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li length is honored in case wipe path was too short. */ if ((!this->on_first_layer() || this->config().bottom_surface_pattern != InfillPattern::ipHilbertCurve) && (role != erTopSolidInfill || this->config().top_surface_pattern != InfillPattern::ipHilbertCurve)) - gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract(); + gcode += toolchange ? m_writer->retract_for_toolchange() : m_writer->retract(); - gcode += m_writer.reset_e(); + gcode += m_writer->reset_e(); // Orca: check if should + can lift (roughly from SuperSlicer) RetractLiftEnforceType retract_lift_type = RetractLiftEnforceType(EXTRUDER_CONFIG(retract_lift_enforce)); bool needs_lift = toolchange - || m_writer.filament()->retraction_length() > 0 + || m_writer->filament()->retraction_length() > 0 || m_config.use_firmware_retraction; bool last_fill_extrusion_role_top_infill = (this->m_last_notgapfill_extrusion_role == ExtrusionRole::erTopSolidInfill || this->m_last_notgapfill_extrusion_role == ExtrusionRole::erIroning); @@ -7591,9 +7425,9 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li if (needs_lift && can_lift) { if (apply_instantly) - gcode += m_writer.eager_lift(lift_type); + gcode += m_writer->eager_lift(lift_type); else - gcode += m_writer.lazy_lift(lift_type, m_spiral_vase != nullptr); + gcode += m_writer->lazy_lift(lift_type, m_spiral_vase != nullptr); } return gcode; @@ -7602,11 +7436,11 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bool by_object, int toolchange_temp_override) { int new_extruder_id = get_extruder_id(new_filament_id); - if (!m_writer.need_toolchange(new_filament_id)) + if (!m_writer->need_toolchange(new_filament_id)) return ""; // if we are running a single-extruder setup, just set the extruder and return nothing - if (!m_writer.multiple_extruders) { + if (!m_writer->multiple_extruders) { this->placeholder_parser().set("current_extruder", new_filament_id); this->placeholder_parser().set("retraction_distance_when_ec", m_config.retraction_distances_when_ec.get_at(new_filament_id)); this->placeholder_parser().set("long_retraction_when_ec", m_config.long_retractions_when_ec.get_at(new_filament_id)); @@ -7629,13 +7463,13 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo check_add_eol(gcode); } if (m_config.enable_pressure_advance.get_at(new_filament_id)) { - gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id)); + gcode += m_writer->set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id)); // Orca: Adaptive PA // Reset Adaptive PA processor last PA value m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(new_filament_id)); } - gcode += m_writer.toolchange(new_filament_id); + gcode += m_writer->toolchange(new_filament_id); return gcode; } @@ -7650,18 +7484,18 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo // BBS: insert skip object label before change filament while by object if (by_object) - m_writer.add_object_change_labels(gcode); + m_writer->add_object_change_labels(gcode); bool add_change_filament_624 = false; - if (m_writer.filament() != nullptr) { + if (m_writer->filament() != nullptr) { // Process the custom filament_end_gcode. set_extruder() is only called if there is no wipe tower // so it should not be injected twice. - unsigned int old_filament_id = m_writer.filament()->id(); + unsigned int old_filament_id = m_writer->filament()->id(); const std::string &filament_end_gcode = m_config.filament_end_gcode.get_at(old_filament_id); if (! filament_end_gcode.empty()) { DynamicConfig config; config.set_key_value("layer_num", new ConfigOptionInt(m_layer_index)); - config.set_key_value("layer_z", new ConfigOptionFloat(m_writer.get_position().z() - m_config.z_offset.value)); + config.set_key_value("layer_z", new ConfigOptionFloat(m_writer->get_position().z() - m_config.z_offset.value)); config.set_key_value("max_layer_z", new ConfigOptionFloat(m_max_layer_z)); config.set_key_value("filament_extruder_id", new ConfigOptionInt(int(get_extruder_id(old_filament_id)))); if (!m_filament_instances_code.empty()) { @@ -7677,7 +7511,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo // If ooze prevention is enabled, park current extruder in the nearest // standby point and set it to the standby temperature. - if (m_ooze_prevention.enable && m_writer.filament() != nullptr) + if (m_ooze_prevention.enable && m_writer->filament() != nullptr) gcode += m_ooze_prevention.pre_toolchange(*this); // BBS @@ -7690,23 +7524,23 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo if (toolchange_temp_override > 0) new_filament_temp = toolchange_temp_override; - Vec3d nozzle_pos = m_writer.get_position(); + Vec3d nozzle_pos = m_writer->get_position(); float old_retract_length, old_retract_length_toolchange, wipe_volume; int old_filament_temp, old_filament_e_feedrate; float filament_area = float((M_PI / 4.f) * pow(m_config.filament_diameter.get_at(new_filament_id), 2)); //BBS: add handling for filament change in start gcode int old_filament_id = -1; - if (m_writer.filament() != nullptr || m_start_gcode_filament != -1) { + if (m_writer->filament() != nullptr || m_start_gcode_filament != -1) { std::vector flush_matrix(cast(get_flush_volumes_matrix(m_config.flush_volumes_matrix.values, new_extruder_id, m_config.nozzle_diameter.values.size()))); const unsigned int number_of_extruders = (unsigned int) (m_config.filament_colour.values.size()); // if is multi_extruder only use the fist extruder matrix - if (m_writer.filament() != nullptr) - assert(m_writer.filament()->id() < number_of_extruders); + if (m_writer->filament() != nullptr) + assert(m_writer->filament()->id() < number_of_extruders); else assert(m_start_gcode_filament < number_of_extruders); - old_filament_id = m_writer.filament() != nullptr ? m_writer.filament()->id() : m_start_gcode_filament; - int old_extruder_id = m_writer.filament() != nullptr ? m_writer.filament()->extruder_id() : get_extruder_id(m_start_gcode_filament); + old_filament_id = m_writer->filament() != nullptr ? m_writer->filament()->id() : m_start_gcode_filament; + int old_extruder_id = m_writer->filament() != nullptr ? m_writer->filament()->extruder_id() : get_extruder_id(m_start_gcode_filament); old_retract_length = m_config.retraction_length.get_at(old_filament_id); old_retract_length_toolchange = m_config.retract_length_toolchange.get_at(old_filament_id); @@ -7716,7 +7550,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo float grab_purge_volume = m_config.grab_length.get_at(new_extruder_id) * 2.4; if (old_extruder_id != new_extruder_id) { //calc flush volume between the same extruder id - int old_filament_id_in_new_extruder = m_writer.filament(new_extruder_id) != nullptr ? m_writer.filament(new_extruder_id)->id() : -1; + int old_filament_id_in_new_extruder = m_writer->filament(new_extruder_id) != nullptr ? m_writer->filament(new_extruder_id)->id() : -1; if (old_filament_id_in_new_extruder == -1) wipe_volume = 0; else { @@ -7852,13 +7686,13 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo //BBS: gcode writer doesn't know where the extruder is and whether fan speed is changed after inserting tool change gcode //Set this flag so that normal lift will be used the first time after tool change. gcode += ";_FORCE_RESUME_FAN_SPEED\n"; - m_writer.set_current_position_clear(false); + m_writer->set_current_position_clear(false); //BBS: check whether custom gcode changes the z position. Update if changed double temp_z_after_tool_change; if (GCodeProcessor::get_last_z_from_gcode(toolchange_gcode_parsed, temp_z_after_tool_change)) { - Vec3d pos = m_writer.get_position(); + Vec3d pos = m_writer->get_position(); pos(2) = temp_z_after_tool_change; - m_writer.set_position(pos); + m_writer->set_position(pos); } } } @@ -7866,13 +7700,13 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo // BBS. Reset old extruder E-value. // Keep retract length because Custom GCode will guarantee retract length be the same as toolchange if (m_config.single_extruder_multi_material) { - m_writer.reset_e(); + m_writer->reset_e(); } //BBS: don't add T[next extruder] if there is no T cmd on filament change //We inform the writer about what is happening, but we may not use the resulting gcode. - std::string toolchange_command = m_writer.toolchange(new_filament_id); - if (!custom_gcode_changes_tool(toolchange_gcode_parsed, m_writer.toolchange_prefix(), new_filament_id)) + std::string toolchange_command = m_writer->toolchange(new_filament_id); + if (!custom_gcode_changes_tool(toolchange_gcode_parsed, m_writer->toolchange_prefix(), new_filament_id)) gcode += toolchange_command; else { // user provided his own toolchange gcode, no need to do anything @@ -7883,7 +7717,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo int temp = (m_layer_index <= 0 ? m_config.nozzle_temperature_initial_layer.get_at(new_filament_id) : m_config.nozzle_temperature.get_at(new_filament_id)); - gcode += m_writer.set_temperature(temp, false); + gcode += m_writer->set_temperature(temp, false); } this->placeholder_parser().set("current_extruder", new_filament_id); @@ -7927,7 +7761,7 @@ std::string GCode::set_extruder(unsigned int new_filament_id, double print_z, bo gcode += m_ooze_prevention.post_toolchange(*this); if (m_config.enable_pressure_advance.get_at(new_filament_id)) { - gcode += m_writer.set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id)); + gcode += m_writer->set_pressure_advance(m_config.pressure_advance.get_at(new_filament_id)); // Orca: Adaptive PA // Reset Adaptive PA processor last PA value m_pa_processor->resetPreviousPA(m_config.pressure_advance.get_at(new_filament_id)); @@ -8013,7 +7847,7 @@ Vec2d GCode::point_to_gcode(const Point &point) const Point GCode::gcode_to_point(const Vec2d &point) const { Vec2d pt = point - m_origin; - if (const Extruder *extruder = m_writer.filament(); extruder) + if (const Extruder *extruder = m_writer->filament(); extruder) // This function may be called at the very start from toolchange G-code when the extruder is not assigned yet. pt += m_config.extruder_offset.get_at(extruder->extruder_id()); return scaled(pt); diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index dbec7d0315..9004f30fd3 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -4,6 +4,7 @@ #include "libslic3r.h" #include "ExPolygon.hpp" #include "GCodeWriter.hpp" +#include "BeltGCodeWriter.hpp" #include "Layer.hpp" #include "Point.hpp" #include "PlaceholderParser.hpp" @@ -206,16 +207,18 @@ public: m_last_obj_copy(nullptr, Point(std::numeric_limits::max(), std::numeric_limits::max())), // BBS m_toolchange_count(0), - m_nominal_z(0.) + m_nominal_z(0.), + m_writer(std::make_unique()) {} - ~GCode() = default; + virtual ~GCode() = default; +public: // throws std::runtime_exception on error, // throws CanceledException through print->throw_if_canceled(). void do_export(Print* print, const char* path, GCodeProcessorResult* result = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr); void export_layer_filaments(GCodeProcessorResult* result); //BBS: set offset for gcode writer - void set_gcode_offset(double x, double y) { m_writer.set_xy_offset(x, y); m_processor.set_xy_offset(x, y);} + void set_gcode_offset(double x, double y) { m_writer->set_xy_offset(x, y); m_processor.set_xy_offset(x, y);} // Exported for the helper classes (OozePrevention, Wipe) and for the Perl binding for unit tests. const Vec2d& origin() const { return m_origin; } @@ -227,8 +230,8 @@ public: Vec2d point_to_gcode_quantized(const Point& point) const; const FullPrintConfig &config() const { return m_config; } const Layer* layer() const { return m_layer; } - GCodeWriter& writer() { return m_writer; } - const GCodeWriter& writer() const { return m_writer; } + GCodeWriter& writer() { return *m_writer; } + const GCodeWriter& writer() const { return *m_writer; } PlaceholderParser& placeholder_parser() { return m_placeholder_parser_integration.parser; } const PlaceholderParser& placeholder_parser() const { return m_placeholder_parser_integration.parser; } // Process a template through the placeholder parser, collect error messages to be reported @@ -248,7 +251,7 @@ public: std::string travel_to(const Point& point, ExtrusionRole role, std::string comment, double z = DBL_MAX); bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type); std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift, bool apply_instantly = false, ExtrusionRole role = erNone); - std::string unretract() { return m_writer.unlift() + m_writer.unretract(); } + std::string unretract() { return m_writer->unlift() + m_writer->unretract(); } std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false, int toolchange_temp_override = -1); bool is_BBL_Printer(); WipeTowerType wipe_tower_type(); @@ -300,7 +303,7 @@ public: } }; -private: +protected: class GCodeOutputStream { public: GCodeOutputStream(FILE *f, GCodeProcessor &processor) : f(f), m_processor(processor) {} @@ -328,9 +331,17 @@ private: FILE *f = nullptr; GCodeProcessor &m_processor; }; + + // Virtual hooks for belt printer subclass (BeltGCode). + // No-ops in base GCode; overridden in BeltGCode. + virtual void init_belt_writer(Print &print, bool is_bbl_printers) {} + virtual void write_belt_header(GCodeOutputStream &file, const Print &print) {} + virtual void on_set_origin(const PrintObject *obj, const Point &inst_shift) {} + virtual bool should_disable_arc_fitting() const { return false; } + void _do_export(Print &print, GCodeOutputStream &file, ThumbnailsGeneratorCallback thumbnail_cb); - static std::vector collect_layers_to_print(const PrintObject &object); + static std::vector collect_layers_to_print(const PrintObject &object, bool skip_empty_first_layer = false); static std::vector>> collect_layers_to_print(const Print &print); std::string generate_skirt(const Print &print, @@ -493,16 +504,11 @@ private: This affects the input arguments supplied to the extrude*() and travel_to() methods. */ Vec2d m_origin; - // Per-axis origin snap: shift G-code so each object's bbox min = offset. - bool m_origin_snap[3] = {false, false, false}; - double m_origin_snap_offset[3] = {0., 0., 0.}; - // Called when switching instances to recompute the writer's snap for this instance. - void update_origin_snap(const PrintObject *obj, const Point &inst_shift); FullPrintConfig m_config; DynamicConfig m_calib_config; // scaled G-code resolution double m_scaled_resolution; - GCodeWriter m_writer; + std::unique_ptr m_writer; struct PlaceholderParserIntegration { void reset(); diff --git a/src/libslic3r/GCode/BeltBackTransform.cpp b/src/libslic3r/GCode/BeltBackTransform.cpp index 7cd5bc6b53..2e2cd26f75 100644 --- a/src/libslic3r/GCode/BeltBackTransform.cpp +++ b/src/libslic3r/GCode/BeltBackTransform.cpp @@ -1,40 +1,8 @@ #include "BeltBackTransform.hpp" -#include "../Geometry.hpp" -#include +#include "../BeltTransform.hpp" namespace Slic3r { -// Keep in sync with PrintObjectSlice.cpp compute_shear_factor (lines ~147-157). -static double compute_shear_factor(BeltShearMode mode, double angle_deg) -{ - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad); - double cos_a = std::cos(angle_rad); - switch (mode) { - case BeltShearMode::PosCot: return (sin_a > EPSILON) ? cos_a / sin_a : 0.; - case BeltShearMode::NegCot: return (sin_a > EPSILON) ? -cos_a / sin_a : 0.; - case BeltShearMode::PosTan: return (cos_a > EPSILON) ? sin_a / cos_a : 0.; - case BeltShearMode::NegTan: return (cos_a > EPSILON) ? -sin_a / cos_a : 0.; - default: return 0.; - } -} - -// Keep in sync with PrintObjectSlice.cpp compute_scale_factor (lines ~180-192). -static double compute_scale_factor(BeltScaleMode mode, double angle_deg) -{ - if (mode == BeltScaleMode::None) return 1.; - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad); - double cos_a = std::cos(angle_rad); - switch (mode) { - case BeltScaleMode::InvSin: return (sin_a > EPSILON) ? 1. / sin_a : 1.; - case BeltScaleMode::InvCos: return (cos_a > EPSILON) ? 1. / cos_a : 1.; - case BeltScaleMode::Sin: return sin_a; - case BeltScaleMode::Cos: return cos_a; - default: return 1.; - } -} - bool BeltBackTransform::init_from_config(const PrintConfig &config) { m_active = false; @@ -43,99 +11,19 @@ bool BeltBackTransform::init_from_config(const PrintConfig &config) if (!config.belt_printer.value || !config.belt_gcode_back_transform.value) return false; - // --- Pre-slice axis remap (same as PrintObjectSlice.cpp) --- - int pre_rx = int(config.belt_preslice_remap_x.value); - int pre_ry = int(config.belt_preslice_remap_y.value); - int pre_rz = int(config.belt_preslice_remap_z.value); - - bool has_preslice_remap = (pre_rx != int(BeltRemapAxis::PosX) || - pre_ry != int(BeltRemapAxis::PosY) || - pre_rz != int(BeltRemapAxis::PosZ)); - // Require at least one active transform to proceed. bool has_global_shear = config.belt_shear_x_global.value || config.belt_shear_y_global.value || config.belt_shear_z_global.value; - if (!has_global_shear && !has_preslice_remap) + if (!has_global_shear && !BeltTransformPipeline::has_preslice_remap(config)) return false; - // Build pre-slice remap matrix. - Transform3d pre_remap = Transform3d::Identity(); - if (has_preslice_remap) { - auto remap_column = [](int r) -> Vec3d { - int axis = r % 3; - Vec3d col = Vec3d::Zero(); - if (r < 3) col[axis] = 1.0; - else if (r < 6) col[axis] = -1.0; - else col[axis] = -1.0; // Rev: max - pos - return col; - }; - - Matrix3d remap_lin; - remap_lin.col(0) = remap_column(pre_rx); - remap_lin.col(1) = remap_column(pre_ry); - remap_lin.col(2) = remap_column(pre_rz); - pre_remap.linear() = remap_lin; - - // Rev mode translation (needs build volume extents). - Vec3d remap_trans = Vec3d::Zero(); - if (pre_rx >= 6 || pre_ry >= 6 || pre_rz >= 6) { - BoundingBoxf bbox_bed(config.printable_area.values); - Vec3d vol_max(bbox_bed.max.x(), bbox_bed.max.y(), - config.printable_height.value); - auto add_rev = [&](int r, int out) { - if (r >= 6) remap_trans[out] = vol_max[r % 3]; - }; - add_rev(pre_rx, 0); - add_rev(pre_ry, 1); - add_rev(pre_rz, 2); - } - pre_remap.translation() = remap_trans; - } - - // Build per-axis shear matrix (same as PrintObjectSlice.cpp). - struct AxisShear { BeltShearMode mode; double angle; int from; }; - AxisShear axes[3] = { - { config.belt_shear_x.value, config.belt_shear_x_angle.value, int(config.belt_shear_x_from.value) }, - { config.belt_shear_y.value, config.belt_shear_y_angle.value, int(config.belt_shear_y_from.value) }, - { config.belt_shear_z.value, config.belt_shear_z_angle.value, int(config.belt_shear_z_from.value) }, - }; - - Matrix3d shear = Matrix3d::Identity(); - bool has_shear = false; - for (int row = 0; row < 3; ++row) { - if (axes[row].mode != BeltShearMode::None) { - double factor = compute_shear_factor(axes[row].mode, axes[row].angle); - if (std::abs(factor) > EPSILON) { - shear(row, axes[row].from) += factor; - has_shear = true; - } - } - } - - // Build per-axis scale diagonal matrix (same as PrintObjectSlice.cpp). - double sx = compute_scale_factor(config.belt_scale_x.value, config.belt_scale_x_angle.value); - double sy = compute_scale_factor(config.belt_scale_y.value, config.belt_scale_y_angle.value); - double sz = compute_scale_factor(config.belt_scale_z.value, config.belt_scale_z_angle.value); - - Matrix3d scale = Matrix3d::Identity(); - bool has_scale = (std::abs(sx - 1.) > EPSILON || - std::abs(sy - 1.) > EPSILON || - std::abs(sz - 1.) > EPSILON); - if (has_scale) { - scale(0, 0) = sx; - scale(1, 1) = sy; - scale(2, 2) = sz; - } - - if (!has_shear && !has_scale && !has_preslice_remap) + // Build the forward pipeline (scale * shear * pre_remap) and store its inverse. + Transform3d forward = BeltTransformPipeline::build_forward_transform(config); + if (forward.isApprox(Transform3d::Identity())) return false; - // Forward pipeline: scale * shear * pre_remap (same order as PrintObjectSlice.cpp). - Transform3d combined = Transform3d::Identity(); - combined.linear() = scale * shear; - combined = combined * pre_remap; - m_inverse = combined.inverse(); + m_inverse = forward.inverse(); m_active = true; return true; } diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp index b5a6402a14..51f82ea009 100644 --- a/src/libslic3r/GCodeWriter.cpp +++ b/src/libslic3r/GCodeWriter.cpp @@ -20,59 +20,6 @@ namespace Slic3r { bool GCodeWriter::full_gcode_comment = true; -void GCodeWriter::set_belt_angle(double angle_deg) -{ - m_belt_angle_rad = Geometry::deg2rad(angle_deg); -} - -void GCodeWriter::set_axis_remap(int rx, int ry, int rz) -{ - m_remap_x = rx; - m_remap_y = ry; - m_remap_z = rz; -} - -void GCodeWriter::set_build_volume_max(const Vec3d &max) -{ - m_build_vol_max = max; -} - -void GCodeWriter::set_belt_back_transform(const PrintConfig &config) -{ - m_belt_back_transform.init_from_config(config); -} - -void GCodeWriter::set_origin_snap(int axis, bool enable, double offset, double bbox_min) -{ - if (axis >= 0 && axis < 3) { - m_origin_snap[axis] = enable; - m_origin_offset[axis] = offset; - m_origin_bbox_min[axis] = bbox_min; - } -} - -Vec3d GCodeWriter::to_machine_coords(const Vec3d &pos) const -{ - if (!is_belt_printer()) - return pos; - // Step 1: Undo the shear/scale applied during slicing. - Vec3d p = m_belt_back_transform.apply(pos); - // Step 2: Apply axis remapping for the machine's coordinate convention. - // BeltRemapAxis: 0-2 = +X/+Y/+Z, 3-5 = -X/-Y/-Z, 6-8 = Rev X/Y/Z - auto remap = [this, &p](int r) -> double { - int axis = r % 3; - if (r < 3) return p[axis]; - if (r < 6) return -p[axis]; - return m_build_vol_max[axis] - p[axis]; - }; - Vec3d result = { remap(m_remap_x), remap(m_remap_y), remap(m_remap_z) }; - // Per-axis origin snap: shift so bbox min on each enabled axis = offset. - for (int i = 0; i < 3; ++i) - if (m_origin_snap[i]) - result[i] -= (m_origin_bbox_min[i] - m_origin_offset[i]); - return result; -} - bool GCodeWriter::supports_separate_travel_acceleration(GCodeFlavor flavor) { return (flavor == gcfRepetier || flavor == gcfMarlinFirmware || flavor == gcfRepRapFirmware); @@ -615,13 +562,7 @@ std::string GCodeWriter::travel_to_xy(const Vec2d &point, const std::string &com Vec2d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset }; GCodeG1Formatter w; - if (is_belt_printer()) { - // Belt printer: transform to machine coordinates (XY travel also needs Z due to YZ rotation) - Vec3d machine = to_machine_coords(Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z())); - w.emit_xyz(machine); - } else { - w.emit_xy(point_on_plate); - } + w.emit_xy(point_on_plate); auto speed = m_is_first_layer ? this->config.get_abs_value("initial_layer_travel_speed") : this->config.travel_speed.value; w.emit_f(speed * 60.0); @@ -635,11 +576,6 @@ it will not perform subsequent lifts, even if Z was raised manually (i.e. with travel_to_z()) and thus _lifted was reduced. */ std::string GCodeWriter::lazy_lift(LiftType lift_type, bool spiral_vase) { - // Belt printer: force NormalLift since SpiralLift and SlopeLift compute slope angles - // that don't account for the YZ coordinate rotation. - if (is_belt_printer()) - lift_type = LiftType::NormalLift; - // check whether the above/below conditions are met double target_lift = 0; { @@ -668,8 +604,7 @@ std::string GCodeWriter::lazy_lift(LiftType lift_type, bool spiral_vase) // BBS: immediately execute an undelayed lift move with a spiral lift pattern // designed specifically for subsequent gcode injection (e.g. timelapse) std::string GCodeWriter::eager_lift(const LiftType type) { - // Belt printer: force NormalLift (SpiralLift/SlopeLift don't account for YZ rotation). - const LiftType effective_type = is_belt_printer() ? LiftType::NormalLift : type; + const LiftType effective_type = type; std::string lift_move; double target_lift = 0; { @@ -756,8 +691,6 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co // / to make the z list early to avoid to hit some warping place when travel is long. Vec2d temp = delta_no_z.normalized() * delta(2) / tan(this->filament()->travel_slope()); Vec3d slope_top_point = Vec3d(temp(0), temp(1), delta(2)) + source; - if (is_belt_printer()) - slope_top_point = to_machine_coords(slope_top_point); GCodeG1Formatter w0; w0.emit_xyz(slope_top_point); w0.emit_f(travel_speed * 60.0); @@ -772,27 +705,18 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co std::string xy_z_move; { - Vec3d emit_target = is_belt_printer() ? to_machine_coords(target) : target; GCodeG1Formatter w0; if (this->is_current_position_clear()) { - w0.emit_xyz(emit_target); + w0.emit_xyz(target); w0.emit_f(travel_speed * 60.0); w0.emit_comment(GCodeWriter::full_gcode_comment, comment); xy_z_move = w0.string(); } else { - if (is_belt_printer()) { - // Belt mode: can't split XY and Z moves independently, emit full XYZ - w0.emit_xyz(emit_target); - w0.emit_f(travel_speed * 60.0); - w0.emit_comment(GCodeWriter::full_gcode_comment, comment); - xy_z_move = w0.string(); - } else { - w0.emit_xy(Vec2d(target.x(), target.y())); - w0.emit_f(travel_speed * 60.0); - w0.emit_comment(GCodeWriter::full_gcode_comment, comment); - xy_z_move = w0.string() + _travel_to_z(target.z(), comment); - } + w0.emit_xy(Vec2d(target.x(), target.y())); + w0.emit_f(travel_speed * 60.0); + w0.emit_comment(GCodeWriter::full_gcode_comment, comment); + xy_z_move = w0.string() + _travel_to_z(target.z(), comment); } } m_pos = dest_point; @@ -818,25 +742,15 @@ std::string GCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &co //BBS: take plate offset into consider Vec3d point_on_plate = { dest_point(0) - m_x_offset, dest_point(1) - m_y_offset, dest_point(2) }; - if (is_belt_printer()) - point_on_plate = to_machine_coords(point_on_plate); std::string out_string; GCodeG1Formatter w; if (!this->is_current_position_clear()) { - if (is_belt_printer()) { - // Belt mode: emit full XYZ since Y and Z are coupled - w.emit_xyz(point_on_plate); - w.emit_f(this->config.travel_speed.value * 60.0); - w.emit_comment(GCodeWriter::full_gcode_comment, comment); - out_string = w.string(); - } else { - //force to move xy first then z after filament change - w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y())); - w.emit_f(this->config.travel_speed.value * 60.0); - w.emit_comment(GCodeWriter::full_gcode_comment, comment); - out_string = w.string() + _travel_to_z(point_on_plate.z(), comment); - } + //force to move xy first then z after filament change + w.emit_xy(Vec2d(point_on_plate.x(), point_on_plate.y())); + w.emit_f(this->config.travel_speed.value * 60.0); + w.emit_comment(GCodeWriter::full_gcode_comment, comment); + out_string = w.string() + _travel_to_z(point_on_plate.z(), comment); } else { GCodeG1Formatter w; w.emit_xyz(point_on_plate); @@ -880,13 +794,7 @@ std::string GCodeWriter::_travel_to_z(double z, const std::string &comment) } GCodeG1Formatter w; - if (is_belt_printer()) { - // Belt printer: a Z-only move in slicing frame needs to emit both Y and Z in machine coords. - Vec3d machine = to_machine_coords(Vec3d(m_pos.x() - m_x_offset, m_pos.y() - m_y_offset, z)); - w.emit_xyz(machine); - } else { - w.emit_z(z); - } + w.emit_z(z); w.emit_f(speed * 60.0); //BBS w.emit_comment(GCodeWriter::full_gcode_comment, comment); @@ -991,12 +899,7 @@ std::string GCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std: Vec2d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset }; GCodeG1Formatter w; - if (is_belt_printer()) { - Vec3d machine = to_machine_coords(Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z())); - w.emit_xyz(machine); - } else { - w.emit_xy(point_on_plate); - } + w.emit_xy(point_on_plate); if (!force_no_extrusion) w.emit_e(filament()->E()); //BBS @@ -1037,8 +940,6 @@ std::string GCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std Vec3d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset, point(2) }; GCodeG1Formatter w; - if (is_belt_printer()) - point_on_plate = to_machine_coords(point_on_plate); w.emit_xyz(point_on_plate); if (!force_no_extrusion) w.emit_e(filament()->E()); diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp index 2334f5d416..c96c3ca2c1 100644 --- a/src/libslic3r/GCodeWriter.hpp +++ b/src/libslic3r/GCodeWriter.hpp @@ -8,12 +8,11 @@ #include "Point.hpp" #include "PrintConfig.hpp" #include "GCode/CoolingBuffer.hpp" -#include "GCode/BeltBackTransform.hpp" - namespace Slic3r { class GCodeWriter { public: + virtual ~GCodeWriter() = default; GCodeConfig config; bool multiple_extruders; @@ -74,21 +73,21 @@ public: std::string set_speed(double F, const std::string &comment = std::string(), const std::string &cooling_marker = std::string()); // SoftFever NOTE: the returned speed is mm/minute double get_current_speed() const { return m_current_speed;} - std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string()); - std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string(), bool force_z = false); + virtual std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string()); + virtual std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string(), bool force_z = false); std::string travel_to_z(double z, const std::string &comment = std::string(), bool force = false); bool will_move_z(double z) const; - std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false); + virtual std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false); //BBS: generate G2 or G3 extrude which moves by arc std::string extrude_arc_to_xy(const Vec2d &point, const Vec2d ¢er_offset, double dE, const bool is_ccw, const std::string &comment = std::string(), bool force_no_extrusion = false); - std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false); + virtual std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false); std::string retract(bool before_wipe = false, double retract_length = 0); std::string retract_for_toolchange(bool before_wipe = false, double retract_length = 0); std::string unretract(); // do lift instantly - std::string eager_lift(const LiftType type); + virtual std::string eager_lift(const LiftType type); // record a lift request, do realy lift in next travel - std::string lazy_lift(LiftType lift_type = LiftType::NormalLift, bool spiral_vase = false); + virtual std::string lazy_lift(LiftType lift_type = LiftType::NormalLift, bool spiral_vase = false); std::string unlift(); const Vec3d& get_position() const { return m_pos; } Vec3d& get_position() { return m_pos; } @@ -126,23 +125,23 @@ public: void set_is_first_layer(bool bval) { m_is_first_layer = bval; } GCodeFlavor get_gcode_flavor() const { return config.gcode_flavor; } - // Belt printer: set the belt angle and precompute sin/cos for coordinate transformation. - void set_belt_angle(double angle_deg); - bool is_belt_printer() const { return m_belt_angle_rad != 0.; } - // Set axis remap for G-code output (BeltRemapAxis enum values). - void set_axis_remap(int rx, int ry, int rz); - // Set build volume extents for Rev remap mode (max X, Y, Z). - void set_build_volume_max(const Vec3d &max); - // Initialize the belt back-transform that undoes slicing shear/scale. - void set_belt_back_transform(const PrintConfig &config); - // Set per-axis origin snap: shifts G-code so bbox min on this axis = offset. - void set_origin_snap(int axis, bool enable, double offset, double bbox_min); - // Transform a point from the slicing frame to machine coordinates. - Vec3d to_machine_coords(const Vec3d &pos) const; - // Returns whether this flavor supports separate print and travel acceleration. static bool supports_separate_travel_acceleration(GCodeFlavor flavor); - private: +protected: + // Position/lift/offset state — accessible to subclasses (e.g. BeltGCodeWriter) + Vec3d m_pos = Vec3d::Zero(); + double m_x_offset{ 0 }; + double m_y_offset{ 0 }; + double m_lifted; + double m_to_lift; + LiftType m_to_lift_type; + bool m_is_first_layer = true; + bool m_is_current_pos_clear = false; + double m_current_speed; + + virtual std::string _travel_to_z(double z, const std::string &comment); + +private: // Extruders are sorted by their ID, so that binary search is possible. std::vector m_filament_extruders; bool m_single_extruder_multi_material; @@ -170,48 +169,21 @@ public: unsigned int m_last_additional_fan_speed; int m_last_bed_temperature; bool m_last_bed_temperature_reached; - double m_lifted; - - // BBS - double m_to_lift; - LiftType m_to_lift_type; - Vec3d m_pos = Vec3d::Zero(); - //BBS: this flag is used to indicate whether the m_pos is real. - //A example that of the first move, the m_pos is zero, but the real position of extruder doesn't - //Pos must be clear after the first xyz travel move - bool m_is_current_pos_clear = false; - //BBS: x, y offset for gcode generated - double m_x_offset{ 0 }; - double m_y_offset{ 0 }; // Orca: slicing resolution in mm double m_resolution = 0.01; - + std::string m_gcode_label_objects_start; std::string m_gcode_label_objects_end; //SoftFever bool m_is_bbl_printers = false; - // Belt printer state - double m_belt_angle_rad = 0.; - int m_remap_x = 0; - int m_remap_y = 1; - int m_remap_z = 2; - Vec3d m_build_vol_max = Vec3d::Zero(); - BeltBackTransform m_belt_back_transform; - bool m_origin_snap[3] = {false, false, false}; - double m_origin_offset[3] = {0., 0., 0.}; // target coord for bbox min - double m_origin_bbox_min[3] = {0., 0., 0.}; // computed bbox min in machine space - double m_current_speed; - bool m_is_first_layer = true; - enum class Acceleration { Travel, Print }; - std::string _travel_to_z(double z, const std::string &comment); std::string _spiral_travel_to_z(double z, const Vec2d &ij_offset, const std::string &comment); std::string _retract(double length, double restart_extra, const std::string &comment); std::string set_acceleration_internal(Acceleration type, unsigned int acceleration); diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 0b7190e8d1..1f787a70cf 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -12,6 +12,7 @@ #include "Thread.hpp" #include "Time.hpp" #include "GCode.hpp" +#include "BeltGCode.hpp" #include "GCode/WipeTower.hpp" #include "GCode/WipeTower2.hpp" #include "Utils.hpp" @@ -2551,12 +2552,17 @@ std::string Print::export_gcode(const std::string& path_template, GCodeProcessor this->set_status(80, message); // The following line may die for multiple reasons. - GCode gcode; + // Factory: use BeltGCode for belt printers, plain GCode otherwise. + std::unique_ptr gcode; + if (m_config.belt_printer.value) + gcode = std::make_unique(); + else + gcode = std::make_unique(); //BBS: compute plate offset for gcode-generator const Vec3d origin = this->get_plate_origin(); - gcode.set_gcode_offset(origin(0), origin(1)); - gcode.do_export(this, path.c_str(), result, thumbnail_cb); - gcode.export_layer_filaments(result); + gcode->set_gcode_offset(origin(0), origin(1)); + gcode->do_export(this, path.c_str(), result, thumbnail_cb); + gcode->export_layer_filaments(result); //BBS result->conflict_result = m_conflict_result; return path.c_str(); diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index c13a1407c0..9a5d000e1c 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -17,6 +17,7 @@ #include "GCode/ThumbnailData.hpp" #include "GCode/GCodeProcessor.hpp" #include "MultiMaterialSegmentation.hpp" +#include "BeltTransform.hpp" #include "libslic3r.h" #include @@ -190,30 +191,7 @@ class ConstSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor double { - int axis = r % 3; - if (r < 3) return v[axis]; - return -v[axis]; - }; - Vec3d mn = bb.min.cast(), mx = bb.max.cast(); - BoundingBoxf3 rbb; - for (int i = 0; i < 8; ++i) { - Vec3d c((i & 1) ? mx.x() : mn.x(), - (i & 2) ? mx.y() : mn.y(), - (i & 4) ? mx.z() : mn.z()); - Vec3d rc(remap_coord(pre_rx, c), remap_coord(pre_ry, c), remap_coord(pre_rz, c)); - if (i == 0) rbb = BoundingBoxf3(rc, rc); - else rbb.merge(rc); - } - return rbb; + return BeltTransformPipeline::remap_bbox(model_object, config); } // Single instance of a PrintObject. diff --git a/src/libslic3r/PrintObject.cpp b/src/libslic3r/PrintObject.cpp index ba2ed408ce..be65c21c59 100644 --- a/src/libslic3r/PrintObject.cpp +++ b/src/libslic3r/PrintObject.cpp @@ -1,5 +1,6 @@ #include "Exception.hpp" #include "Print.hpp" +#include "BeltTransform.hpp" #include "BoundingBox.hpp" #include "ClipperUtils.hpp" #include "ElephantFootCompensation.hpp" @@ -3398,76 +3399,22 @@ void PrintObject::update_slicing_parameters() // Orca: updated function call for XYZ shrinkage compensation if (!m_slicing_params.valid) { coordf_t object_height = this->model_object()->max_z(); - // Belt floor parameters for support clipping (populated below if belt Z-shear is active). - double belt_floor_shear_factor_out = 0.0; - int belt_floor_from_axis_out = 1; - double belt_floor_z_shift_out = 0.0; - // Belt shear/scale/pre-remap may change the effective Z height. + BeltTransformPipeline::BeltFloorParams belt_floor; const auto &pcfg = this->print()->config(); if (pcfg.belt_printer.value) { - BoundingBoxf3 bb = belt_remapped_bbox(*this->model_object(), pcfg); - bool has_preslice_remap = (int(pcfg.belt_preslice_remap_x.value) != int(BeltRemapAxis::PosX) || - int(pcfg.belt_preslice_remap_y.value) != int(BeltRemapAxis::PosY) || - int(pcfg.belt_preslice_remap_z.value) != int(BeltRemapAxis::PosZ)); - if (has_preslice_remap) + BoundingBoxf3 bb = BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg); + if (BeltTransformPipeline::has_preslice_remap(pcfg)) object_height = bb.size().z(); - - bool has_z_shear = pcfg.belt_shear_z.value != BeltShearMode::None; - bool has_z_scale = pcfg.belt_scale_z.value != BeltScaleMode::None; - if (has_z_shear || has_z_scale) { - auto compute_shear_factor = [](BeltShearMode mode, double angle_deg) -> double { - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad), cos_a = std::cos(angle_rad); - switch (mode) { - case BeltShearMode::PosCot: return (sin_a > EPSILON) ? cos_a / sin_a : 0.; - case BeltShearMode::NegCot: return (sin_a > EPSILON) ? -cos_a / sin_a : 0.; - case BeltShearMode::PosTan: return (cos_a > EPSILON) ? sin_a / cos_a : 0.; - case BeltShearMode::NegTan: return (cos_a > EPSILON) ? -sin_a / cos_a : 0.; - default: return 0.; - } - }; - auto compute_scale_factor = [](BeltScaleMode mode, double angle_deg) -> double { - if (mode == BeltScaleMode::None) return 1.; - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad), cos_a = std::cos(angle_rad); - switch (mode) { - case BeltScaleMode::InvSin: return (sin_a > EPSILON) ? 1. / sin_a : 1.; - case BeltScaleMode::InvCos: return (cos_a > EPSILON) ? 1. / cos_a : 1.; - case BeltScaleMode::Sin: return sin_a; - case BeltScaleMode::Cos: return cos_a; - default: return 1.; - } - }; - double shear_factor = has_z_shear ? compute_shear_factor(pcfg.belt_shear_z.value, pcfg.belt_shear_z_angle.value) : 0.; - double scale_z = compute_scale_factor(pcfg.belt_scale_z.value, pcfg.belt_scale_z_angle.value); - if (has_z_shear && std::abs(shear_factor) > EPSILON) { - int from = int(pcfg.belt_shear_z_from.value); - double min_rz = std::numeric_limits::max(); - double max_rz = std::numeric_limits::lowest(); - for (double vz : {bb.min.z(), bb.max.z()}) - for (double vs : {bb.min(from), bb.max(from)}) { - double new_z = scale_z * (vz + shear_factor * vs); - min_rz = std::min(min_rz, new_z); - max_rz = std::max(max_rz, new_z); - } - object_height = max_rz - min_rz; - belt_floor_shear_factor_out = shear_factor; - belt_floor_from_axis_out = from; - // Belt contact surface starts at bb.min.z() pre-shear; add the - // slicing Z-shift that keeps the mesh above Z=0. - // Exact value is patched after slice_volumes() in posSlice. - belt_floor_z_shift_out = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.); - } else { - object_height *= scale_z; - } - } + auto hr = BeltTransformPipeline::compute_belt_height_and_floor(pcfg, bb, object_height); + object_height = hr.object_height; + belt_floor = hr.floor_params; } m_slicing_params = SlicingParameters::create_from_config(pcfg, m_config, object_height, this->object_extruders(), this->print()->shrinkage_compensation()); // Populate belt floor parameters into slicing params for support clipping. - m_slicing_params.belt_floor_shear_factor = belt_floor_shear_factor_out; - m_slicing_params.belt_floor_from_axis = belt_floor_from_axis_out; - m_slicing_params.belt_floor_z_shift = belt_floor_z_shift_out; + m_slicing_params.belt_floor_shear_factor = belt_floor.shear_factor; + m_slicing_params.belt_floor_from_axis = belt_floor.from_axis; + m_slicing_params.belt_floor_z_shift = belt_floor.z_shift; } } @@ -3505,75 +3452,23 @@ SlicingParameters PrintObject::slicing_parameters(const DynamicPrintConfig &full sort_remove_duplicates(object_extruders); //FIXME add painting extruders - // Belt floor parameters for support clipping (populated below if belt Z-shear is active). - double belt_floor_shear_factor_out = 0.0; - int belt_floor_from_axis_out = 1; - double belt_floor_z_shift_out = 0.0; - + BeltTransformPipeline::BeltFloorParams belt_floor; if (object_max_z <= 0.f) { BoundingBoxf3 bb = model_object.raw_bounding_box(); object_max_z = (float)bb.size().z(); - // Belt pre-remap/shear/scale may change the effective Z height. if (print_config.belt_printer.value) { - bb = belt_remapped_bbox(model_object, print_config); - bool has_preslice_remap = (int(print_config.belt_preslice_remap_x.value) != int(BeltRemapAxis::PosX) || - int(print_config.belt_preslice_remap_y.value) != int(BeltRemapAxis::PosY) || - int(print_config.belt_preslice_remap_z.value) != int(BeltRemapAxis::PosZ)); - if (has_preslice_remap) + bb = BeltTransformPipeline::remap_bbox(model_object, print_config); + if (BeltTransformPipeline::has_preslice_remap(print_config)) object_max_z = (float)bb.size().z(); - - bool has_z_shear = print_config.belt_shear_z.value != BeltShearMode::None; - bool has_z_scale = print_config.belt_scale_z.value != BeltScaleMode::None; - if (has_z_shear || has_z_scale) { - auto compute_shear_factor = [](BeltShearMode mode, double angle_deg) -> double { - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad), cos_a = std::cos(angle_rad); - switch (mode) { - case BeltShearMode::PosCot: return (sin_a > EPSILON) ? cos_a / sin_a : 0.; - case BeltShearMode::NegCot: return (sin_a > EPSILON) ? -cos_a / sin_a : 0.; - case BeltShearMode::PosTan: return (cos_a > EPSILON) ? sin_a / cos_a : 0.; - case BeltShearMode::NegTan: return (cos_a > EPSILON) ? -sin_a / cos_a : 0.; - default: return 0.; - } - }; - auto compute_scale_factor = [](BeltScaleMode mode, double angle_deg) -> double { - if (mode == BeltScaleMode::None) return 1.; - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad), cos_a = std::cos(angle_rad); - switch (mode) { - case BeltScaleMode::InvSin: return (sin_a > EPSILON) ? 1. / sin_a : 1.; - case BeltScaleMode::InvCos: return (cos_a > EPSILON) ? 1. / cos_a : 1.; - case BeltScaleMode::Sin: return sin_a; - case BeltScaleMode::Cos: return cos_a; - default: return 1.; - } - }; - double shear_factor = has_z_shear ? compute_shear_factor(print_config.belt_shear_z.value, print_config.belt_shear_z_angle.value) : 0.; - double scale_z = compute_scale_factor(print_config.belt_scale_z.value, print_config.belt_scale_z_angle.value); - if (has_z_shear && std::abs(shear_factor) > EPSILON) { - int from = int(print_config.belt_shear_z_from.value); - double min_rz = std::numeric_limits::max(); - double max_rz = std::numeric_limits::lowest(); - for (double vz : {bb.min.z(), bb.max.z()}) - for (double vs : {bb.min(from), bb.max(from)}) { - double new_z = scale_z * (vz + shear_factor * vs); - min_rz = std::min(min_rz, new_z); - max_rz = std::max(max_rz, new_z); - } - object_max_z = (float)(max_rz - min_rz); - belt_floor_shear_factor_out = shear_factor; - belt_floor_from_axis_out = from; - belt_floor_z_shift_out = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.); - } else { - object_max_z *= (float)scale_z; - } - } + auto hr = BeltTransformPipeline::compute_belt_height_and_floor(print_config, bb, object_max_z); + object_max_z = (float)hr.object_height; + belt_floor = hr.floor_params; } } SlicingParameters params = SlicingParameters::create_from_config(print_config, object_config, object_max_z, object_extruders, object_shrinkage_compensation); - params.belt_floor_shear_factor = belt_floor_shear_factor_out; - params.belt_floor_from_axis = belt_floor_from_axis_out; - params.belt_floor_z_shift = belt_floor_z_shift_out; + params.belt_floor_shear_factor = belt_floor.shear_factor; + params.belt_floor_from_axis = belt_floor.from_axis; + params.belt_floor_z_shift = belt_floor.z_shift; return params; } diff --git a/src/libslic3r/PrintObjectSlice.cpp b/src/libslic3r/PrintObjectSlice.cpp index 4c767ea0ed..3067772f09 100644 --- a/src/libslic3r/PrintObjectSlice.cpp +++ b/src/libslic3r/PrintObjectSlice.cpp @@ -9,6 +9,8 @@ #include "Layer.hpp" #include "MultiMaterialSegmentation.hpp" #include "Print.hpp" +#include "BeltTransform.hpp" +#include "BeltSliceStrategy.hpp" #include "Geometry.hpp" //BBS #include "ShortestPath.hpp" @@ -142,147 +144,11 @@ static std::vector slice_volumes_inner( params_base.closing_radius = print_object_config.slice_closing_radius.value; params_base.extra_offset = 0; params_base.trafo = object_trafo; - if (print_config.belt_printer.value) { - // --- Pre-slice axis remap --- - // Permutes/negates model axes before slicing so the slicer's coordinate - // system matches the physical bed orientation (e.g. XZ bed instead of XY). - int pre_rx = int(print_config.belt_preslice_remap_x.value); - int pre_ry = int(print_config.belt_preslice_remap_y.value); - int pre_rz = int(print_config.belt_preslice_remap_z.value); - - bool has_preslice_remap = (pre_rx != int(BeltRemapAxis::PosX) || - pre_ry != int(BeltRemapAxis::PosY) || - pre_rz != int(BeltRemapAxis::PosZ)); - - if (has_preslice_remap) { - // Build volume extents for Rev mode. - BoundingBoxf bbox_bed(print_config.printable_area.values); - Vec3d vol_max(bbox_bed.max.x(), bbox_bed.max.y(), - print_config.printable_height.value); - - // Each remap value selects a source axis and sign. - // The column vector tells the matrix which input axis feeds this output. - auto remap_column = [](int r) -> Vec3d { - int axis = r % 3; - Vec3d col = Vec3d::Zero(); - if (r < 3) col[axis] = 1.0; // +axis - else if (r < 6) col[axis] = -1.0; // -axis - else col[axis] = -1.0; // Rev: max - pos = -(pos - max) - return col; - }; - - Matrix3d remap_lin; - remap_lin.col(0) = remap_column(pre_rx); - remap_lin.col(1) = remap_column(pre_ry); - remap_lin.col(2) = remap_column(pre_rz); - - // Translation for Rev modes: output = max[src] - input[src]. - Vec3d remap_trans = Vec3d::Zero(); - auto add_rev_offset = [&](int r, int out_axis) { - if (r >= 6) { - int src_axis = r % 3; - remap_trans[out_axis] = vol_max[src_axis]; - } - }; - add_rev_offset(pre_rx, 0); - add_rev_offset(pre_ry, 1); - add_rev_offset(pre_rz, 2); - - Transform3d pre_remap = Transform3d::Identity(); - pre_remap.linear() = remap_lin; - pre_remap.translation() = remap_trans; - - params_base.trafo = pre_remap * params_base.trafo; - } - - // Build per-axis shear matrix from 3 independent axis configs. - auto compute_shear_factor = [](BeltShearMode mode, double angle_deg) -> double { - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad); - double cos_a = std::cos(angle_rad); - switch (mode) { - case BeltShearMode::PosCot: return (sin_a > EPSILON) ? cos_a / sin_a : 0.; - case BeltShearMode::NegCot: return (sin_a > EPSILON) ? -cos_a / sin_a : 0.; - case BeltShearMode::PosTan: return (cos_a > EPSILON) ? sin_a / cos_a : 0.; - case BeltShearMode::NegTan: return (cos_a > EPSILON) ? -sin_a / cos_a : 0.; - default: return 0.; - } - }; - - struct AxisShear { BeltShearMode mode; double angle; int from; }; - AxisShear axes[3] = { - { print_config.belt_shear_x.value, print_config.belt_shear_x_angle.value, int(print_config.belt_shear_x_from.value) }, - { print_config.belt_shear_y.value, print_config.belt_shear_y_angle.value, int(print_config.belt_shear_y_from.value) }, - { print_config.belt_shear_z.value, print_config.belt_shear_z_angle.value, int(print_config.belt_shear_z_from.value) }, - }; - - Transform3d belt_shear = Transform3d::Identity(); - bool has_shear = false; - for (int row = 0; row < 3; ++row) { - if (axes[row].mode != BeltShearMode::None) { - double factor = compute_shear_factor(axes[row].mode, axes[row].angle); - if (std::abs(factor) > EPSILON) { - belt_shear.matrix()(row, axes[row].from) += factor; - has_shear = true; - } - } - } - - // Build per-axis scale matrix. - auto compute_scale_factor = [](BeltScaleMode mode, double angle_deg) -> double { - if (mode == BeltScaleMode::None) return 1.; - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad); - double cos_a = std::cos(angle_rad); - switch (mode) { - case BeltScaleMode::InvSin: return (sin_a > EPSILON) ? 1. / sin_a : 1.; - case BeltScaleMode::InvCos: return (cos_a > EPSILON) ? 1. / cos_a : 1.; - case BeltScaleMode::Sin: return sin_a; - case BeltScaleMode::Cos: return cos_a; - default: return 1.; - } - }; - - Transform3d belt_scale = Transform3d::Identity(); - bool has_scale = false; - double sx = compute_scale_factor(print_config.belt_scale_x.value, print_config.belt_scale_x_angle.value); - double sy = compute_scale_factor(print_config.belt_scale_y.value, print_config.belt_scale_y_angle.value); - double sz = compute_scale_factor(print_config.belt_scale_z.value, print_config.belt_scale_z_angle.value); - if (std::abs(sx - 1.) > EPSILON || std::abs(sy - 1.) > EPSILON || std::abs(sz - 1.) > EPSILON) { - belt_scale.matrix()(0, 0) = sx; - belt_scale.matrix()(1, 1) = sy; - belt_scale.matrix()(2, 2) = sz; - has_scale = true; - } - - // Apply: scale * shear * trafo (shear first, then scale). - if (has_shear || has_scale) - params_base.trafo = belt_scale * belt_shear * params_base.trafo; - - // After pre-remap/shear/scale, the mesh may clip through the build - // plate (Z < 0). Detect this and shift the mesh up along slicer Z. - if (has_preslice_remap || has_shear || has_scale) { - Transform3d combined = params_base.trafo; - double min_z = std::numeric_limits::max(); - for (const ModelVolume *mv : model_volumes) { - if (!mv->is_model_part()) continue; - for (const stl_vertex &v : mv->mesh().its.vertices) { - Vec3d pt = combined * v.cast(); - min_z = std::min(min_z, pt.z()); - } - } - double belt_z_shift_val = (min_z < 0. && min_z != std::numeric_limits::max()) ? -min_z : 0.; - BOOST_LOG_TRIVIAL(warning) << "Belt Z-shift: min_z=" << min_z - << " z_shift=" << belt_z_shift_val - << " trafo_z=" << object_trafo.matrix()(2, 3); - if (belt_z_shift_val > 0.) { - Transform3d z_shift = Transform3d::Identity(); - z_shift.matrix()(2, 3) = belt_z_shift_val; - params_base.trafo = z_shift * params_base.trafo; - } - if (out_belt_min_z) - *out_belt_min_z = (min_z != std::numeric_limits::max()) ? min_z : 0.; - } + { + // Belt printer: apply pre-slice transforms (remap, shear, scale, z-shift) via strategy. + auto belt_strategy = BeltSliceStrategy::create(print_config); + if (belt_strategy) + belt_strategy->apply_to_trafo(params_base.trafo, model_volumes, out_belt_min_z); } //BBS: 0.0025mm is safe enough to simplify the data to speed slicing up for high-resolution model. //Also has on influence on arc fitting which has default resolution 0.0125mm. @@ -968,11 +834,8 @@ void PrintObject::slice() // Without pre-remap, the belt surface IS at Z=0 and bb.min.z() is // already folded into m_belt_min_z, so use 0. const auto &pcfg = this->print()->config(); - bool has_preslice_remap = (int(pcfg.belt_preslice_remap_x.value) != int(BeltRemapAxis::PosX) || - int(pcfg.belt_preslice_remap_y.value) != int(BeltRemapAxis::PosY) || - int(pcfg.belt_preslice_remap_z.value) != int(BeltRemapAxis::PosZ)); - double belt_surface_z = has_preslice_remap - ? belt_remapped_bbox(*this->model_object(), pcfg).min.z() : 0.; + double belt_surface_z = BeltTransformPipeline::has_preslice_remap(pcfg) + ? BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg).min.z() : 0.; m_slicing_params.belt_floor_z_shift = belt_surface_z + z_shift_val; } @@ -1025,18 +888,6 @@ void PrintObject::slice() << " belt_shear_z_global=" << pcfg.belt_shear_z_global.value << " object=" << this->model_object()->name; if (pcfg.belt_printer.value) { - auto compute_shear_factor = [](BeltShearMode mode, double angle_deg) -> double { - double angle_rad = Geometry::deg2rad(angle_deg); - double sin_a = std::sin(angle_rad); - double cos_a = std::cos(angle_rad); - switch (mode) { - case BeltShearMode::PosCot: return (sin_a > EPSILON) ? cos_a / sin_a : 0.; - case BeltShearMode::NegCot: return (sin_a > EPSILON) ? -cos_a / sin_a : 0.; - case BeltShearMode::PosTan: return (cos_a > EPSILON) ? sin_a / cos_a : 0.; - case BeltShearMode::NegTan: return (cos_a > EPSILON) ? -sin_a / cos_a : 0.; - default: return 0.; - } - }; Point inst_shift = this->instances().empty() ? Point(0, 0) : this->instances().front().shift - this->center_offset(); @@ -1058,7 +909,7 @@ void PrintObject::slice() // PrintObjects so the lowest-positioned object stays at Z=0. const auto &za = gaxes[2]; // Z row if (za.global && za.mode != BeltShearMode::None && za.from < 2) { - double factor = compute_shear_factor(za.mode, za.angle); + double factor = BeltTransformPipeline::compute_shear_factor(za.mode, za.angle); // The global Z offset accounts for the instance's position- // dependent shear contribution. m_belt_min_z is the minimum Z // of the mesh after pre_remap + shear + trafo_centered, which @@ -1066,12 +917,8 @@ void PrintObject::slice() // Subtract the belt surface's centered Z position so we get // only the shear-induced contribution (same correction as the // belt_floor_z_shift fix). - // Same pre-remap guard as belt_floor_z_shift above. - bool has_preslice_remap2 = (int(pcfg.belt_preslice_remap_x.value) != int(BeltRemapAxis::PosX) || - int(pcfg.belt_preslice_remap_y.value) != int(BeltRemapAxis::PosY) || - int(pcfg.belt_preslice_remap_z.value) != int(BeltRemapAxis::PosZ)); - double belt_surface_z = has_preslice_remap2 - ? belt_remapped_bbox(*this->model_object(), this->print()->config()).min.z() : 0.; + double belt_surface_z = BeltTransformPipeline::has_preslice_remap(pcfg) + ? BeltTransformPipeline::remap_bbox(*this->model_object(), pcfg).min.z() : 0.; double shear_min_z = m_belt_min_z - belt_surface_z; Point phys = inst_shift; // already has center_offset subtracted double center_on_axis = (za.from == 0) ? unscale(phys.x()) : unscale(phys.y()); diff --git a/src/libslic3r/Support/BeltFloorContext.cpp b/src/libslic3r/Support/BeltFloorContext.cpp new file mode 100644 index 0000000000..d2ffcfd397 --- /dev/null +++ b/src/libslic3r/Support/BeltFloorContext.cpp @@ -0,0 +1,124 @@ +#include "BeltFloorContext.hpp" + +#include +#include + +namespace Slic3r { + +bool BeltFloorContext::init(const SlicingParameters &sp, const PrintConfig &pcfg) +{ + m_active = false; + m_shear_factor = sp.belt_floor_shear_factor; + m_from_axis = sp.belt_floor_from_axis; + m_z_shift = sp.belt_floor_z_shift; + m_floor_offset = pcfg.belt_support_floor_offset.value; + + if (std::abs(m_shear_factor) < EPSILON) + return false; + + m_active = true; + return true; +} + +bool BeltFloorContext::init_local(const SlicingParameters &sp, const PrintConfig &pcfg, + double global_z_offset) +{ + if (!init(sp, pcfg)) + return false; + // Local Z: subtract the global Z offset so polygon computation + // works in the object's local coordinate space. + m_z_shift -= global_z_offset; + return true; +} + +Polygons BeltFloorContext::surface_polygon(coordf_t print_z) const +{ + return half_plane(print_z, /*belt_surface=*/true); +} + +Polygons BeltFloorContext::valid_region_polygon(coordf_t print_z) const +{ + return half_plane(print_z, /*belt_surface=*/false); +} + +double BeltFloorContext::floor_print_z(const Point &pos_slicing) const +{ + if (!m_active) + return -std::numeric_limits::infinity(); + double pos = unscale(m_from_axis == 0 ? pos_slicing.x() : pos_slicing.y()); + return m_shear_factor * pos + m_floor_offset + m_z_shift; +} + +std::vector BeltFloorContext::compute_per_layer_floors( + size_t num_layers, + const std::function &layer_print_z) const +{ + std::vector result(num_layers); + if (!m_active) + return result; + for (size_t i = 0; i < num_layers; ++i) + result[i] = surface_polygon(layer_print_z(i)); + return result; +} + +Polygons BeltFloorContext::half_plane(coordf_t print_z, bool belt_surface) const +{ + if (!m_active) + return {}; + + const double cutoff = (print_z - m_z_shift - m_floor_offset) / m_shear_factor; + const coord_t cutoff_scaled = scale_(cutoff); + const coord_t large_bound = scale_(1e3); + + // The belt surface is on one side of the cutoff line; the valid region + // is on the other side. Which side depends on shear_factor sign. + // + // belt_surface=true → the belt side (where support should NOT exist) + // belt_surface=false → the valid side (where support IS allowed) + // + // For shear_factor > 0: belt surface is from_axis >= cutoff + // For shear_factor < 0: belt surface is from_axis <= cutoff + bool high_side = (m_shear_factor > 0) == belt_surface; + + Polygon poly; + if (m_from_axis == 0) { + if (high_side) { + // X >= cutoff + poly.points = { + Point(cutoff_scaled, -large_bound), + Point(large_bound, -large_bound), + Point(large_bound, large_bound), + Point(cutoff_scaled, large_bound) + }; + } else { + // X < cutoff + poly.points = { + Point(-large_bound, -large_bound), + Point(cutoff_scaled, -large_bound), + Point(cutoff_scaled, large_bound), + Point(-large_bound, large_bound) + }; + } + } else { + if (high_side) { + // Y >= cutoff + poly.points = { + Point(-large_bound, cutoff_scaled), + Point( large_bound, cutoff_scaled), + Point( large_bound, large_bound), + Point(-large_bound, large_bound) + }; + } else { + // Y < cutoff + poly.points = { + Point(-large_bound, -large_bound), + Point( large_bound, -large_bound), + Point( large_bound, cutoff_scaled), + Point(-large_bound, cutoff_scaled) + }; + } + } + return { poly }; +} + +} // namespace Slic3r diff --git a/src/libslic3r/Support/BeltFloorContext.hpp b/src/libslic3r/Support/BeltFloorContext.hpp new file mode 100644 index 0000000000..7b8b704ef6 --- /dev/null +++ b/src/libslic3r/Support/BeltFloorContext.hpp @@ -0,0 +1,74 @@ +#pragma once + +#include "../libslic3r.h" +#include "../Point.hpp" +#include "../Polygon.hpp" +#include "../Slicing.hpp" +#include "../PrintConfig.hpp" + +namespace Slic3r { + +class PrintObject; + +// Belt floor context: encapsulates the parameters and polygon computation +// for belt printer floor clipping in support generation. +// +// All belt floor code across SupportMaterial, TreeSupport, TreeSupport3D, +// and TreeModelVolumes uses the same 4 parameters and the same 4-case +// polygon construction. This class consolidates that logic. +// +// Construct once per PrintObject, then call surface_polygon() or +// valid_region_polygon() per layer with the layer's print_z. +class BeltFloorContext +{ +public: + BeltFloorContext() = default; + + // Initialize from slicing parameters and print config. + // Uses global Z coordinates (for SupportMaterial, non-organic TreeSupport). + bool init(const SlicingParameters &sp, const PrintConfig &pcfg); + + // Initialize with a Z offset subtracted from z_shift. + // Uses local Z coordinates (for TreeSupport3D, TreeModelVolumes organic pipeline). + bool init_local(const SlicingParameters &sp, const PrintConfig &pcfg, + double global_z_offset); + + bool is_active() const { return m_active; } + + // Compute the belt-side half-plane polygon at a given print_z. + // This is the region where the belt surface exists. + Polygons surface_polygon(coordf_t print_z) const; + + // Compute the valid-region half-plane polygon at a given print_z. + // This is the complement: the region where support is allowed. + Polygons valid_region_polygon(coordf_t print_z) const; + + // Compute the belt floor Z position at a given XY position (in slicing coords). + // Returns -infinity if not active. + double floor_print_z(const Point &pos_slicing) const; + + // Pre-compute belt floor polygons for a range of layers. + // layer_print_z(i) returns the print_z for layer index i. + std::vector compute_per_layer_floors( + size_t num_layers, + const std::function &layer_print_z) const; + + // Accessors + double shear_factor() const { return m_shear_factor; } + int from_axis() const { return m_from_axis; } + double z_shift() const { return m_z_shift; } + double floor_offset() const { return m_floor_offset; } + +private: + bool m_active = false; + double m_shear_factor = 0.0; + int m_from_axis = 1; // 0=X, 1=Y + double m_z_shift = 0.0; + double m_floor_offset = 0.0; + + // Internal: compute the raw half-plane polygon. + // If belt_surface=true, returns the belt side; otherwise the valid (complement) side. + Polygons half_plane(coordf_t print_z, bool belt_surface) const; +}; + +} // namespace Slic3r diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index d70153a7dd..c1f732bc6d 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -5,6 +5,7 @@ #include "Print.hpp" #include "SupportMaterial.hpp" #include "SupportCommon.hpp" +#include "BeltFloorContext.hpp" #include "Geometry.hpp" #include "Point.hpp" #include "MutablePolygon.hpp" @@ -625,64 +626,10 @@ Polygons belt_floor_surface_polygon( const PrintObject &object, coordf_t print_z) { - const double shear_factor = slicing_params.belt_floor_shear_factor; - if (std::abs(shear_factor) < EPSILON) + BeltFloorContext ctx; + if (!ctx.init(slicing_params, print_config)) return {}; - - const int from_axis = slicing_params.belt_floor_from_axis; // 0=X, 1=Y - const double floor_offset = print_config.belt_support_floor_offset.value; - - // Belt floor line in slicing coordinates: Z = sf * Y + z_shift. - // z_shift accounts for the upward shift applied when post-shear geometry - // extends below the bed (overhangs). Solving for Y: - // cutoff = (print_z - z_shift - floor_offset) / sf - const double z_shift = slicing_params.belt_floor_z_shift; - const double cutoff = (print_z - z_shift - floor_offset) / shear_factor; - const coord_t cutoff_scaled = scale_(cutoff); - const coord_t large_bound = scale_(1e4); - - // Build the belt-side half-plane (inverted from the valid region). - // If shear_factor > 0: valid region is from_axis < cutoff, so belt surface is from_axis >= cutoff. - // If shear_factor < 0: valid region is from_axis > cutoff, so belt surface is from_axis <= cutoff. - Polygon belt_poly; - if (from_axis == 0) { - if (shear_factor > 0) { - // Belt surface: X >= cutoff - belt_poly.points = { - Point(cutoff_scaled, -large_bound), - Point(large_bound, -large_bound), - Point(large_bound, large_bound), - Point(cutoff_scaled, large_bound) - }; - } else { - // Belt surface: X <= cutoff - belt_poly.points = { - Point(-large_bound, -large_bound), - Point(cutoff_scaled, -large_bound), - Point(cutoff_scaled, large_bound), - Point(-large_bound, large_bound) - }; - } - } else { - if (shear_factor > 0) { - // Belt surface: Y >= cutoff - belt_poly.points = { - Point(-large_bound, cutoff_scaled), - Point( large_bound, cutoff_scaled), - Point( large_bound, large_bound), - Point(-large_bound, large_bound) - }; - } else { - // Belt surface: Y <= cutoff - belt_poly.points = { - Point(-large_bound, -large_bound), - Point( large_bound, -large_bound), - Point( large_bound, cutoff_scaled), - Point(-large_bound, cutoff_scaled) - }; - } - } - return { belt_poly }; + return ctx.surface_polygon(print_z); } // Belt printer: compute the valid-region half-plane polygon at a given print_z. @@ -694,58 +641,10 @@ static Polygons belt_floor_valid_region_polygon( const PrintObject &object, coordf_t print_z) { - const double shear_factor = slicing_params.belt_floor_shear_factor; - if (std::abs(shear_factor) < EPSILON) + BeltFloorContext ctx; + if (!ctx.init(slicing_params, print_config)) return {}; - - const int from_axis = slicing_params.belt_floor_from_axis; - const double floor_offset = print_config.belt_support_floor_offset.value; - - const double z_shift = slicing_params.belt_floor_z_shift; - const double cutoff = (print_z - z_shift - floor_offset) / shear_factor; - const coord_t cutoff_scaled = scale_(cutoff); - const coord_t large_bound = scale_(1e4); - - // Valid region: the complement of the belt surface polygon. - Polygon valid_poly; - if (from_axis == 0) { - if (shear_factor > 0) { - // Valid: X < cutoff - valid_poly.points = { - Point(-large_bound, -large_bound), - Point(cutoff_scaled, -large_bound), - Point(cutoff_scaled, large_bound), - Point(-large_bound, large_bound) - }; - } else { - // Valid: X > cutoff - valid_poly.points = { - Point(cutoff_scaled, -large_bound), - Point(large_bound, -large_bound), - Point(large_bound, large_bound), - Point(cutoff_scaled, large_bound) - }; - } - } else { - if (shear_factor > 0) { - // Valid: Y < cutoff - valid_poly.points = { - Point(-large_bound, -large_bound), - Point( large_bound, -large_bound), - Point( large_bound, cutoff_scaled), - Point(-large_bound, cutoff_scaled) - }; - } else { - // Valid: Y > cutoff - valid_poly.points = { - Point(-large_bound, cutoff_scaled), - Point( large_bound, cutoff_scaled), - Point( large_bound, large_bound), - Point(-large_bound, large_bound) - }; - } - } - return { valid_poly }; + return ctx.valid_region_polygon(print_z); } // Collect outer contours of all slices of this layer. @@ -3373,22 +3272,18 @@ static void trim_support_layers_by_belt_floor( const PrintObject &object, SupportGeneratorLayersPtr &support_layers) { - if (std::abs(slicing_params.belt_floor_shear_factor) < EPSILON) + BeltFloorContext ctx; + if (!ctx.init(slicing_params, print_config)) return; if (print_config.belt_support_floor_mode.value != BeltSupportFloorMode::GeneratorOnly) return; tbb::parallel_for(tbb::blocked_range(0, support_layers.size()), [&](const tbb::blocked_range &range) { - for (size_t i = range.begin(); i < range.end(); ++ i) { - SupportGeneratorLayer *layer = support_layers[i]; - if (layer->polygons.empty()) - continue; - Polygons belt_surface = belt_floor_surface_polygon( - slicing_params, print_config, object, layer->print_z); - if (! belt_surface.empty()) - layer->polygons = diff(layer->polygons, belt_surface); - } + for (size_t i = range.begin(); i < range.end(); ++i) + if (support_layers[i]) + support_layers[i]->polygons = diff(support_layers[i]->polygons, + ctx.surface_polygon(support_layers[i]->print_z)); }); } diff --git a/src/libslic3r/Support/TreeModelVolumes.cpp b/src/libslic3r/Support/TreeModelVolumes.cpp index ec38ffed73..ebc3d2c958 100644 --- a/src/libslic3r/Support/TreeModelVolumes.cpp +++ b/src/libslic3r/Support/TreeModelVolumes.cpp @@ -8,6 +8,7 @@ #include "TreeModelVolumes.hpp" #include "TreeSupportCommon.hpp" +#include "BeltFloorContext.hpp" #include "../BuildVolume.hpp" #include "../ClipperUtils.hpp" @@ -101,13 +102,11 @@ TreeModelVolumes::TreeModelVolumes( { const auto &sp = print_object.slicing_parameters(); const auto &pcfg = print_object.print()->config(); - const double sf = sp.belt_floor_shear_factor; - if (std::abs(sf) > EPSILON + BeltFloorContext ctx; + ctx.init_local(sp, pcfg, print_object.belt_global_z_offset()); + if (ctx.is_active() && std::abs(print_object.belt_global_z_offset()) > EPSILON && pcfg.belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { - const int from_axis = sp.belt_floor_from_axis; - const double floor_off = pcfg.belt_support_floor_offset.value; - const double z_shift = sp.belt_floor_z_shift - print_object.belt_global_z_offset(); size_t num_layers_needed = print_object.layer_count(); // Ensure m_anti_overhang is large enough. if (m_anti_overhang.size() < num_layers_needed) @@ -115,22 +114,7 @@ TreeModelVolumes::TreeModelVolumes( for (size_t layer_idx = 0; layer_idx < num_layers_needed; ++layer_idx) { double print_z = print_object.get_layer(layer_idx)->print_z - print_object.belt_global_z_offset(); - double cutoff = (print_z - z_shift - floor_off) / sf; - coord_t cutoff_sc = scale_(cutoff); - coord_t big = scale_(1e4); - Polygon belt_poly; - if (from_axis == 0) { - if (sf > 0) - belt_poly.points = {{cutoff_sc,-big},{big,-big},{big,big},{cutoff_sc,big}}; - else - belt_poly.points = {{-big,-big},{cutoff_sc,-big},{cutoff_sc,big},{-big,big}}; - } else { - if (sf > 0) - belt_poly.points = {{-big,cutoff_sc},{big,cutoff_sc},{big,big},{-big,big}}; - else - belt_poly.points = {{-big,-big},{big,-big},{big,cutoff_sc},{-big,cutoff_sc}}; - } - append(m_anti_overhang[layer_idx], Polygons{belt_poly}); + append(m_anti_overhang[layer_idx], ctx.surface_polygon(print_z)); } } } @@ -180,40 +164,20 @@ TreeModelVolumes::TreeModelVolumes( // Branches grow toward the belt and their slices are clipped at the belt // surface in organic_draw_branches(). The organic pipeline works in LOCAL // Z (no global_z_offset), so use local z_shift and local print_z. - const auto &slicing_params = print_object.slicing_parameters(); - const auto &pcfg = print_object.print()->config(); - const double sf = slicing_params.belt_floor_shear_factor; - if (std::abs(sf) > EPSILON - && pcfg.belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { - const int from_axis = slicing_params.belt_floor_from_axis; - const double floor_off = pcfg.belt_support_floor_offset.value; - // Subtract global_z_offset to get the LOCAL z_shift — the organic - // pipeline's Z coordinates don't include the global offset. - const double z_shift = slicing_params.belt_floor_z_shift - - print_object.belt_global_z_offset(); - m_belt_floor.assign(num_layers, Polygons{}); - for (size_t layer_idx = 0; layer_idx < num_layers; ++layer_idx) { - // Use local print_z (subtract global offset from object layer). - double print_z = (layer_idx >= num_raft_layers) - ? print_object.get_layer(layer_idx - num_raft_layers)->print_z - - print_object.belt_global_z_offset() - : 0.; - double cutoff = (print_z - z_shift - floor_off) / sf; - coord_t cutoff_sc = scale_(cutoff); - coord_t big = scale_(1e4); - Polygon belt_poly; - if (from_axis == 0) { - if (sf > 0) - belt_poly.points = {{cutoff_sc,-big},{big,-big},{big,big},{cutoff_sc,big}}; - else - belt_poly.points = {{-big,-big},{cutoff_sc,-big},{cutoff_sc,big},{-big,big}}; - } else { - if (sf > 0) - belt_poly.points = {{-big,cutoff_sc},{big,cutoff_sc},{big,big},{-big,big}}; - else - belt_poly.points = {{-big,-big},{big,-big},{big,cutoff_sc},{-big,cutoff_sc}}; - } - m_belt_floor[layer_idx] = { belt_poly }; + { + const auto &slicing_params = print_object.slicing_parameters(); + const auto &pcfg2 = print_object.print()->config(); + BeltFloorContext ctx; + ctx.init_local(slicing_params, pcfg2, print_object.belt_global_z_offset()); + if (ctx.is_active() + && pcfg2.belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { + m_belt_floor = ctx.compute_per_layer_floors(num_layers, [&](size_t layer_idx) -> double { + // Use local print_z (subtract global offset from object layer). + return (layer_idx >= num_raft_layers) + ? print_object.get_layer(layer_idx - num_raft_layers)->print_z + - print_object.belt_global_z_offset() + : 0.; + }); } } } diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index 60e681add9..6c359115da 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -14,6 +14,7 @@ #include "TreeSupportCommon.hpp" #include "TreeSupport.hpp" #include "TreeSupport3D.hpp" +#include "BeltFloorContext.hpp" #include #include @@ -639,16 +640,10 @@ TreeSupport::TreeSupport(PrintObject& object, const SlicingParameters &slicing_p double TreeSupport::belt_floor_print_z(const Point &pos_slicing) const { - double sf = m_slicing_params.belt_floor_shear_factor; - if (std::abs(sf) < EPSILON) - return -std::numeric_limits::max(); // no belt floor - int from = m_slicing_params.belt_floor_from_axis; - // Belt floor in slicing coords: Z = sf * Y + z_shift + floor_offset. - // Inverse of cutoff = (Z - z_shift - floor_offset) / sf. - double pos = unscale(from == 0 ? pos_slicing.x() : pos_slicing.y()); - double floor_offset = m_print_config->belt_support_floor_offset.value; - double z_shift = m_slicing_params.belt_floor_z_shift; - return sf * pos + floor_offset + z_shift; + BeltFloorContext ctx; + if (!ctx.init(m_slicing_params, *m_print_config)) + return -std::numeric_limits::infinity(); + return ctx.floor_print_z(pos_slicing); } #define SUPPORT_SURFACES_OFFSET_PARAMETERS ClipperLib::jtSquare, 0. @@ -1738,18 +1733,11 @@ void TreeSupport::generate() // layer and clipped at the belt surface. These layers bypass the tree // algorithm entirely — they're pure geometry added after draw_circles(). { - const auto &sp = m_slicing_params; - const auto &pcfg = *m_print_config; - const double sf = sp.belt_floor_shear_factor; - if (std::abs(sf) > EPSILON - && pcfg.belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly + BeltFloorContext ctx; + if (ctx.init(m_slicing_params, *m_print_config) + && m_print_config->belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly && m_object->support_layer_count() > 0) { - const int from_axis = sp.belt_floor_from_axis; - const double floor_off = pcfg.belt_support_floor_offset.value; - // Support layer print_z values are in GLOBAL Z (non-organic inherits - // from object layers which include global_z_offset). Use the GLOBAL - // belt_floor_z_shift to match. - const double z_shift = sp.belt_floor_z_shift; + const auto &sp = m_slicing_params; // Find the lowest non-empty, non-brim support layer. ExPolygons source_areas; double source_z = 0; @@ -1778,7 +1766,7 @@ void TreeSupport::generate() } if (!source_areas.empty()) { BoundingBoxf3 bb = belt_remapped_bbox(*m_object->model_object(), m_object->print()->config()); - double from_extent = std::abs(bb.min(from_axis)); + double from_extent = std::abs(bb.min(ctx.from_axis())); double bb_min_z = std::abs(bb.min.z()); double first_z = m_object->get_support_layer(0)->print_z; // Depth = from-axis extent + pre-shear bbox Z offset (ensure_on_bed @@ -1792,18 +1780,8 @@ void TreeSupport::generate() for (int i = num_extra; i >= 1 && !prev_areas.empty(); --i) { double print_z = first_z - i * sp.layer_height; if (print_z < -sp.layer_height) continue; - double cutoff = (print_z - z_shift - floor_off) / sf; - coord_t cutoff_sc = scale_(cutoff); - coord_t big = scale_(1e3); - Polygon belt_poly; - if (from_axis == 0) { - if (sf > 0) belt_poly.points = {{cutoff_sc,-big},{big,-big},{big,big},{cutoff_sc,big}}; - else belt_poly.points = {{-big,-big},{cutoff_sc,-big},{cutoff_sc,big},{-big,big}}; - } else { - if (sf > 0) belt_poly.points = {{-big,cutoff_sc},{big,cutoff_sc},{big,big},{-big,big}}; - else belt_poly.points = {{-big,-big},{big,-big},{big,cutoff_sc},{-big,cutoff_sc}}; - } - ExPolygons clipped = diff_ex(source_areas, Polygons{belt_poly}); + Polygons belt_surface = ctx.surface_polygon(print_z); + ExPolygons clipped = diff_ex(source_areas, belt_surface); if (clipped.empty()) continue; SupportLayer *sl = new SupportLayer(0, 0, m_object, sp.layer_height, print_z, -1); sl->base_areas = clipped; @@ -2250,37 +2228,19 @@ void TreeSupport::draw_circles() // Belt floor: clip tree support polygons by the belt surface plane. // ts_layer->print_z is at LOCAL Z (global offset applied later in - // _generate_support_material), but belt_floor_z_shift includes - // global_z_offset — subtract it to get the cutoff in local coords. - if (std::abs(m_slicing_params.belt_floor_shear_factor) > EPSILON - && m_print_config->belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { - const double sf = m_slicing_params.belt_floor_shear_factor; - const int from_axis = m_slicing_params.belt_floor_from_axis; - const double floor_off = m_print_config->belt_support_floor_offset.value; - const double z_shift_local = m_slicing_params.belt_floor_z_shift - - m_object->belt_global_z_offset(); - const double cutoff = (ts_layer->print_z - z_shift_local - floor_off) / sf; - const coord_t cutoff_sc = scale_(cutoff); - const coord_t big = scale_(1e4); - - Polygon belt_poly; - if (from_axis == 0) { - if (sf > 0) - belt_poly.points = { {cutoff_sc,-big}, {big,-big}, {big,big}, {cutoff_sc,big} }; - else - belt_poly.points = { {-big,-big}, {cutoff_sc,-big}, {cutoff_sc,big}, {-big,big} }; - } else { - if (sf > 0) - belt_poly.points = { {-big,cutoff_sc}, {big,cutoff_sc}, {big,big}, {-big,big} }; - else - belt_poly.points = { {-big,-big}, {big,-big}, {big,cutoff_sc}, {-big,cutoff_sc} }; + // _generate_support_material), so use init_local to subtract + // belt_global_z_offset from z_shift. + { + BeltFloorContext ctx; + if (ctx.init_local(m_slicing_params, *m_print_config, m_object->belt_global_z_offset()) + && m_print_config->belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { + Polygons belt_surface = ctx.surface_polygon(ts_layer->print_z); + base_areas = diff_ex(base_areas, belt_surface); + roof_areas = diff_ex(roof_areas, belt_surface); + roof_1st_layer = diff_ex(roof_1st_layer, belt_surface); + floor_areas = diff_ex(floor_areas, belt_surface); + roof_gap_areas = diff_ex(roof_gap_areas, belt_surface); } - Polygons belt_surface = { belt_poly }; - base_areas = diff_ex(base_areas, belt_surface); - roof_areas = diff_ex(roof_areas, belt_surface); - roof_1st_layer = diff_ex(roof_1st_layer, belt_surface); - floor_areas = diff_ex(floor_areas, belt_surface); - roof_gap_areas = diff_ex(roof_gap_areas, belt_surface); } if (SQUARE_SUPPORT) { @@ -3593,6 +3553,20 @@ TreeSupportData::TreeSupportData(const PrintObject &object, coordf_t xy_distance poly.simplify(scale_(m_radius_sample_resolution), &outline); } + // Belt floor: add belt surface polygon to layer outlines so the + // collision system treats the belt as a physical surface. + { + BeltFloorContext ctx; + double local_print_z = layer->print_z - object.belt_global_z_offset(); + if (ctx.init_local(object.slicing_parameters(), object.print()->config(), + object.belt_global_z_offset()) + && object.print()->config().belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { + Polygons belt_surface = ctx.surface_polygon(local_print_z); + for (auto &p : belt_surface) + outline.emplace_back(ExPolygon(p)); + } + } + if (layer_nr == 0) m_layer_outlines_below.push_back(outline); else diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index 9d35cb2dd8..8a07609930 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -19,6 +19,7 @@ #include "Polygon.hpp" #include "Polyline.hpp" #include "MutablePolygon.hpp" +#include "BeltFloorContext.hpp" #include "SupportCommon.hpp" #include "TriangleMeshSlicer.hpp" #include "TreeSupport.hpp" @@ -3373,8 +3374,9 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons PrintObject &po = *print.get_object(processing.second.front()); const auto &sp = po.slicing_parameters(); const auto &pcfg = po.print()->config(); - const double sf = sp.belt_floor_shear_factor; - if (std::abs(sf) > EPSILON && std::abs(po.belt_global_z_offset()) > EPSILON + BeltFloorContext ctx; + ctx.init_local(sp, pcfg, po.belt_global_z_offset()); + if (ctx.is_active() && std::abs(po.belt_global_z_offset()) > EPSILON && pcfg.belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { // z_shift_local is the belt surface height at Y=0 in local coords. // Extend below the belt so the base expansion and build-plate @@ -3598,32 +3600,16 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons // Compute the belt floor polygon directly from each layer's print_z // rather than mapping to a layer index (avoids index mismatch issues). { - const auto &sp = print_object.slicing_parameters(); - const double sf = sp.belt_floor_shear_factor; - const double z_shift = sp.belt_floor_z_shift - print_object.belt_global_z_offset(); - const double floor_off = print_object.print()->config().belt_support_floor_offset.value; - const int from_axis = sp.belt_floor_from_axis; - if (std::abs(sf) > EPSILON - && print_object.print()->config().belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { + const auto &sp = print_object.slicing_parameters(); + const auto &pcfg = print_object.print()->config(); + BeltFloorContext ctx; + ctx.init_local(sp, pcfg, print_object.belt_global_z_offset()); + if (ctx.is_active() + && pcfg.belt_support_floor_mode.value == BeltSupportFloorMode::GeneratorOnly) { tbb::parallel_for_each(layers_sorted.begin(), layers_sorted.end(), [&](SupportGeneratorLayer *layer) { if (!layer || layer->polygons.empty()) return; - double cutoff = (layer->print_z - z_shift - floor_off) / sf; - coord_t cutoff_sc = scale_(cutoff); - coord_t big = scale_(1e4); - Polygon belt_poly; - if (from_axis == 0) { - if (sf > 0) - belt_poly.points = {{cutoff_sc,-big},{big,-big},{big,big},{cutoff_sc,big}}; - else - belt_poly.points = {{-big,-big},{cutoff_sc,-big},{cutoff_sc,big},{-big,big}}; - } else { - if (sf > 0) - belt_poly.points = {{-big,cutoff_sc},{big,cutoff_sc},{big,big},{-big,big}}; - else - belt_poly.points = {{-big,-big},{big,-big},{big,cutoff_sc},{-big,cutoff_sc}}; - } - layer->polygons = diff(layer->polygons, Polygons{belt_poly}); + layer->polygons = diff(layer->polygons, ctx.surface_polygon(layer->print_z)); }); } }