mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-09 16:51:12 +00:00
Guard the layer count and the per-object layer collection against an object that is left without a layer to print on a belt (the counting loop stepped before begin() and front() was taken of an empty vector). Check the belt temperature tower's embossed model before the current project is replaced, not after. Only invalidate the support step of objects that own a belt brim when an object is added or removed. The empty-layers test now counts an extrusion only where material is laid down along a move. The BeltBrim.cpp SEQUENCING note says exactly which layers are read, and the machine-frame scale is 1/|sin|. Remove more code that nothing calls: the kinematics inverse (to_logical, apply_axis_remap_inverse, to_build_volume and the state kept for them), the world_coordinates(), is_active() and belt_brim_areas_by_layer() accessors, the PrintConfig overload of physical_tilt() and the DynamicPrintConfig overload of compute_belt_height_and_floor(). Comments in GCode.hpp, BeltSliceStrategy.hpp/.cpp and PrintObjectSlice.cpp that described the retired pre-slice remap and plane-evaluator still did; the purge-tower width tooltip named the wrong switch. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
154 lines
6.0 KiB
C++
154 lines
6.0 KiB
C++
#include "BeltTransform.hpp"
|
||
#include "Model.hpp"
|
||
#include "BoundingBox.hpp"
|
||
#include "Config.hpp"
|
||
#include "Geometry.hpp"
|
||
#include "Point.hpp"
|
||
#include "PrintConfig.hpp"
|
||
#include "libslic3r.h"
|
||
|
||
#include <limits>
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
#include <cstdlib>
|
||
|
||
namespace Slic3r {
|
||
|
||
// ---- Matrix builders ------------------------------------------------------
|
||
|
||
Matrix3d BeltTransformPipeline::build_rotation_matrix(const PrintConfig &config, bool *has_rot_out)
|
||
{
|
||
BeltRotationAxis axis = config.belt_slice_rotation.value;
|
||
double angle_deg = config.belt_slice_rotation_angle.value;
|
||
bool active = axis != BeltRotationAxis::None && std::abs(angle_deg) > EPSILON;
|
||
if (has_rot_out) *has_rot_out = active;
|
||
if (!active)
|
||
return Matrix3d::Identity();
|
||
double angle_rad = Geometry::deg2rad(angle_deg);
|
||
Vec3d unit_axis;
|
||
switch (axis) {
|
||
case BeltRotationAxis::X: unit_axis = Vec3d::UnitX(); break;
|
||
case BeltRotationAxis::Y: unit_axis = Vec3d::UnitY(); break;
|
||
case BeltRotationAxis::Z: unit_axis = Vec3d::UnitZ(); break;
|
||
default: return Matrix3d::Identity();
|
||
}
|
||
return Eigen::AngleAxisd(angle_rad, unit_axis).toRotationMatrix();
|
||
}
|
||
|
||
Transform3d BeltTransformPipeline::build_forward_transform(const PrintConfig &config)
|
||
{
|
||
// Mesh-side belt transform: the rotation. (Shear & scale are a g-code-side
|
||
// stage, not part of the mesh transform.)
|
||
Transform3d combined = Transform3d::Identity();
|
||
combined.linear() = build_rotation_matrix(config);
|
||
return combined;
|
||
}
|
||
|
||
// ---- Belt floor parameters ------------------------------------------------
|
||
|
||
namespace {
|
||
|
||
// Belt floor in the rotated slicer frame: the image of z_machine = 0 under R.
|
||
// R(+α, X): point (·, y, 0) → (·, cos α · y, sin α · y) ⇒ z = tan(α) · y_s
|
||
// R(+α, Y): point (x, ·, 0) → (cos α · x, ·, -sin α · x) ⇒ z = -tan(α) · x_s
|
||
// R(+α, Z): point (·, ·, 0) → (·, ·, 0); no tilt → no floor
|
||
void belt_floor_shear(BeltRotationAxis rot_axis, double angle_rad, BeltTransformPipeline::BeltFloorParams &out)
|
||
{
|
||
double sin_a = std::sin(angle_rad), cos_a = std::cos(angle_rad);
|
||
switch (rot_axis) {
|
||
case BeltRotationAxis::X:
|
||
out.shear_factor = (std::abs(cos_a) > EPSILON) ? sin_a / cos_a : 0.;
|
||
out.from_axis = 1; // Y
|
||
break;
|
||
case BeltRotationAxis::Y:
|
||
out.shear_factor = (std::abs(cos_a) > EPSILON) ? -sin_a / cos_a : 0.;
|
||
out.from_axis = 0; // X
|
||
break;
|
||
case BeltRotationAxis::Z:
|
||
default:
|
||
out.shear_factor = 0.0;
|
||
out.from_axis = 1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Z of the belt floor directly under a point of the rotated (unshifted) frame.
|
||
inline double belt_floor_z(const BeltTransformPipeline::BeltFloorParams &fp, const Vec3d &pt)
|
||
{
|
||
return fp.shear_factor * (fp.from_axis == 0 ? pt.x() : pt.y());
|
||
}
|
||
|
||
|
||
BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl(
|
||
const PrintConfig &config, const BoundingBoxf3 &bb, double original_height)
|
||
{
|
||
BeltTransformPipeline::BeltHeightResult result;
|
||
result.object_height = original_height;
|
||
|
||
// The mesh rotation (the sole mesh-side belt transform).
|
||
const BeltRotationAxis rot_axis = config.belt_slice_rotation.value;
|
||
const double rot_angle = config.belt_slice_rotation_angle.value;
|
||
|
||
bool has_rotation = rot_axis != BeltRotationAxis::None && std::abs(rot_angle) > EPSILON;
|
||
if (!has_rotation)
|
||
return result;
|
||
|
||
// Rotation path: sweep the 8 bbox corners through R to get the rotated height,
|
||
// then derive the belt floor (the image of machine-Z = 0 under R).
|
||
double angle_rad = Geometry::deg2rad(rot_angle);
|
||
Vec3d unit_axis;
|
||
switch (rot_axis) {
|
||
case BeltRotationAxis::X: unit_axis = Vec3d::UnitX(); break;
|
||
case BeltRotationAxis::Y: unit_axis = Vec3d::UnitY(); break;
|
||
case BeltRotationAxis::Z: unit_axis = Vec3d::UnitZ(); break;
|
||
default: unit_axis = Vec3d::UnitX(); break;
|
||
}
|
||
Matrix3d R = Eigen::AngleAxisd(angle_rad, unit_axis).toRotationMatrix();
|
||
belt_floor_shear(rot_axis, angle_rad, result.floor_params);
|
||
// The slicing frame starts at the lowest point of the support region: the
|
||
// lowest belt-floor point under the footprint, not the lowest vertex. The
|
||
// belt under the leading end of an overhang lies below every vertex of the
|
||
// part, and supports have to be able to reach it (see
|
||
// BeltSliceStrategy::apply_preslice_transforms for the exact vertex-scan
|
||
// counterpart of this bbox estimate).
|
||
double min_rz = std::numeric_limits<double>::max();
|
||
double max_rz = std::numeric_limits<double>::lowest();
|
||
for (int i = 0; i < 8; ++i) {
|
||
Vec3d c((i & 1) ? bb.max.x() : bb.min.x(),
|
||
(i & 2) ? bb.max.y() : bb.min.y(),
|
||
(i & 4) ? bb.max.z() : bb.min.z());
|
||
Vec3d rc = R * c;
|
||
double z = rc.z();
|
||
min_rz = std::min(min_rz, z);
|
||
max_rz = std::max(max_rz, z);
|
||
min_rz = std::min(min_rz, belt_floor_z(result.floor_params, rc));
|
||
}
|
||
min_rz -= BeltTransformPipeline::frame_margin(result.floor_params);
|
||
result.object_height = max_rz - min_rz;
|
||
|
||
result.floor_params.z_shift = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.);
|
||
|
||
return result;
|
||
}
|
||
|
||
} // anonymous namespace
|
||
|
||
BeltTransformPipeline::BeltHeightResult BeltTransformPipeline::compute_belt_height_and_floor(
|
||
const PrintConfig &config, const BoundingBoxf3 &bbox, double original_height)
|
||
{
|
||
return compute_belt_height_and_floor_impl(config, bbox, original_height);
|
||
}
|
||
|
||
bool BeltTransformPipeline::floor_shear(const PrintConfig &config, BeltFloorParams &out)
|
||
{
|
||
out = BeltFloorParams{};
|
||
const BeltRotationAxis rot_axis = config.belt_slice_rotation.value;
|
||
const double rot_angle = config.belt_slice_rotation_angle.value;
|
||
if (rot_axis == BeltRotationAxis::None || std::abs(rot_angle) <= EPSILON)
|
||
return false;
|
||
belt_floor_shear(rot_axis, Geometry::deg2rad(rot_angle), out);
|
||
return std::abs(out.shear_factor) > EPSILON;
|
||
}
|
||
|
||
} // namespace Slic3r
|