mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-07 15:51:08 +00:00
The slicing frame of a belt object started at its lowest vertex, but the belt under the leading end of an overhang lies below that, by the overhang's length times the tilt's shear. Every support generator works in layers at z >= 0, so none of them could reach it: normal supports stopped at the object's own lowest layer, and the two tree generators each carried a stack of hacks to extend themselves below it (a post-hoc copy of the lowest base area in TreeSupport, "virtual belt raft layers" in TreeSupport3D/TreeModelVolumes), sized from the pre-rotation bbox and capped at global z = 0, which is only right for the trailing half of the belt. Start the frame at the lowest belt-floor point under the footprint instead, less a 10 mm margin along the belt for the base of a support column (BeltSliceStrategy::apply_preslice_transforms and BeltTransformPipeline::compute_belt_height_and_floor agree on it). The layers between it and the first vertex come out empty, which belt slicing already tolerates, and the generators need no extension at all: - normal supports: the generator anchors its layer grid at the frame origin, so run it in the object frame and shift the global belt Z offset onto the result afterwards, as organic supports already did. With the offset on the object layers a top contact at negative z turned the intermediate-layer count negative and the generator allocated layers until the kernel killed it (any overhang in the leading half of the belt). Drop the first-layer flange expansion on a belt: the first support layer is the leading tip of the support, not a flange, and inflating it put lines in the air ahead of the belt. - classic tree: a node now keeps dropping until its whole circle is in the belt, so the branch tapers to a tip on the belt instead of stopping, a radius above it, when its centre crosses. - organic: the belt is no longer a support blocker. A blocker is a collision, and a branch descending onto one slides off it, down the tilted belt and ahead of the part; the belt is where branches end, which the per-layer m_belt_floor clipping already does. The belt brim is generated after the parallel support step instead of inside it: belt_brim_obstacles() reads every object's layers and support layers, which another object's support step rebuilds (and, now, shifts) at the same time. This is the race behind the Windows arm64 segfault in "Belt brim of each object precedes its perimeters on its own filament". Also: the belt tilt axis moves to Developer mode as its own row (a shared line is shown by its first option's mode), first_layer_plane band thickness, belt_support_floor_mode, belt_preslice_global and gcode_back_transform are retired and presumed on, the gravity arrow is a plain line along the up direction, and the "Show raw G-code (belt only)" preview toggle is gone. Regression test: "Belt supports reach the belt under a leading overhang" slices a cube with a fin whose underside is parallel to the layers, 20 mm ahead of the cube and up to 41 mm of slicing Z above the belt, for normal, organic and classic tree supports, and checks that the lowest support layer sits on the belt beneath its own lines. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
144 lines
6.3 KiB
C++
144 lines
6.3 KiB
C++
#pragma once
|
|
|
|
#include "libslic3r.h"
|
|
#include "Point.hpp"
|
|
#include "BoundingBox.hpp"
|
|
#include "PrintConfig.hpp"
|
|
#include "Geometry.hpp"
|
|
#include "Config.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 * rotation * trafo_in
|
|
//
|
|
// Rotation is the sole mesh-side belt transform; shear & scale are applied
|
|
// to the g-code instead (see MachineFrameTransform). 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(). The machine-frame shear/scale is derived directly
|
|
// from the tilt angle in MachineFrameTransform and no longer lives here.
|
|
//
|
|
// Design note: this mesh-rotation approach replaced an earlier pre-shear
|
|
// method (now removed). While that initial pre-shear method was instrumental
|
|
// in getting belt printer slicing off the ground in the first place, its place is
|
|
// in the past. A big thank you goes to the Unlayered3D team, who recommended
|
|
// switching to a pre-slice rotation stage instead. Doing so keeps the slicing
|
|
// operation isometric — no distortion of the sliced geometry — while the
|
|
// non-orthogonal machine-axis compensation is confined to a g-code-side shear/scale
|
|
// derived from the same tilt angle.
|
|
//
|
|
// This fixed a number of issues, including several issues noticed by hotcubcar
|
|
// regarding adaptive infills not working, gyroid becoming anisotropic, and more
|
|
// that were all mostly resolved as a result of the switch.
|
|
//
|
|
// This also means that the pre-slice rotation transform methodology can be used
|
|
// more cleanly on non-belt printers.
|
|
// - HarrierPigeon (Joseph Robertson)
|
|
|
|
class BeltTransformPipeline
|
|
{
|
|
public:
|
|
// ---- Identity checks --------------------------------------------------
|
|
|
|
// Whether the G-code axis remap applies at all. The remap fields are only
|
|
// offered in the belt printer group, so a value left in a profile must not
|
|
// change a non-belt print: with belt mode off every belt-only key is a no-op.
|
|
// This is the one place to widen if a non-belt use ever needs them.
|
|
static bool axis_remap_enabled(const PrintConfig &config) { return config.belt_printer.value; }
|
|
|
|
static bool has_rotation(const PrintConfig &config)
|
|
{
|
|
return config.belt_slice_rotation.value != BeltRotationAxis::None &&
|
|
std::abs(config.belt_slice_rotation_angle.value) > EPSILON;
|
|
}
|
|
|
|
// Physical belt tilt derived from the slicing rotation — the single source of
|
|
// truth for bed rendering, support gravity tilt and the bed-exclusion
|
|
// projection. Returns the tilt magnitude in degrees split onto the X and Y
|
|
// build-plate tilt axes according to the rotation axis:
|
|
// rotation about X → tilt_x = angle (gantry tilts in the YZ plane)
|
|
// rotation about Y → tilt_y = angle (gantry tilts in the XZ plane)
|
|
// rotation about Z / None → no tilt (in-plane spin doesn't tilt the belt)
|
|
// The magnitude uses abs(angle) so a negative rotation still reports a positive
|
|
// physical tilt.
|
|
struct PhysicalTilt { double tilt_x_deg = 0.; double tilt_y_deg = 0.; };
|
|
|
|
static PhysicalTilt physical_tilt(BeltRotationAxis axis, double angle_deg)
|
|
{
|
|
PhysicalTilt t;
|
|
double mag = std::abs(angle_deg);
|
|
switch (axis) {
|
|
case BeltRotationAxis::X: t.tilt_x_deg = mag; break;
|
|
case BeltRotationAxis::Y: t.tilt_y_deg = mag; break;
|
|
default: break; // Z / None: no physical tilt
|
|
}
|
|
return t;
|
|
}
|
|
|
|
static PhysicalTilt physical_tilt(const PrintConfig &config)
|
|
{
|
|
return physical_tilt(config.belt_slice_rotation.value,
|
|
config.belt_slice_rotation_angle.value);
|
|
}
|
|
|
|
// ---- Matrix builders --------------------------------------------------
|
|
|
|
// Build the 3x3 rotation matrix from belt_slice_rotation* config.
|
|
// Returns Identity if rotation axis is None or angle is ~0.
|
|
// Also sets has_rot_out if non-null.
|
|
static Matrix3d build_rotation_matrix(const PrintConfig &config, bool *has_rot_out = nullptr);
|
|
|
|
// Forward transform (the rotation) — the mesh-side belt transform that
|
|
// BeltSliceStrategy applies and BeltBackTransform inverts.
|
|
// Does NOT include the per-object Z-shift.
|
|
static Transform3d build_forward_transform(const PrintConfig &config);
|
|
|
|
// ---- Belt floor parameters --------------------------------------------
|
|
|
|
struct BeltFloorParams {
|
|
double shear_factor = 0.0;
|
|
int from_axis = 1;
|
|
double z_shift = 0.0;
|
|
};
|
|
|
|
// Shear factor and from-axis of the belt floor in the rotated slicer frame
|
|
// (z_floor = shear_factor * u, u = the from-axis coordinate), for the
|
|
// rotation the config selects. z_shift is left at 0. Returns false (and
|
|
// zero shear) when the config has no tilt.
|
|
static bool floor_shear(const PrintConfig &config, BeltFloorParams &out);
|
|
|
|
// How far below the lowest belt-floor point under the footprint the slicing
|
|
// frame starts, in slicing Z. A support column meeting the belt is wider at
|
|
// its base than at its tip, so under a leading overhang the base reaches ahead
|
|
// of the part along the belt, and the layers that trim it to the belt plane
|
|
// lie below that lowest point: 10 mm along the belt.
|
|
static double frame_margin(const BeltFloorParams &fp) { return 10. * std::abs(fp.shear_factor); }
|
|
|
|
// 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 the object's 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 &bbox,
|
|
double original_height);
|
|
|
|
// Overload for DynamicPrintConfig (used by static slicing_parameters).
|
|
static BeltHeightResult compute_belt_height_and_floor(
|
|
const DynamicPrintConfig &config, const BoundingBoxf3 &bbox,
|
|
double original_height);
|
|
};
|
|
|
|
} // namespace Slic3r
|