New gcode visualization integration - Export toolpaths to obj

This commit is contained in:
enricoturri1966 2023-12-15 12:37:24 +01:00 committed by Lukas Matena
parent dcd8b30842
commit 137495dd88
5 changed files with 500 additions and 187 deletions

View File

@ -1611,7 +1611,11 @@ bool GCodeViewer::can_export_toolpaths() const
{
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#if ENABLE_NEW_GCODE_VIEWER
// TODO: Fix the export to obj first
const std::array<uint32_t, 2>& visible_range = m_viewer.get_view_visible_range();
for (size_t i = visible_range[0]; i <= visible_range[1]; ++i) {
if (m_viewer.get_vertex_at(i).is_extrusion())
return true;
}
return false;
#else
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@ -1756,6 +1760,307 @@ void GCodeViewer::set_layers_z_range(const std::array<unsigned int, 2>& layers_z
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
}
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#if ENABLE_NEW_GCODE_VIEWER
class ToolpathsObjExporter
{
public:
explicit ToolpathsObjExporter(const libvgcode::Viewer& viewer)
: m_viewer(viewer) {
}
void export_to(const std::string& filename) {
CNumericLocalesSetter locales_setter;
// open geometry file
FilePtr f_geo = boost::nowide::fopen(filename.c_str(), "w");
if (f_geo.f == nullptr) {
BOOST_LOG_TRIVIAL(error) << "ToolpathsObjExporter: Couldn't open " << filename << " for writing";
return;
}
boost::filesystem::path materials_filename(filename);
materials_filename.replace_extension("mtl");
// write header to geometry file
fprintf(f_geo.f, "# G-Code Toolpaths\n");
fprintf(f_geo.f, "# Generated by %s-%s based on Slic3r\n", SLIC3R_APP_NAME, SLIC3R_VERSION);
fprintf(f_geo.f, "\nmtllib ./%s\n", materials_filename.filename().string().c_str());
// open material file
FilePtr f_mat = boost::nowide::fopen(materials_filename.string().c_str(), "w");
if (f_mat.f == nullptr) {
BOOST_LOG_TRIVIAL(error) << "ToolpathsObjExporter: Couldn't open " << materials_filename.string() << " for writing";
return;
}
// write header to material file
fprintf(f_mat.f, "# G-Code Toolpaths Materials\n");
fprintf(f_mat.f, "# Generated by %s-%s based on Slic3r\n", SLIC3R_APP_NAME, SLIC3R_VERSION);
const std::array<uint32_t, 2>& visible_range = m_viewer.get_view_visible_range();
for (size_t i = visible_range[0]; i <= visible_range[1]; ++i) {
const libvgcode::PathVertex& curr = m_viewer.get_vertex_at(i);
const libvgcode::PathVertex& next = m_viewer.get_vertex_at(i + 1);
if (!curr.is_extrusion() || !next.is_extrusion())
continue;
const libvgcode::PathVertex& nextnext = m_viewer.get_vertex_at(i + 2);
unsigned char flags = 0;
if (curr.gcode_id == next.gcode_id)
flags |= Flag_First;
if (!nextnext.is_extrusion())
flags |= Flag_Last;
else
flags |= Flag_Internal;
export_segment(*f_geo.f, flags, curr, next, nextnext);
}
export_materials(*f_mat.f);
}
private:
const libvgcode::Viewer& m_viewer;
size_t m_vertices_count{ 0 };
std::vector<libvgcode::Color> m_colors;
static const unsigned char Flag_First = 0x01;
static const unsigned char Flag_Last = 0x02;
static const unsigned char Flag_Internal = 0x04;
unsigned char m_flags{ 0 };
static const float Cap_Rounding_Factor;
struct SegmentLocalSystem
{
Vec3f forward;
Vec3f right;
Vec3f up;
};
SegmentLocalSystem segment_local_system(const Vec3f& v1, const Vec3f& v2) {
SegmentLocalSystem ret;
const Vec3f line = v2 - v1;
const float line_sqlen = line.squaredNorm();
ret.forward = (line_sqlen < 1e-4) ? Vec3f::UnitX() : line.normalized();
ret.right = ret.forward.cross(Vec3f::UnitZ()).normalized();
ret.up = ret.right.cross(ret.forward);
return ret;
}
struct Vertex
{
Vec3f position;
Vec3f normal;
};
struct VertexCrossSection
{
Vertex right;
Vertex top;
Vertex left;
Vertex bottom;
};
VertexCrossSection cross_section(const Vec3f& v, const Vec3f& right, const Vec3f& up, float width, float height) {
VertexCrossSection ret;
const Vec3f w_shift = 0.5f * width * right;
const Vec3f h_shift = 0.5f * height * up;
ret.right.position = v + w_shift;
ret.right.normal = right;
ret.top.position = v + h_shift;
ret.top.normal = up;
ret.left.position = v - w_shift;
ret.left.normal = -right;
ret.bottom.position = v - h_shift;
ret.bottom.normal = -up;
return ret;
}
VertexCrossSection normal_cross_section(const Vec3f& v, const SegmentLocalSystem& axes, float width, float height) {
return cross_section(v, axes.right, axes.up, width, height);
}
enum CornerType : unsigned char
{
RightTurn,
LeftTurn,
Straight
};
VertexCrossSection corner_cross_section(const Vec3f& v, const SegmentLocalSystem& axes1, const SegmentLocalSystem& axes2,
float width, float height, CornerType& corner_type) {
if (std::abs(std::abs(axes1.forward.dot(axes2.forward)) - 1.0f) < EPSILON)
corner_type = CornerType::Straight;
else if (axes1.up.dot(axes1.forward.cross(axes2.forward)) < 0.0f)
corner_type = CornerType::RightTurn;
else
corner_type = CornerType::LeftTurn;
const Vec3f right = (0.5f * (axes1.right + axes2.right)).normalized();
return cross_section(v, right, axes1.up, width, height);
}
void export_segment(FILE& f, unsigned char flags, const libvgcode::PathVertex& v1, const libvgcode::PathVertex& v2, const libvgcode::PathVertex& v3) {
const Vec3f v1_pos = libvgcode::convert(v1.position);
const Vec3f v2_pos = libvgcode::convert(v2.position);
const Vec3f v3_pos = libvgcode::convert(v3.position);
const SegmentLocalSystem v1_v2 = segment_local_system(v1_pos, v2_pos);
const SegmentLocalSystem v2_v3 = segment_local_system(v2_pos, v3_pos);
// starting cap
if ((flags & Flag_First) > 0) {
const Vertex v0 = { v1_pos - Cap_Rounding_Factor * v1.width * v1_v2.forward, -v1_v2.forward };
const VertexCrossSection ncs = normal_cross_section(v1_pos, v1_v2, v1.width, v1.height);
export_vertex(f, v0); // 0
export_vertex(f, ncs.right); // 1
export_vertex(f, ncs.top); // 2
export_vertex(f, ncs.left); // 3
export_vertex(f, ncs.bottom); // 4
export_material(f, color_id(v1));
export_triangle(f, vertex_id(0), vertex_id(1), vertex_id(2));
export_triangle(f, vertex_id(0), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(0), vertex_id(3), vertex_id(4));
export_triangle(f, vertex_id(0), vertex_id(4), vertex_id(1));
m_vertices_count += 5;
}
// segment body + ending cap
if ((flags & Flag_Last) > 0) {
const Vertex v0 = { v2_pos + Cap_Rounding_Factor * v2.width * v1_v2.forward, v1_v2.forward };
const VertexCrossSection ncs = normal_cross_section(v2_pos, v1_v2, v2.width, v2.height);
export_vertex(f, v0); // 0
export_vertex(f, ncs.right); // 1
export_vertex(f, ncs.top); // 2
export_vertex(f, ncs.left); // 3
export_vertex(f, ncs.bottom); // 4
export_material(f, color_id(v2));
// segment body
export_triangle(f, vertex_id(-4), vertex_id(1), vertex_id(2));
export_triangle(f, vertex_id(-4), vertex_id(2), vertex_id(-3));
export_triangle(f, vertex_id(-3), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(-3), vertex_id(3), vertex_id(-2));
export_triangle(f, vertex_id(-2), vertex_id(3), vertex_id(4));
export_triangle(f, vertex_id(-2), vertex_id(4), vertex_id(-1));
export_triangle(f, vertex_id(-1), vertex_id(4), vertex_id(1));
export_triangle(f, vertex_id(-1), vertex_id(1), vertex_id(-4));
// ending cap
export_triangle(f, vertex_id(0), vertex_id(3), vertex_id(2));
export_triangle(f, vertex_id(0), vertex_id(2), vertex_id(1));
export_triangle(f, vertex_id(0), vertex_id(1), vertex_id(4));
export_triangle(f, vertex_id(0), vertex_id(4), vertex_id(3));
m_vertices_count += 5;
}
else {
CornerType corner_type;
const VertexCrossSection ccs = corner_cross_section(v2_pos, v1_v2, v2_v3, v2.width, v2.height, corner_type);
const VertexCrossSection ncs12 = normal_cross_section(v2_pos, v1_v2, v2.width, v2.height);
const VertexCrossSection ncs23 = normal_cross_section(v2_pos, v2_v3, v2.width, v2.height);
if (corner_type == CornerType::Straight) {
export_vertex(f, ncs12.right); // 0
export_vertex(f, ncs12.top); // 1
export_vertex(f, ncs12.left); // 2
export_vertex(f, ncs12.bottom); // 3
export_material(f, color_id(v2));
// segment body
export_triangle(f, vertex_id(-4), vertex_id(0), vertex_id(1));
export_triangle(f, vertex_id(-4), vertex_id(1), vertex_id(-3));
export_triangle(f, vertex_id(-3), vertex_id(1), vertex_id(2));
export_triangle(f, vertex_id(-3), vertex_id(2), vertex_id(-2));
export_triangle(f, vertex_id(-2), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(-2), vertex_id(3), vertex_id(-1));
export_triangle(f, vertex_id(-1), vertex_id(3), vertex_id(0));
export_triangle(f, vertex_id(-1), vertex_id(0), vertex_id(-4));
m_vertices_count += 4;
}
else if (corner_type == CornerType::RightTurn) {
export_vertex(f, ncs12.left); // 0
export_vertex(f, ccs.left); // 1
export_vertex(f, ccs.right); // 2
export_vertex(f, ncs12.top); // 3
export_vertex(f, ncs23.left); // 4
export_vertex(f, ncs12.bottom); // 5
export_material(f, color_id(v2));
// segment body
export_triangle(f, vertex_id(-4), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(-4), vertex_id(3), vertex_id(-3));
export_triangle(f, vertex_id(-3), vertex_id(3), vertex_id(0));
export_triangle(f, vertex_id(-3), vertex_id(0), vertex_id(-2));
export_triangle(f, vertex_id(-2), vertex_id(0), vertex_id(5));
export_triangle(f, vertex_id(-2), vertex_id(5), vertex_id(-1));
export_triangle(f, vertex_id(-1), vertex_id(5), vertex_id(2));
export_triangle(f, vertex_id(-1), vertex_id(2), vertex_id(-4));
// corner
export_triangle(f, vertex_id(1), vertex_id(0), vertex_id(3));
export_triangle(f, vertex_id(1), vertex_id(3), vertex_id(4));
export_triangle(f, vertex_id(1), vertex_id(4), vertex_id(5));
export_triangle(f, vertex_id(1), vertex_id(5), vertex_id(0));
m_vertices_count += 6;
}
else {
export_vertex(f, ncs12.right); // 0
export_vertex(f, ccs.right); // 1
export_vertex(f, ncs23.right); // 2
export_vertex(f, ncs12.top); // 3
export_vertex(f, ccs.left); // 4
export_vertex(f, ncs12.bottom); // 5
export_material(f, color_id(v2));
// segment body
export_triangle(f, vertex_id(-4), vertex_id(0), vertex_id(3));
export_triangle(f, vertex_id(-4), vertex_id(3), vertex_id(-3));
export_triangle(f, vertex_id(-3), vertex_id(3), vertex_id(4));
export_triangle(f, vertex_id(-3), vertex_id(4), vertex_id(-2));
export_triangle(f, vertex_id(-2), vertex_id(4), vertex_id(5));
export_triangle(f, vertex_id(-2), vertex_id(5), vertex_id(-1));
export_triangle(f, vertex_id(-1), vertex_id(5), vertex_id(0));
export_triangle(f, vertex_id(-1), vertex_id(0), vertex_id(-4));
// corner
export_triangle(f, vertex_id(1), vertex_id(2), vertex_id(3));
export_triangle(f, vertex_id(1), vertex_id(3), vertex_id(0));
export_triangle(f, vertex_id(1), vertex_id(0), vertex_id(5));
export_triangle(f, vertex_id(1), vertex_id(5), vertex_id(2));
m_vertices_count += 6;
}
}
}
size_t vertex_id(int id) { return static_cast<size_t>(1 + static_cast<int>(m_vertices_count) + id); }
void export_vertex(FILE& f, const Vertex& v) {
fprintf(&f, "v %g %g %g\n", v.position.x(), v.position.y(), v.position.z());
fprintf(&f, "vn %g %g %g\n", v.normal.x(), v.normal.y(), v.normal.z());
}
void export_material(FILE& f, size_t material_id) {
fprintf(&f, "\nusemtl material_%zu\n", material_id + 1);
}
void export_triangle(FILE& f, size_t v1, size_t v2, size_t v3) {
fprintf(&f, "f %zu//%zu %zu//%zu %zu//%zu\n", v1, v1, v2, v2, v3, v3);
}
void export_materials(FILE& f) {
static const float inv_255 = 1.0f / 255.0f;
size_t materials_counter = 0;
for (const auto& color : m_colors) {
fprintf(&f, "\nnewmtl material_%zu\n", ++materials_counter);
fprintf(&f, "Ka 1 1 1\n");
fprintf(&f, "Kd %g %g %g\n", static_cast<float>(color[0]) * inv_255,
static_cast<float>(color[1]) * inv_255,
static_cast<float>(color[2]) * inv_255);
fprintf(&f, "Ks 0 0 0\n");
}
}
size_t color_id(const libvgcode::PathVertex& v) {
const libvgcode::Color color = m_viewer.get_vertex_color(v);
auto color_it = std::find_if(m_colors.begin(), m_colors.end(), [&color](const libvgcode::Color& m) { return m == color; });
if (color_it == m_colors.end()) {
m_colors.emplace_back(color);
color_it = std::prev(m_colors.end());
}
return std::distance(m_colors.begin(), color_it);
}
};
const float ToolpathsObjExporter::Cap_Rounding_Factor = 0.25f;
#endif // ENABLE_NEW_GCODE_VIEWER
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
{
if (filename == nullptr)
@ -1768,7 +2073,8 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#if ENABLE_NEW_GCODE_VIEWER
// TODO -> new export to obj
ToolpathsObjExporter exporter(m_viewer);
exporter.export_to(filename);
#else
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
// the data needed is contained into the Extrude TBuffer

View File

@ -89,6 +89,8 @@ public:
//
PathVertex get_vertex_at(uint32_t id) const;
Color get_vertex_color(const PathVertex& vertex) const;
//
// Return the count of path segments enabled for rendering
//

View File

@ -182,6 +182,11 @@ PathVertex Viewer::get_vertex_at(uint32_t id) const
return m_impl->get_vertex_at(id);
}
Color Viewer::get_vertex_color(const PathVertex& vertex) const
{
return m_impl->get_vertex_color(vertex);
}
size_t Viewer::get_enabled_segments_count() const
{
return m_impl->get_enabled_segments_count();

View File

@ -703,7 +703,7 @@ void ViewerImpl::update_colors()
for (size_t i = 0; i < m_vertices.size(); ++i) {
colors[i] = (color_top_layer_only && m_vertices[i].layer_id < top_layer_id &&
(!m_settings.spiral_vase_mode || i != m_view_range.get_enabled()[0])) ?
encode_color(Dummy_Color) : encode_color(select_color(m_vertices[i]));
encode_color(Dummy_Color) : encode_color(get_vertex_color(m_vertices[i]));
}
// update gpu buffer for colors
@ -933,6 +933,73 @@ PathVertex ViewerImpl::get_vertex_at(size_t id) const
return (id < m_vertices.size()) ? m_vertices[id] : PathVertex();
}
Color ViewerImpl::get_vertex_color(const PathVertex& v) const
{
if (v.type == EMoveType::Noop)
return Dummy_Color;
if (v.is_wipe())
return Wipe_Color;
if (v.is_option())
return get_option_color(type_to_option(v.type));
switch (m_settings.view_type)
{
case EViewType::FeatureType:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : get_extrusion_role_color(v.role);
}
case EViewType::Height:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_height_range.get_color_at(v.height);
}
case EViewType::Width:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_width_range.get_color_at(v.width);
}
case EViewType::Speed:
{
return m_speed_range.get_color_at(v.feedrate);
}
case EViewType::FanSpeed:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_fan_speed_range.get_color_at(v.fan_speed);
}
case EViewType::Temperature:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_temperature_range.get_color_at(v.temperature);
}
case EViewType::VolumetricFlowRate:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_volumetric_rate_range.get_color_at(v.volumetric_rate);
}
case EViewType::LayerTimeLinear:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) :
m_layer_time_range[0].get_color_at(m_layers.get_layer_time(m_settings.time_mode, static_cast<size_t>(v.layer_id)));
}
case EViewType::LayerTimeLogarithmic:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) :
m_layer_time_range[1].get_color_at(m_layers.get_layer_time(m_settings.time_mode, static_cast<size_t>(v.layer_id)));
}
case EViewType::Tool:
{
assert(static_cast<size_t>(v.extruder_id) < m_tool_colors.size());
return m_tool_colors[v.extruder_id];
}
case EViewType::ColorPrint:
{
return m_layers.layer_contains_colorprint_options(static_cast<size_t>(v.layer_id)) ? Dummy_Color :
m_tool_colors[static_cast<size_t>(v.color_id) % m_tool_colors.size()];
}
default: { break; }
}
return Dummy_Color;
}
size_t ViewerImpl::get_enabled_segments_count() const
{
return m_enabled_segments_count;
@ -1369,73 +1436,6 @@ void ViewerImpl::update_heights_widths()
glsafe(glBindBuffer(GL_TEXTURE_BUFFER, 0));
}
Color ViewerImpl::select_color(const PathVertex& v) const
{
if (v.type == EMoveType::Noop)
return Dummy_Color;
if (v.is_wipe())
return Wipe_Color;
if (v.is_option())
return get_option_color(type_to_option(v.type));
switch (m_settings.view_type)
{
case EViewType::FeatureType:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : get_extrusion_role_color(v.role);
}
case EViewType::Height:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_height_range.get_color_at(v.height);
}
case EViewType::Width:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_width_range.get_color_at(v.width);
}
case EViewType::Speed:
{
return m_speed_range.get_color_at(v.feedrate);
}
case EViewType::FanSpeed:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_fan_speed_range.get_color_at(v.fan_speed);
}
case EViewType::Temperature:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_temperature_range.get_color_at(v.temperature);
}
case EViewType::VolumetricFlowRate:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) : m_volumetric_rate_range.get_color_at(v.volumetric_rate);
}
case EViewType::LayerTimeLinear:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) :
m_layer_time_range[0].get_color_at(m_layers.get_layer_time(m_settings.time_mode, static_cast<size_t>(v.layer_id)));
}
case EViewType::LayerTimeLogarithmic:
{
return v.is_travel() ? get_travel_move_color(static_cast<ETravelMoveType>(v.role)) :
m_layer_time_range[1].get_color_at(m_layers.get_layer_time(m_settings.time_mode, static_cast<size_t>(v.layer_id)));
}
case EViewType::Tool:
{
assert(static_cast<size_t>(v.extruder_id) < m_tool_colors.size());
return m_tool_colors[v.extruder_id];
}
case EViewType::ColorPrint:
{
return m_layers.layer_contains_colorprint_options(static_cast<size_t>(v.layer_id)) ? Dummy_Color :
m_tool_colors[static_cast<size_t>(v.color_id) % m_tool_colors.size()];
}
default: { break; }
}
return Dummy_Color;
}
void ViewerImpl::render_segments(const Mat4x4& view_matrix, const Mat4x4& projection_matrix, const Vec3& camera_position)
{
if (m_segments_shader_id == 0)

View File

@ -109,6 +109,7 @@ public:
size_t get_vertices_count() const;
PathVertex get_current_vertex() const;
PathVertex get_vertex_at(size_t id) const;
Color get_vertex_color(const PathVertex& vertex) const;
size_t get_enabled_segments_count() const;
const std::array<uint32_t, 2>& get_enabled_segments_range() const;
@ -328,7 +329,6 @@ private:
void update_view_full_range();
void update_color_ranges();
void update_heights_widths();
Color select_color(const PathVertex& vertex) const;
void render_segments(const Mat4x4& view_matrix, const Mat4x4& projection_matrix, const Vec3& camera_position);
void render_options(const Mat4x4& view_matrix, const Mat4x4& projection_matrix);
#if !ENABLE_NEW_GCODE_VIEWER_NO_COG_AND_TOOL_MARKERS