mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 21:42:43 +00:00
* minor logic swap * first attempt, has a race condition * fixed the offset issue * found a solution, I think things work now (at least once I quash this race condition) * still chasing down race conditions * add manual shear / scale order strategy swap * tweak manual shear, fix ui uninitialization crash * fix z height / g-code desync issue * fix shear then scale cutoff planes * getting closer * fix support termination planes * fix incorrect offsets in shear-then-scale mode * test - fix overextrusion due to model/layer scale
292 lines
12 KiB
C++
292 lines
12 KiB
C++
#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.preslice_remap_x.value);
|
||
int pre_ry = int(config.preslice_remap_y.value);
|
||
int pre_rz = int(config.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);
|
||
|
||
// Match the mesh-side ordering selected by belt_mesh_transform_order so
|
||
// BeltBackTransform inverts the same composition that BeltSliceStrategy
|
||
// applied to the mesh.
|
||
// ScaleThenShear: applied to p, scale runs first then shear (shear * scale).
|
||
// ShearThenScale: applied to p, shear runs first then scale (scale * shear).
|
||
Transform3d combined = Transform3d::Identity();
|
||
combined.linear() = (config.belt_mesh_transform_order.value == BeltTransformOrder::ScaleThenShear)
|
||
? Matrix3d(shear * scale)
|
||
: Matrix3d(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.preslice_remap_x.value);
|
||
int pre_ry = int(config.preslice_remap_y.value);
|
||
int pre_rz = int(config.preslice_remap_z.value);
|
||
|
||
if (pre_rx == int(RemapAxis::PosX) &&
|
||
pre_ry == int(RemapAxis::PosY) &&
|
||
pre_rz == int(RemapAxis::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 + per-axis scale + transform order from config.
|
||
BeltShearMode z_shear_mode;
|
||
double z_shear_angle;
|
||
BeltScaleMode z_scale_mode;
|
||
double z_scale_angle;
|
||
int z_shear_from;
|
||
BeltScaleMode from_scale_mode; // scale on the shear's source axis
|
||
double from_scale_angle;
|
||
BeltTransformOrder order;
|
||
|
||
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);
|
||
order = config.belt_mesh_transform_order.value;
|
||
if (z_shear_from == 0) {
|
||
from_scale_mode = config.belt_scale_x.value;
|
||
from_scale_angle = config.belt_scale_x_angle.value;
|
||
} else {
|
||
from_scale_mode = config.belt_scale_y.value;
|
||
from_scale_angle = config.belt_scale_y_angle.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;
|
||
};
|
||
auto get_order = [&](const char *key) {
|
||
auto *opt = config.template option<ConfigOptionEnum<BeltTransformOrder>>(key);
|
||
return opt ? opt->value : BeltTransformOrder::ScaleThenShear;
|
||
};
|
||
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");
|
||
order = get_order("belt_mesh_transform_order");
|
||
if (z_shear_from == 0) {
|
||
from_scale_mode = get_scale("belt_scale_x");
|
||
from_scale_angle = get_float("belt_scale_x_angle");
|
||
} else {
|
||
from_scale_mode = get_scale("belt_scale_y");
|
||
from_scale_angle = get_float("belt_scale_y_angle");
|
||
}
|
||
}
|
||
|
||
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);
|
||
double scale_from = BeltTransformPipeline::compute_scale_factor(from_scale_mode, from_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)}) {
|
||
// Mesh-frame new_z computed per ordering.
|
||
// scale-then-shear: Z_s = sz*Z_m + s_from*tan(α)*from_m
|
||
// shear-then-scale: Z_s = sz*(Z_m + tan(α)*from_m)
|
||
double new_z = (order == BeltTransformOrder::ScaleThenShear)
|
||
? scale_z * vz + scale_from * shear_factor * vs
|
||
: 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;
|
||
// Effective slicer-frame slope of the belt surface (Z_m=0 line):
|
||
// scale-then-shear: Z_s = tan(α) * Y_s → slope = tan(α)
|
||
// shear-then-scale: Z_s = sz/s_from * tan(α) * Y_s → slope = sz*tan(α)/s_from
|
||
// The downstream cutoff formula `Y_s = (print_z - z_shift) / slope`
|
||
// and floor_print_z(Y_s) = slope * Y_s + z_shift use this slope.
|
||
double effective_shear = (order == BeltTransformOrder::ScaleThenShear)
|
||
? shear_factor
|
||
: (std::abs(scale_from) > EPSILON
|
||
? scale_z * shear_factor / scale_from
|
||
: shear_factor);
|
||
result.floor_params.shear_factor = effective_shear;
|
||
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
|