GCodeViewer -> Extrusion toolpaths colored by height
This commit is contained in:
parent
179dbc7d0e
commit
6e5a6f3b43
3 changed files with 137 additions and 20 deletions
|
@ -56,8 +56,7 @@ public:
|
||||||
// Color mapping to convert a float into a smooth rainbow of 10 colors.
|
// Color mapping to convert a float into a smooth rainbow of 10 colors.
|
||||||
class RangeBase
|
class RangeBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
virtual void reset() = 0;
|
virtual void reset() = 0;
|
||||||
virtual bool empty() const = 0;
|
virtual bool empty() const = 0;
|
||||||
virtual float min() const = 0;
|
virtual float min() const = 0;
|
||||||
|
@ -73,7 +72,7 @@ public:
|
||||||
// Color mapping converting a float in a range between a min and a max into a smooth rainbow of 10 colors.
|
// Color mapping converting a float in a range between a min and a max into a smooth rainbow of 10 colors.
|
||||||
class Range : public RangeBase
|
class Range : public RangeBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Range();
|
Range();
|
||||||
|
|
||||||
// RangeBase Overrides
|
// RangeBase Overrides
|
||||||
|
@ -97,8 +96,7 @@ public:
|
||||||
template <typename EnumRangeType>
|
template <typename EnumRangeType>
|
||||||
class MultiRange : public RangeBase
|
class MultiRange : public RangeBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void reset() override
|
void reset() override
|
||||||
{
|
{
|
||||||
bounds = decltype(bounds){};
|
bounds = decltype(bounds){};
|
||||||
|
@ -160,8 +158,7 @@ public:
|
||||||
mode.set(static_cast<std::size_t>(range_type_value), enable);
|
mode.set(static_cast<std::size_t>(range_type_value), enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Interval bounds
|
// Interval bounds
|
||||||
struct Bounds
|
struct Bounds
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,14 +59,38 @@ bool GCodeViewer::IBuffer::init_shader(const std::string& vertex_shader_src, con
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GCodeViewer::IBuffer::add_path(GCodeProcessor::EMoveType type, ExtrusionRole role)
|
void GCodeViewer::IBuffer::add_path(GCodeProcessor::EMoveType type, ExtrusionRole role, float height)
|
||||||
{
|
{
|
||||||
unsigned int id = static_cast<unsigned int>(data.size());
|
unsigned int id = static_cast<unsigned int>(data.size());
|
||||||
paths.push_back({ type, role, id, id });
|
paths.push_back({ type, role, id, id, height });
|
||||||
|
}
|
||||||
|
|
||||||
|
std::array<float, 4> GCodeViewer::Extrusions::Range::get_color_at(float value, const std::array<std::array<float, 4>, Default_Range_Colors_Count>& colors) const
|
||||||
|
{
|
||||||
|
// Input value scaled to the color range
|
||||||
|
const float step = step_size();
|
||||||
|
const float global_t = (step != 0.0f) ? std::max(0.0f, value - min) / step : 0.0f; // lower limit of 0.0f
|
||||||
|
|
||||||
|
const size_t color_max_idx = colors.size() - 1;
|
||||||
|
|
||||||
|
// Compute the two colors just below (low) and above (high) the input value
|
||||||
|
const size_t color_low_idx = std::clamp(static_cast<std::size_t>(global_t), std::size_t{ 0 }, color_max_idx);
|
||||||
|
const size_t color_high_idx = std::clamp(color_low_idx + 1, std::size_t{ 0 }, color_max_idx);
|
||||||
|
|
||||||
|
// Compute how far the value is between the low and high colors so that they can be interpolated
|
||||||
|
const float local_t = std::min(global_t - static_cast<float>(color_low_idx), 1.0f); // upper limit of 1.0f
|
||||||
|
|
||||||
|
// Interpolate between the low and high colors in RGB space to find exactly which color the input value should get
|
||||||
|
std::array<float, 4> ret;
|
||||||
|
for (unsigned int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
ret[i] = lerp(colors[color_low_idx][i], colors[color_high_idx][i], local_t);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::array<std::array<float, 4>, erCount> GCodeViewer::Default_Extrusion_Role_Colors {{
|
const std::array<std::array<float, 4>, erCount> GCodeViewer::Default_Extrusion_Role_Colors {{
|
||||||
{ 0.00f, 0.00f, 0.00f, 1.0f }, // erNone
|
{ 1.00f, 1.00f, 1.00f, 1.0f }, // erNone
|
||||||
{ 1.00f, 1.00f, 0.40f, 1.0f }, // erPerimeter
|
{ 1.00f, 1.00f, 0.40f, 1.0f }, // erPerimeter
|
||||||
{ 1.00f, 0.65f, 0.00f, 1.0f }, // erExternalPerimeter
|
{ 1.00f, 0.65f, 0.00f, 1.0f }, // erExternalPerimeter
|
||||||
{ 0.00f, 0.00f, 1.00f, 1.0f }, // erOverhangPerimeter
|
{ 0.00f, 0.00f, 1.00f, 1.0f }, // erOverhangPerimeter
|
||||||
|
@ -83,6 +107,19 @@ const std::array<std::array<float, 4>, erCount> GCodeViewer::Default_Extrusion_R
|
||||||
{ 0.00f, 0.00f, 0.00f, 1.0f } // erMixed
|
{ 0.00f, 0.00f, 0.00f, 1.0f } // erMixed
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
const std::array<std::array<float, 4>, GCodeViewer::Default_Range_Colors_Count> GCodeViewer::Default_Range_Colors {{
|
||||||
|
{ 0.043f, 0.173f, 0.478f, 1.0f },
|
||||||
|
{ 0.075f, 0.349f, 0.522f, 1.0f },
|
||||||
|
{ 0.110f, 0.533f, 0.569f, 1.0f },
|
||||||
|
{ 0.016f, 0.839f, 0.059f, 1.0f },
|
||||||
|
{ 0.667f, 0.949f, 0.000f, 1.0f },
|
||||||
|
{ 0.988f, 0.975f, 0.012f, 1.0f },
|
||||||
|
{ 0.961f, 0.808f, 0.039f, 1.0f },
|
||||||
|
{ 0.890f, 0.533f, 0.125f, 1.0f },
|
||||||
|
{ 0.820f, 0.408f, 0.188f, 1.0f },
|
||||||
|
{ 0.761f, 0.322f, 0.235f, 1.0f }
|
||||||
|
}};
|
||||||
|
|
||||||
void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized)
|
void GCodeViewer::load(const GCodeProcessor::Result& gcode_result, const Print& print, bool initialized)
|
||||||
{
|
{
|
||||||
if (m_last_result_id == gcode_result.id)
|
if (m_last_result_id == gcode_result.id)
|
||||||
|
@ -108,6 +145,7 @@ void GCodeViewer::reset()
|
||||||
|
|
||||||
m_bounding_box = BoundingBoxf3();
|
m_bounding_box = BoundingBoxf3();
|
||||||
m_extrusions.reset_role_visibility_flags();
|
m_extrusions.reset_role_visibility_flags();
|
||||||
|
m_extrusions.reset_ranges();
|
||||||
m_shells.volumes.clear();
|
m_shells.volumes.clear();
|
||||||
m_layers_zs = std::vector<double>();
|
m_layers_zs = std::vector<double>();
|
||||||
m_roles = std::vector<ExtrusionRole>();
|
m_roles = std::vector<ExtrusionRole>();
|
||||||
|
@ -241,17 +279,22 @@ void GCodeViewer::load_toolpaths(const GCodeProcessor::Result& gcode_result)
|
||||||
case GCodeProcessor::EMoveType::Retract:
|
case GCodeProcessor::EMoveType::Retract:
|
||||||
case GCodeProcessor::EMoveType::Unretract:
|
case GCodeProcessor::EMoveType::Unretract:
|
||||||
{
|
{
|
||||||
buffer.add_path(curr.type, curr.extrusion_role);
|
buffer.add_path(curr.type, curr.extrusion_role, curr.height);
|
||||||
buffer.data.push_back(static_cast<unsigned int>(i));
|
buffer.data.push_back(static_cast<unsigned int>(i));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case GCodeProcessor::EMoveType::Extrude:
|
case GCodeProcessor::EMoveType::Extrude:
|
||||||
case GCodeProcessor::EMoveType::Travel:
|
case GCodeProcessor::EMoveType::Travel:
|
||||||
{
|
{
|
||||||
if (prev.type != curr.type)
|
if (prev.type != curr.type || !buffer.paths.back().matches(curr))
|
||||||
{
|
{
|
||||||
buffer.add_path(curr.type, curr.extrusion_role);
|
buffer.add_path(curr.type, curr.extrusion_role, curr.height);
|
||||||
buffer.data.push_back(static_cast<unsigned int>(i - 1));
|
buffer.data.push_back(static_cast<unsigned int>(i - 1));
|
||||||
|
|
||||||
|
if (curr.type == GCodeProcessor::EMoveType::Extrude)
|
||||||
|
{
|
||||||
|
m_extrusions.ranges.height.update_from(curr.height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.paths.back().last = static_cast<unsigned int>(buffer.data.size());
|
buffer.paths.back().last = static_cast<unsigned int>(buffer.data.size());
|
||||||
|
@ -375,6 +418,10 @@ void GCodeViewer::render_toolpaths() const
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EViewType::Height:
|
case EViewType::Height:
|
||||||
|
{
|
||||||
|
color = m_extrusions.ranges.height.get_color_at(path.height, m_extrusions.ranges.colors);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case EViewType::Width:
|
case EViewType::Width:
|
||||||
case EViewType::Feedrate:
|
case EViewType::Feedrate:
|
||||||
case EViewType::FanSpeed:
|
case EViewType::FanSpeed:
|
||||||
|
@ -513,8 +560,9 @@ void GCodeViewer::render_shells() const
|
||||||
void GCodeViewer::render_overlay() const
|
void GCodeViewer::render_overlay() const
|
||||||
{
|
{
|
||||||
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
|
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
|
||||||
static const float ICON_BORDER_SIZE = 20.0f;
|
static const float ICON_BORDER_SIZE = 25.0f;
|
||||||
static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
|
static const ImU32 ICON_BORDER_COLOR = ImGui::GetColorU32(ImVec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||||
|
static const float GAP_ICON_TEXT = 5.0f;
|
||||||
|
|
||||||
if (!m_legend_enabled || m_roles.empty())
|
if (!m_legend_enabled || m_roles.empty())
|
||||||
return;
|
return;
|
||||||
|
@ -551,16 +599,34 @@ void GCodeViewer::render_overlay() const
|
||||||
{
|
{
|
||||||
ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f);
|
ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f);
|
||||||
draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0);
|
draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0);
|
||||||
const std::array<float, 4>& role_color = m_extrusions.role_colors[static_cast<unsigned int>(role)];
|
const std::array<float, 4>& color = m_extrusions.role_colors[static_cast<unsigned int>(role)];
|
||||||
ImU32 fill_color = ImGui::GetColorU32(ImVec4(role_color[0], role_color[1], role_color[2], role_color[3]));
|
ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3]));
|
||||||
draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color);
|
draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color);
|
||||||
ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + 4.0f);
|
ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT);
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
imgui.text(Slic3r::I18N::translate(ExtrusionEntity::role_to_string(role)));
|
imgui.text(Slic3r::I18N::translate(ExtrusionEntity::role_to_string(role)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EViewType::Height: { break; }
|
case EViewType::Height:
|
||||||
|
{
|
||||||
|
float step_size = m_extrusions.ranges.height.step_size();
|
||||||
|
for (int i = Default_Range_Colors_Count - 1; i >= 0; --i)
|
||||||
|
{
|
||||||
|
ImVec2 pos(ImGui::GetCursorPosX() + 2.0f, ImGui::GetCursorPosY() + 2.0f);
|
||||||
|
draw_list->AddRect(ImVec2(pos.x, pos.y), ImVec2(pos.x + ICON_BORDER_SIZE, pos.y + ICON_BORDER_SIZE), ICON_BORDER_COLOR, 0.0f, 0);
|
||||||
|
const std::array<float, 4>& color = m_extrusions.ranges.colors[i];
|
||||||
|
ImU32 fill_color = ImGui::GetColorU32(ImVec4(color[0], color[1], color[2], color[3]));
|
||||||
|
draw_list->AddRectFilled(ImVec2(pos.x + 1.0f, pos.y + 1.0f), ImVec2(pos.x + ICON_BORDER_SIZE - 1.0f, pos.y + ICON_BORDER_SIZE - 1.0f), fill_color);
|
||||||
|
ImGui::SetCursorPosX(pos.x + ICON_BORDER_SIZE + GAP_ICON_TEXT);
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
char buf[1024];
|
||||||
|
::sprintf(buf, "%.*f", 3, m_extrusions.ranges.height.min + static_cast<float>(i) * step_size);
|
||||||
|
imgui.text(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case EViewType::Width: { break; }
|
case EViewType::Width: { break; }
|
||||||
case EViewType::Feedrate: { break; }
|
case EViewType::Feedrate: { break; }
|
||||||
case EViewType::FanSpeed: { break; }
|
case EViewType::FanSpeed: { break; }
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
#include "3DScene.hpp"
|
#include "3DScene.hpp"
|
||||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||||
|
|
||||||
|
#include <float.h>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
class Print;
|
class Print;
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
@ -14,6 +16,8 @@ namespace GUI {
|
||||||
class GCodeViewer
|
class GCodeViewer
|
||||||
{
|
{
|
||||||
static const std::array<std::array<float, 4>, erCount> Default_Extrusion_Role_Colors;
|
static const std::array<std::array<float, 4>, erCount> Default_Extrusion_Role_Colors;
|
||||||
|
static const size_t Default_Range_Colors_Count = 10;
|
||||||
|
static const std::array<std::array<float, 4>, Default_Range_Colors_Count> Default_Range_Colors;
|
||||||
|
|
||||||
// buffer containing vertices data
|
// buffer containing vertices data
|
||||||
struct VBuffer
|
struct VBuffer
|
||||||
|
@ -29,14 +33,16 @@ class GCodeViewer
|
||||||
static size_t vertex_size_bytes() { return vertex_size() * sizeof(float); }
|
static size_t vertex_size_bytes() { return vertex_size() * sizeof(float); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used to identify different toolpath sub-types inside a IBuffer
|
||||||
struct Path
|
struct Path
|
||||||
{
|
{
|
||||||
GCodeProcessor::EMoveType type{ GCodeProcessor::EMoveType::Noop };
|
GCodeProcessor::EMoveType type{ GCodeProcessor::EMoveType::Noop };
|
||||||
ExtrusionRole role{ erNone };
|
ExtrusionRole role{ erNone };
|
||||||
unsigned int first{ 0 };
|
unsigned int first{ 0 };
|
||||||
unsigned int last{ 0 };
|
unsigned int last{ 0 };
|
||||||
|
float height{ 0.0f };
|
||||||
|
|
||||||
bool matches(GCodeProcessor::EMoveType type, ExtrusionRole role) const { return this->type == type && this->role == role; }
|
bool matches(const GCodeProcessor::MoveVertex& move) const { return type == move.type && role == move.extrusion_role && height == move.height; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// buffer containing indices data and shader for a specific toolpath type
|
// buffer containing indices data and shader for a specific toolpath type
|
||||||
|
@ -52,7 +58,7 @@ class GCodeViewer
|
||||||
void reset();
|
void reset();
|
||||||
bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src);
|
bool init_shader(const std::string& vertex_shader_src, const std::string& fragment_shader_src);
|
||||||
|
|
||||||
void add_path(GCodeProcessor::EMoveType type, ExtrusionRole role);
|
void add_path(GCodeProcessor::EMoveType type, ExtrusionRole role, float height);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Shells
|
struct Shells
|
||||||
|
@ -62,10 +68,55 @@ class GCodeViewer
|
||||||
Shader shader;
|
Shader shader;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// helper to render extrusion paths
|
||||||
struct Extrusions
|
struct Extrusions
|
||||||
{
|
{
|
||||||
|
struct Range
|
||||||
|
{
|
||||||
|
float min;
|
||||||
|
float max;
|
||||||
|
|
||||||
|
Range() { reset(); }
|
||||||
|
|
||||||
|
void update_from(const float value)
|
||||||
|
{
|
||||||
|
min = std::min(min, value);
|
||||||
|
max = std::max(max, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
min = FLT_MAX;
|
||||||
|
max = -FLT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
float step_size() const { return (max - min) / static_cast<float>(Default_Range_Colors_Count); }
|
||||||
|
std::array<float, 4> get_color_at(float value, const std::array<std::array<float, 4>, Default_Range_Colors_Count>& colors) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Ranges
|
||||||
|
{
|
||||||
|
std::array<std::array<float, 4>, Default_Range_Colors_Count> colors;
|
||||||
|
|
||||||
|
// Color mapping by layer height.
|
||||||
|
Range height;
|
||||||
|
// // Color mapping by extrusion width.
|
||||||
|
// Range width;
|
||||||
|
// // Color mapping by feedrate.
|
||||||
|
// MultiRange<FeedrateKind> feedrate;
|
||||||
|
// // Color mapping by fan speed.
|
||||||
|
// Range fan_speed;
|
||||||
|
// // Color mapping by volumetric extrusion rate.
|
||||||
|
// Range volumetric_rate;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
height.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::array<std::array<float, 4>, erCount> role_colors;
|
std::array<std::array<float, 4>, erCount> role_colors;
|
||||||
unsigned int role_visibility_flags{ 0 };
|
unsigned int role_visibility_flags{ 0 };
|
||||||
|
Ranges ranges;
|
||||||
|
|
||||||
void reset_role_visibility_flags() {
|
void reset_role_visibility_flags() {
|
||||||
role_visibility_flags = 0;
|
role_visibility_flags = 0;
|
||||||
|
@ -75,6 +126,8 @@ class GCodeViewer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reset_ranges() { ranges.reset(); }
|
||||||
|
|
||||||
static bool is_role_visible(unsigned int flags, ExtrusionRole role) {
|
static bool is_role_visible(unsigned int flags, ExtrusionRole role) {
|
||||||
return role < erCount && (flags & (1 << role)) != 0;
|
return role < erCount && (flags & (1 << role)) != 0;
|
||||||
}
|
}
|
||||||
|
@ -113,6 +166,7 @@ public:
|
||||||
|
|
||||||
bool init() {
|
bool init() {
|
||||||
m_extrusions.role_colors = Default_Extrusion_Role_Colors;
|
m_extrusions.role_colors = Default_Extrusion_Role_Colors;
|
||||||
|
m_extrusions.ranges.colors = Default_Range_Colors;
|
||||||
set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true);
|
set_toolpath_move_type_visible(GCodeProcessor::EMoveType::Extrude, true);
|
||||||
return init_shaders();
|
return init_shaders();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue