mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-07-16 15:32:09 +00:00
Part 3.1: refactor BeltTransform pipeline
add BeltGCodeWriter add BeltGCode consolidate changes into shared classes for BeltGcode
This commit is contained in:
119
src/libslic3r/BeltGCode.cpp
Normal file
119
src/libslic3r/BeltGCode.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
#include "BeltGCode.hpp"
|
||||
#include "BeltGCodeWriter.hpp"
|
||||
#include "BeltTransform.hpp"
|
||||
#include "Print.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
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<BeltGCodeWriter>();
|
||||
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<BeltGCodeWriter*>(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<double>(inst_shift.x()),
|
||||
unscale<double>(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<double>(), mx = bb.max.cast<double>();
|
||||
Vec3d inst_min(std::numeric_limits<double>::max(),
|
||||
std::numeric_limits<double>::max(),
|
||||
std::numeric_limits<double>::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
|
||||
28
src/libslic3r/BeltGCode.hpp
Normal file
28
src/libslic3r/BeltGCode.hpp
Normal file
@@ -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
|
||||
243
src/libslic3r/BeltGCodeWriter.cpp
Normal file
243
src/libslic3r/BeltGCodeWriter.cpp
Normal file
@@ -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<double>::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
|
||||
50
src/libslic3r/BeltGCodeWriter.hpp
Normal file
50
src/libslic3r/BeltGCodeWriter.hpp
Normal file
@@ -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
|
||||
62
src/libslic3r/BeltSliceStrategy.cpp
Normal file
62
src/libslic3r/BeltSliceStrategy.cpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "BeltSliceStrategy.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
std::unique_ptr<BeltSliceStrategy> BeltSliceStrategy::create(const PrintConfig &config)
|
||||
{
|
||||
if (!config.belt_printer.value)
|
||||
return nullptr;
|
||||
return std::unique_ptr<BeltSliceStrategy>(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<double>::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<double>();
|
||||
min_z = std::min(min_z, pt.z());
|
||||
}
|
||||
}
|
||||
double belt_z_shift_val = (min_z < 0. && min_z != std::numeric_limits<double>::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<double>::max()) ? min_z : 0.;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
45
src/libslic3r/BeltSliceStrategy.hpp
Normal file
45
src/libslic3r/BeltSliceStrategy.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Point.hpp"
|
||||
#include "BeltTransform.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
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<BeltSliceStrategy> 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
|
||||
246
src/libslic3r/BeltTransform.cpp
Normal file
246
src/libslic3r/BeltTransform.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
#include "BeltTransform.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
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<double>(), mx = bb.max.cast<double>();
|
||||
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<typename Config>
|
||||
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<Config, PrintConfig>) {
|
||||
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<ConfigOptionEnum<BeltShearMode>>(key);
|
||||
return opt ? opt->value : BeltShearMode::None;
|
||||
};
|
||||
auto get_scale = [&](const char *key) {
|
||||
auto *opt = config.template option<ConfigOptionEnum<BeltScaleMode>>(key);
|
||||
return opt ? opt->value : BeltScaleMode::None;
|
||||
};
|
||||
auto get_float = [&](const char *key) {
|
||||
auto *opt = config.template option<ConfigOptionFloat>(key);
|
||||
return opt ? opt->value : 45.0;
|
||||
};
|
||||
auto get_axis = [&](const char *key) {
|
||||
auto *opt = config.template option<ConfigOptionEnum<BeltAxis>>(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<double>::max();
|
||||
double max_rz = std::numeric_limits<double>::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
|
||||
146
src/libslic3r/BeltTransform.hpp
Normal file
146
src/libslic3r/BeltTransform.hpp
Normal file
@@ -0,0 +1,146 @@
|
||||
#pragma once
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Point.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Geometry.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
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<ConfigOptionEnum<BeltRemapAxis>>(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
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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<coord_t>::max(), std::numeric_limits<coord_t>::max())),
|
||||
// BBS
|
||||
m_toolchange_count(0),
|
||||
m_nominal_z(0.)
|
||||
m_nominal_z(0.),
|
||||
m_writer(std::make_unique<GCodeWriter>())
|
||||
{}
|
||||
~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<LayerToPrint> collect_layers_to_print(const PrintObject &object);
|
||||
static std::vector<LayerToPrint> collect_layers_to_print(const PrintObject &object, bool skip_empty_first_layer = false);
|
||||
static std::vector<std::pair<coordf_t, std::vector<LayerToPrint>>> 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<GCodeWriter> m_writer;
|
||||
|
||||
struct PlaceholderParserIntegration {
|
||||
void reset();
|
||||
|
||||
@@ -1,40 +1,8 @@
|
||||
#include "BeltBackTransform.hpp"
|
||||
#include "../Geometry.hpp"
|
||||
#include <cmath>
|
||||
#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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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<Extruder> 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);
|
||||
|
||||
@@ -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> gcode;
|
||||
if (m_config.belt_printer.value)
|
||||
gcode = std::make_unique<BeltGCode>();
|
||||
else
|
||||
gcode = std::make_unique<GCode>();
|
||||
//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();
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
#include "GCode/GCodeProcessor.hpp"
|
||||
#include "MultiMaterialSegmentation.hpp"
|
||||
#include "BeltTransform.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
@@ -190,30 +191,7 @@ class ConstSupportLayerPtrsAdaptor : public ConstVectorOfPtrsAdaptor<SupportLaye
|
||||
// When no remap is active, returns the unmodified raw_bounding_box().
|
||||
inline BoundingBoxf3 belt_remapped_bbox(const ModelObject &model_object, const PrintConfig &config)
|
||||
{
|
||||
BoundingBoxf3 bb = model_object.raw_bounding_box();
|
||||
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, no change.
|
||||
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<double>(), mx = bb.max.cast<double>();
|
||||
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.
|
||||
|
||||
@@ -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<double>::max();
|
||||
double max_rz = std::numeric_limits<double>::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<double>::max();
|
||||
double max_rz = std::numeric_limits<double>::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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<VolumeSlices> 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<double>::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<double>();
|
||||
min_z = std::min(min_z, pt.z());
|
||||
}
|
||||
}
|
||||
double belt_z_shift_val = (min_z < 0. && min_z != std::numeric_limits<double>::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<double>::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<double>(phys.x()) : unscale<double>(phys.y());
|
||||
|
||||
124
src/libslic3r/Support/BeltFloorContext.cpp
Normal file
124
src/libslic3r/Support/BeltFloorContext.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include "BeltFloorContext.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
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<double>::infinity();
|
||||
double pos = unscale<double>(m_from_axis == 0 ? pos_slicing.x() : pos_slicing.y());
|
||||
return m_shear_factor * pos + m_floor_offset + m_z_shift;
|
||||
}
|
||||
|
||||
std::vector<Polygons> BeltFloorContext::compute_per_layer_floors(
|
||||
size_t num_layers,
|
||||
const std::function<double(size_t)> &layer_print_z) const
|
||||
{
|
||||
std::vector<Polygons> 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
|
||||
74
src/libslic3r/Support/BeltFloorContext.hpp
Normal file
74
src/libslic3r/Support/BeltFloorContext.hpp
Normal file
@@ -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<Polygons> compute_per_layer_floors(
|
||||
size_t num_layers,
|
||||
const std::function<double(size_t)> &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
|
||||
@@ -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<size_t>(0, support_layers.size()),
|
||||
[&](const tbb::blocked_range<size_t> &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));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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.;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "TreeSupportCommon.hpp"
|
||||
#include "TreeSupport.hpp"
|
||||
#include "TreeSupport3D.hpp"
|
||||
#include "BeltFloorContext.hpp"
|
||||
#include <libnest2d/backends/libslic3r/geometries.hpp>
|
||||
#include <libnest2d/placers/nfpplacer.hpp>
|
||||
|
||||
@@ -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<double>::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<double>(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<double>::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
|
||||
|
||||
@@ -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));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user