mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-01 14:27:00 +00:00
Normalize the junction direction vector over XYZE (#15308)
* Normalize the junction direction vector over XYZE calc_vmax_junction_deviation() treats the dot product of two jd_unit_vec as a cosine, but the vectors were scaled by 1 / block.distance, which is the XYZ length. On an extruding move the E component then pushes the 4D norm above 1 and the dot product below -1, so the corner reads as straighter than it is and is planned too fast -- the more so the higher the flow. Measured on a 6 degree corner at scv 5: 86.9mm/s with no extrusion, 94.4mm/s at 0.029mm/mm, 150.0mm/s at 0.1mm/mm. Neither firmware does that. Marlin normalizes over XYZE for any extruding move (planner.cpp: `if (... || esteps > 0) normalize_junction_vector(unit_vec)`) and Klipper leaves E out of the cosine entirely, dotting only axes_r[0..2] (toolhead.py::Move.calc_junction). Normalizing satisfies both: with E normalized in, the cosine differs from the XYZ-only one by ~1e-5 at printing flow rates. This is a deliberate divergence from PrusaSlicer, which still scales by 1 / distance -- it carries an older Marlin's behaviour. Travel moves are unaffected, their vector was already unit length. Reported by Copilot in review of #15304. * Test that extrusion rate does not change corner planning The junction deviation tests were all travel-only, which is exactly why the E component of the junction vector went unchecked. Cover it: the same corner has to be planned the same whether nothing, an ordinary 0.42 x 0.2 line, or a fat large-nozzle line is extruded through it, on both Klipper and Marlin 2. Reported by Copilot in review of #15304.
This commit is contained in:
@@ -5037,10 +5037,10 @@ void GCodeProcessor::process_G1(const std::array<std::optional<double>, 4>& axes
|
||||
if (!is_extrusion_only_move(delta_pos))
|
||||
curr.enter_direction = curr.enter_direction / norm;
|
||||
curr.exit_direction = curr.enter_direction;
|
||||
curr.jd_unit_vec = Vec4f(static_cast<float>(delta_pos[X]) * inv_distance,
|
||||
static_cast<float>(delta_pos[Y]) * inv_distance,
|
||||
static_cast<float>(delta_pos[Z]) * inv_distance,
|
||||
static_cast<float>(delta_pos[E]) * inv_distance);
|
||||
curr.jd_unit_vec = Vec4f(static_cast<float>(delta_pos[X]),
|
||||
static_cast<float>(delta_pos[Y]),
|
||||
static_cast<float>(delta_pos[Z]),
|
||||
static_cast<float>(delta_pos[E])).normalized();
|
||||
|
||||
TimeBlock block;
|
||||
block.move_type = type;
|
||||
@@ -5415,10 +5415,10 @@ void GCodeProcessor::process_VG1(const GCodeReader::GCodeLine& line)
|
||||
if (!is_extrusion_only_move(delta_pos))
|
||||
curr.enter_direction = curr.enter_direction / norm;
|
||||
curr.exit_direction = curr.enter_direction;
|
||||
curr.jd_unit_vec = Vec4f(static_cast<float>(delta_pos[X]) * inv_distance,
|
||||
static_cast<float>(delta_pos[Y]) * inv_distance,
|
||||
static_cast<float>(delta_pos[Z]) * inv_distance,
|
||||
static_cast<float>(delta_pos[E]) * inv_distance);
|
||||
curr.jd_unit_vec = Vec4f(static_cast<float>(delta_pos[X]),
|
||||
static_cast<float>(delta_pos[Y]),
|
||||
static_cast<float>(delta_pos[Z]),
|
||||
static_cast<float>(delta_pos[E])).normalized();
|
||||
|
||||
TimeBlock block;
|
||||
block.move_type = type;
|
||||
@@ -7245,6 +7245,11 @@ float GCodeProcessor::calc_vmax_junction_deviation(const TimeBlock& block, const
|
||||
return 0.0f; // starts from rest, the planner raises this on the reverse pass
|
||||
|
||||
// -1 for a straight continuation, +1 for a full reversal. Half angle identity, no acos()/sin().
|
||||
// Both vectors are unit length over XYZE, so this really is a cosine: scaling by 1 / distance
|
||||
// instead, as PrusaSlicer does, leaves an E term that makes extruding corners look straighter
|
||||
// than they are. Marlin normalizes over XYZE for any extruding move (planner.cpp, esteps > 0)
|
||||
// and Klipper keeps E out of the cosine entirely (toolhead.py::Move.calc_junction); both agree
|
||||
// that the corner is planned by its geometry, and normalizing matches them to within 1e-5.
|
||||
float junction_cos_theta = (-prev.jd_unit_vec).dot(curr.jd_unit_vec);
|
||||
if (junction_cos_theta > 0.999999f)
|
||||
return 0.0f; // the path doubles back, the machine has to stop
|
||||
|
||||
@@ -637,9 +637,8 @@ class Print;
|
||||
//For line move, there are same. For arc move, there are different.
|
||||
Vec3f enter_direction;
|
||||
Vec3f exit_direction;
|
||||
// Orca: move direction over all four axes, scaled by 1 / block.distance. Used by
|
||||
// calc_vmax_junction_deviation(), which needs E to see extrusion-rate changes
|
||||
// between collinear moves the way Marlin and Klipper do.
|
||||
// Orca: move direction over all four axes, unit length. Used by
|
||||
// calc_vmax_junction_deviation(); see there for why E is normalized in.
|
||||
Vec4f jd_unit_vec;
|
||||
|
||||
void reset();
|
||||
|
||||
Reference in New Issue
Block a user