Add Kinematics (Jerk & Accel) visualization support (#13169)

* Acceleration preview

Co-Authored-By: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>

* Jerk visualization

* JD

---------

Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
This commit is contained in:
Ian Bassi
2026-04-24 03:20:47 -03:00
committed by GitHub
parent c30276ef24
commit f8b0bcc725
9 changed files with 192 additions and 8 deletions

View File

@@ -5480,6 +5480,37 @@ void GCodeProcessor::process_filament_change(int id)
void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type, bool internal_only)
{
int filament_id = get_filament_id();
const auto normal_mode = PrintEstimatedStatistics::ETimeMode::Normal;
const size_t normal_mode_id = static_cast<size_t>(normal_mode);
const float delta_x = std::abs(m_end_position[X] - m_start_position[X]);
const float delta_y = std::abs(m_end_position[Y] - m_start_position[Y]);
const float delta_z = std::abs(m_end_position[Z] - m_start_position[Z]);
const float delta_e = std::abs(m_end_position[E] - m_start_position[E]);
const bool has_x = delta_x > 0.0f;
const bool has_y = delta_y > 0.0f;
const bool has_z = delta_z > 0.0f;
const bool has_e = delta_e > 0.0f;
const float move_acceleration =
(type == EMoveType::Travel) ? get_travel_acceleration(normal_mode) :
((type == EMoveType::Retract || type == EMoveType::Unretract) ? get_retract_acceleration(normal_mode) :
get_acceleration(normal_mode));
const float junction_deviation = get_option_value(m_time_processor.machine_limits.machine_max_junction_deviation, normal_mode_id);
const bool use_jd_jerk = (m_flavor == gcfMarlinFirmware && junction_deviation > 0.0f);
const auto axis_jerk_for_preview = [this, normal_mode, use_jd_jerk, move_acceleration](Axis axis) {
return use_jd_jerk ? get_axis_max_jerk_with_jd(normal_mode, axis, move_acceleration) : get_axis_max_jerk(normal_mode, axis);
};
const float jerk_x = axis_jerk_for_preview(X);
const float jerk_y = axis_jerk_for_preview(Y);
const float jerk_z = axis_jerk_for_preview(Z);
const float jerk_e = axis_jerk_for_preview(E);
const float move_jerk =
(has_e && !has_x && !has_y && !has_z) ? jerk_e :
(has_z && !has_x && !has_y) ? jerk_z :
(has_x && has_y) ? std::min(jerk_x, jerk_y) :
has_x ? jerk_x :
has_y ? jerk_y :
has_z ? jerk_z :
std::min(jerk_x, jerk_y);
m_last_line_id = (type == EMoveType::Color_change || type == EMoveType::Pause_Print || type == EMoveType::Custom_GCode) ?
m_line_id + 1 :
((type == EMoveType::Seam) ? m_last_line_id : m_line_id);
@@ -5503,6 +5534,10 @@ void GCodeProcessor::store_move_vertex(EMoveType type, EMovePathType path_type,
m_extruder_temps[filament_id],
// ORCA: Add Pressure Advance visualization support
m_pressure_advance,
// ORCA: Add Acceleration visualization support
move_acceleration,
// ORCA: Add Jerk visualization support
move_jerk,
{ 0.0f, 0.0f }, // time
static_cast<float>(m_layer_id), //layer_duration: set later
std::max<unsigned int>(1, m_layer_id) - 1,
@@ -5578,7 +5613,7 @@ float GCodeProcessor::get_axis_max_acceleration(PrintEstimatedStatistics::ETimeM
}
}
float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis, float acceleration) const
{
if (axis != X && axis != Y && axis != Z && axis != E)
return 0.0f;
@@ -5589,12 +5624,22 @@ float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeM
return 0.0f;
const float axis_max_acc = get_axis_max_acceleration(mode, axis);
const float generic_acc = get_acceleration(mode);
const float effective_acc = axis_max_acc > 0.0f ? axis_max_acc : generic_acc;
float effective_acc = acceleration;
if (effective_acc <= 0.0f)
effective_acc = get_acceleration(mode);
if (axis_max_acc > 0.0f)
effective_acc = effective_acc > 0.0f ? std::min(effective_acc, axis_max_acc) : axis_max_acc;
if (effective_acc <= 0.0f)
return 0.0f;
return std::sqrt(jd * effective_acc * 2.5f);
}
float GCodeProcessor::get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
return get_axis_max_jerk_with_jd(mode, axis, get_acceleration(mode));
}
float GCodeProcessor::get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const
{
const size_t id = static_cast<size_t>(mode);

View File

@@ -186,6 +186,10 @@ class Print;
float temperature{ 0.0f }; // Celsius degrees
// ORCA: Add Pressure Advance visualization support
float pressure_advance{ 0.0f };
// ORCA: Add Acceleration visualization support
float acceleration{ 0.0f }; // mm/s^2
// ORCA: Add Jerk visualization support
float jerk{ 0.0f }; // mm/s
std::array<float, static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Count)> time{ 0.0f, 0.0f }; // s
float layer_duration{ 0.0f }; // s
unsigned int layer_id{ 0 };
@@ -1074,6 +1078,7 @@ class Print;
// per-nozzle machine limits (filament_map_2 / get_config_idx_for_filament).
float get_axis_max_feedrate(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_acceleration(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis, float acceleration) const;
float get_axis_max_jerk_with_jd(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
float get_axis_max_jerk(PrintEstimatedStatistics::ETimeMode mode, Axis axis) const;
Vec3f get_xyz_max_jerk(PrintEstimatedStatistics::ETimeMode mode) const;

View File

@@ -91,6 +91,16 @@ struct PathVertex
// Pressure advance value
//
float pressure_advance{ 0.0f };
//
// ORCA: Add Acceleration visualization support
// Acceleration value
//
float acceleration{ 0.0f };
//
// ORCA: Add Jerk visualization support
// Jerk value
//
float jerk{ 0.0f };
//
// Return true if the segment is an extrusion move

View File

@@ -94,6 +94,10 @@ enum class EViewType : uint8_t
Temperature,
// ORCA: Add Pressure Advance visualization support
PressureAdvance,
// ORCA: Add Acceleration visualization support
Acceleration,
// ORCA: Add Jerk visualization support
Jerk,
Tool,
COUNT
};

View File

@@ -171,6 +171,10 @@ public:
// EViewType::Temperature
// ORCA: Add Pressure Advance visualization support
// EViewType::PressureAdvance
// ORCA: Add Acceleration visualization support
// EViewType::Acceleration
// ORCA: Add Jerk visualization support
// EViewType::Jerk
// EViewType::VolumetricFlowRate
// EViewType::ActualVolumetricFlowRate
// EViewType::LayerTimeLinear
@@ -189,6 +193,10 @@ public:
// EViewType::Temperature
// ORCA: Add Pressure Advance visualization support
// EViewType::PressureAdvance
// ORCA: Add Acceleration visualization support
// EViewType::Acceleration
// ORCA: Add Jerk visualization support
// EViewType::Jerk
// EViewType::VolumetricFlowRate
// EViewType::ActualVolumetricFlowRate
// EViewType::LayerTimeLinear

View File

@@ -1469,7 +1469,7 @@ Color ViewerImpl::get_vertex_color(const PathVertex& v) const
if (v.type == EMoveType::Noop)
return DUMMY_COLOR;
if ((v.is_wipe() && (m_settings.view_type != EViewType::Speed && m_settings.view_type != EViewType::ActualSpeed)) || v.is_option())
if ((v.is_wipe() && (m_settings.view_type != EViewType::Speed && m_settings.view_type != EViewType::ActualSpeed && m_settings.view_type != EViewType::Acceleration && m_settings.view_type != EViewType::Jerk)) || v.is_option())
return get_option_color(move_type_to_option(v.type));
switch (m_settings.view_type)
@@ -1507,6 +1507,16 @@ Color ViewerImpl::get_vertex_color(const PathVertex& v) const
{
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_pressure_advance_range.get_color_at(v.pressure_advance);
}
// ORCA: Add Acceleration visualization support
case EViewType::Acceleration:
{
return m_acceleration_range.get_color_at(v.acceleration);
}
// ORCA: Add Jerk visualization support
case EViewType::Jerk:
{
return m_jerk_range.get_color_at(v.jerk);
}
case EViewType::VolumetricFlowRate:
{
return v.is_travel() ? get_option_color(move_type_to_option(v.type)) : m_volumetric_rate_range.get_color_at(v.volumetric_rate());
@@ -1598,6 +1608,10 @@ const ColorRange& ViewerImpl::get_color_range(EViewType type) const
case EViewType::Temperature: { return m_temperature_range; }
// ORCA: Add Pressure Advance visualization support
case EViewType::PressureAdvance: { return m_pressure_advance_range; }
// ORCA: Add Acceleration visualization support
case EViewType::Acceleration: { return m_acceleration_range; }
// ORCA: Add Jerk visualization support
case EViewType::Jerk: { return m_jerk_range; }
case EViewType::VolumetricFlowRate: { return m_volumetric_rate_range; }
case EViewType::ActualVolumetricFlowRate: { return m_actual_volumetric_rate_range; }
case EViewType::LayerTimeLinear: { return m_layer_time_range[0]; }
@@ -1618,6 +1632,10 @@ void ViewerImpl::set_color_range_palette(EViewType type, const Palette& palette)
case EViewType::Temperature: { m_temperature_range.set_palette(palette); break; }
// ORCA: Add Pressure Advance visualization support
case EViewType::PressureAdvance: { m_pressure_advance_range.set_palette(palette); break; }
// ORCA: Add Acceleration visualization support
case EViewType::Acceleration: { m_acceleration_range.set_palette(palette); break; }
// ORCA: Add Jerk visualization support
case EViewType::Jerk: { m_jerk_range.set_palette(palette); break; }
case EViewType::VolumetricFlowRate: { m_volumetric_rate_range.set_palette(palette); break; }
case EViewType::ActualVolumetricFlowRate: { m_actual_volumetric_rate_range.set_palette(palette); break; }
case EViewType::LayerTimeLinear: { m_layer_time_range[0].set_palette(palette); break; }
@@ -1657,6 +1675,10 @@ size_t ViewerImpl::get_used_cpu_memory() const
ret += m_temperature_range.size_in_bytes_cpu();
// ORCA: Add Pressure Advance visualization support
ret += m_pressure_advance_range.size_in_bytes_cpu();
// ORCA: Add Acceleration visualization support
ret += m_acceleration_range.size_in_bytes_cpu();
// ORCA: Add Jerk visualization support
ret += m_jerk_range.size_in_bytes_cpu();
ret += m_volumetric_rate_range.size_in_bytes_cpu();
ret += m_actual_volumetric_rate_range.size_in_bytes_cpu();
for (size_t i = 0; i < COLOR_RANGE_TYPES_COUNT; ++i) {
@@ -1809,6 +1831,10 @@ void ViewerImpl::update_color_ranges()
m_temperature_range.reset();
// ORCA: Add Pressure Advance visualization support
m_pressure_advance_range.reset();
// ORCA: Add Acceleration visualization support
m_acceleration_range.reset();
// ORCA: Add Jerk visualization support
m_jerk_range.reset();
m_volumetric_rate_range.reset();
m_actual_volumetric_rate_range.reset();
m_layer_time_range[0].reset(); // ColorRange::EType::Linear
@@ -1834,6 +1860,10 @@ void ViewerImpl::update_color_ranges()
v.is_extrusion()) {
m_speed_range.update(v.feedrate);
m_actual_speed_range.update(v.actual_feedrate);
// ORCA: Add Acceleration visualization support
m_acceleration_range.update(v.acceleration);
// ORCA: Add Jerk visualization support
m_jerk_range.update(v.jerk);
}
}

View File

@@ -291,6 +291,10 @@ private:
ColorRange m_temperature_range;
// ORCA: Add Pressure Advance visualization support
ColorRange m_pressure_advance_range;
// ORCA: Add Acceleration visualization support
ColorRange m_acceleration_range;
// ORCA: Add Jerk visualization support
ColorRange m_jerk_range;
ColorRange m_volumetric_rate_range;
ColorRange m_actual_volumetric_rate_range;
std::array<ColorRange, COLOR_RANGE_TYPES_COUNT> m_layer_time_range{

View File

@@ -77,6 +77,10 @@ static std::string get_view_type_string(libvgcode::EViewType view_type)
return _u8L("Speed");
else if (view_type == libvgcode::EViewType::ActualSpeed)
return _u8L("Actual Speed");
else if (view_type == libvgcode::EViewType::Acceleration)
return _u8L("Acceleration");
else if (view_type == libvgcode::EViewType::Jerk)
return _u8L("Jerk");
else if (view_type == libvgcode::EViewType::FanSpeed)
return _u8L("Fan Speed");
else if (view_type == libvgcode::EViewType::Temperature)
@@ -382,6 +386,16 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
sprintf(buff, ("%.1f " + _u8L("mm/s")).c_str(), vertex.feedrate);
const std::string text = std::string(buff);
ImGuiWrapper::text(text);
});
append_table_row(_u8L("Acceleration"), [&vertex, &buff]() {
sprintf(buff, ("%.0f " + _u8L("mm/s²")).c_str(), vertex.acceleration);
const std::string text = std::string(buff);
ImGuiWrapper::text(text);
});
append_table_row(_u8L("Jerk"), [&vertex, &buff]() {
sprintf(buff, ("%.1f " + _u8L("mm/s")).c_str(), vertex.jerk);
const std::string text = std::string(buff);
ImGuiWrapper::text(text);
});
append_table_row(_u8L("Flow rate"), [&vertex, &buff, NA_TXT]() { // ORCA use "Flow rate" instead "Volumetric flow Rate" to make window more compact
std::string text;
@@ -617,6 +631,14 @@ void GCodeViewer::SequentialView::Marker::render_position_window(const libvgcode
sprintf(buf, "%s %s%.1f", buf, _u8L("Actual Speed: ").c_str(), vertex.actual_feedrate);
break;
}
case libvgcode::EViewType::Acceleration: {
sprintf(buf, "%s %s%.0f", buf, _u8L("Acceleration: ").c_str(), vertex.acceleration);
break;
}
case libvgcode::EViewType::Jerk: {
sprintf(buf, "%s %s%.1f", buf, _u8L("Jerk: ").c_str(), vertex.jerk);
break;
}
// ORCA: Add Pressure Advance visualization support
case libvgcode::EViewType::PressureAdvance: {
sprintf(buf, "%s %s%.4f", buf, _u8L("PA: ").c_str(), vertex.pressure_advance);
@@ -1031,6 +1053,8 @@ void GCodeViewer::update_by_mode(ConfigOptionMode mode)
view_type_items.push_back(libvgcode::EViewType::ColorPrint);
view_type_items.push_back(libvgcode::EViewType::Speed);
view_type_items.push_back(libvgcode::EViewType::ActualSpeed);
view_type_items.push_back(libvgcode::EViewType::Acceleration);
view_type_items.push_back(libvgcode::EViewType::Jerk);
view_type_items.push_back(libvgcode::EViewType::Height);
view_type_items.push_back(libvgcode::EViewType::Width);
view_type_items.push_back(libvgcode::EViewType::VolumetricFlowRate);
@@ -2287,6 +2311,8 @@ void GCodeViewer::render_toolpaths()
add_range_property_row("height range", m_viewer.get_color_range(libvgcode::EViewType::Height).get_range());
add_range_property_row("width range", m_viewer.get_color_range(libvgcode::EViewType::Width).get_range());
add_range_property_row("speed range", m_viewer.get_color_range(libvgcode::EViewType::Speed).get_range());
add_range_property_row("acceleration range", m_viewer.get_color_range(libvgcode::EViewType::Acceleration).get_range());
add_range_property_row("jerk range", m_viewer.get_color_range(libvgcode::EViewType::Jerk).get_range());
add_range_property_row("fan speed range", m_viewer.get_color_range(libvgcode::EViewType::FanSpeed).get_range());
add_range_property_row("temperature range", m_viewer.get_color_range(libvgcode::EViewType::Temperature).get_range());
// ORCA: Add Pressure Advance visualization support
@@ -3536,6 +3562,16 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
imgui.title(_u8L("Actual Speed (mm/s)"));
break;
}
case libvgcode::EViewType::Acceleration:
{
imgui.title(_u8L("Acceleration (mm/s²)"));
break;
}
case libvgcode::EViewType::Jerk:
{
imgui.title(_u8L("Jerk (mm/s)"));
break;
}
case libvgcode::EViewType::FanSpeed: { imgui.title(_u8L("Fan Speed (%)")); break; }
case libvgcode::EViewType::Temperature: { imgui.title(_u8L("Temperature (°C)")); break; }
// ORCA: Add Pressure Advance visualization support
@@ -3714,6 +3750,38 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
ImGui::PopStyleVar(1);
break;
}
case libvgcode::EViewType::Acceleration: {
append_range(m_viewer.get_color_range(libvgcode::EViewType::Acceleration), 0);
ImGui::Spacing();
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
offsets = calculate_offsets({ { _u8L("Options"), { _u8L("Travel")}}, { _u8L("Display"), {""}} }, icon_size);
append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} });
const bool travel_visible = m_viewer.is_option_visible(libvgcode::EOptionType::Travels);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 3.0f));
append_item(EItemType::None, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), { {_u8L("Travel"), offsets[0] }}, true, predictable_icon_pos/*ORCA checkbox_pos*/, travel_visible, [this, travel_visible]() {
m_viewer.toggle_option_visibility(libvgcode::EOptionType::Travels);
update_moves_slider();
});
ImGui::PopStyleVar(1);
break;
}
case libvgcode::EViewType::Jerk: {
append_range(m_viewer.get_color_range(libvgcode::EViewType::Jerk), 1);
ImGui::Spacing();
ImGui::Dummy({ window_padding, window_padding });
ImGui::SameLine();
offsets = calculate_offsets({ { _u8L("Options"), { _u8L("Travel")}}, { _u8L("Display"), {""}} }, icon_size);
append_headers({ {_u8L("Options"), offsets[0] }, { _u8L("Display"), offsets[1]} });
const bool travel_visible = m_viewer.is_option_visible(libvgcode::EOptionType::Travels);
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 3.0f));
append_item(EItemType::None, libvgcode::convert(m_viewer.get_option_color(libvgcode::EOptionType::Travels)), { {_u8L("Travel"), offsets[0] }}, true, predictable_icon_pos/*ORCA checkbox_pos*/, travel_visible, [this, travel_visible]() {
m_viewer.toggle_option_visibility(libvgcode::EOptionType::Travels);
update_moves_slider();
});
ImGui::PopStyleVar(1);
break;
}
case libvgcode::EViewType::FanSpeed: { append_range(m_viewer.get_color_range(libvgcode::EViewType::FanSpeed), 0); break; }
case libvgcode::EViewType::Temperature: { append_range(m_viewer.get_color_range(libvgcode::EViewType::Temperature), 0); break; }
// ORCA: Add Pressure Advance visualization support
@@ -4078,6 +4146,8 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv
{
case libvgcode::EViewType::Speed:
case libvgcode::EViewType::ActualSpeed:
case libvgcode::EViewType::Acceleration:
case libvgcode::EViewType::Jerk:
case libvgcode::EViewType::Tool:
case libvgcode::EViewType::ColorPrint: {
break;

View File

@@ -225,13 +225,17 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve
curr.mm3_per_mm, curr.fan_speed, curr.temperature, 0.0f, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f },
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance };
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance,
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
/* ORCA: Add Jerk visualization support */ curr.jerk };
#else
const libvgcode::PathVertex vertex = { convert(prev.position), curr.height, curr.width, curr.feedrate, prev.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), { 0.0f, 0.0f },
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance };
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance,
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
/* ORCA: Add Jerk visualization support */ curr.jerk };
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
ret.vertices.emplace_back(vertex);
}
@@ -243,13 +247,17 @@ GCodeInputData convert(const Slic3r::GCodeProcessorResult& result, const std::ve
result.filament_densities[curr.extruder_id] * curr.mm3_per_mm * (curr.position - prev.position).norm(),
convert(curr.extrusion_role), curr_type, static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time,
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance };
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance,
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
/* ORCA: Add Jerk visualization support */ curr.jerk };
#else
const libvgcode::PathVertex vertex = { convert(curr.position), curr.height, curr.width, curr.feedrate, curr.actual_feedrate,
curr.mm3_per_mm, curr.fan_speed, curr.temperature, convert(curr.extrusion_role), curr_type,
static_cast<uint32_t>(curr.gcode_id), static_cast<uint32_t>(curr.layer_id),
static_cast<uint8_t>(curr.extruder_id), static_cast<uint8_t>(curr.cp_color_id), curr.time,
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance };
/* ORCA: Add Pressure Advance visualization support */ 0.0f, curr.pressure_advance,
/* ORCA: Add Acceleration visualization support */ curr.acceleration,
/* ORCA: Add Jerk visualization support */ curr.jerk };
#endif // VGCODE_ENABLE_COG_AND_TOOL_MARKERS
ret.vertices.emplace_back(vertex);
}