Generation of gcode paths moved to c++

This commit is contained in:
Enrico Turri 2018-06-05 10:56:55 +02:00
parent 1f79713c15
commit 5392008916
12 changed files with 1488 additions and 689 deletions

View file

@ -5,6 +5,7 @@
#include "../../libslic3r/ClipperUtils.hpp"
#include "../../libslic3r/PrintConfig.hpp"
#include "../../libslic3r/Print.hpp"
#include "../../libslic3r/GCode/PreviewData.hpp"
#include <GL/glew.h>
@ -943,7 +944,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, wxGLContext* context)
, m_print(nullptr)
, m_dirty(true)
, m_use_VBOs(false)
, m_first_render(true)
, m_force_zoom_to_bed_enabled(false)
, m_apply_zoom_to_volumes_filter(false)
, m_hover_volume_id(-1)
, m_warning_texture_enabled(false)
@ -1190,6 +1191,12 @@ void GLCanvas3D::enable_shader(bool enable)
{
m_shader_enabled = enable;
}
void GLCanvas3D::enable_force_zoom_to_bed(bool enable)
{
m_force_zoom_to_bed_enabled = enable;
}
void GLCanvas3D::allow_multisample(bool allow)
{
m_multisample_allowed = allow;
@ -1263,14 +1270,12 @@ void GLCanvas3D::render()
if (!is_shown_on_screen())
return;
if (!set_current())
// ensures that the proper context is selected and that this canvas is initialized
if (!set_current() || !_3DScene::init(m_canvas))
return;
if (!_3DScene::init(m_canvas))
return;
if (m_first_render)
_before_first_render();
if (m_force_zoom_to_bed_enabled)
_force_zoom_to_bed();
_camera_tranform();
@ -1328,6 +1333,44 @@ void GLCanvas3D::set_toolpaths_range(double low, double high)
m_volumes->set_range(low, high);
}
void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
{
if ((m_canvas != nullptr) && (m_volumes != nullptr) && (m_print != nullptr))
{
// ensures that the proper context is selected and that this canvas is initialized
if (!set_current() || !_3DScene::init(m_canvas))
return;
if (m_volumes->empty())
{
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
m_gcode_preview_volume_index.reset();
_load_gcode_extrusion_paths(preview_data, tool_colors);
_load_gcode_travel_paths(preview_data, tool_colors);
_load_gcode_retractions(preview_data);
_load_gcode_unretractions(preview_data);
if (m_volumes->empty())
_3DScene::reset_legend_texture();
else
{
_3DScene::generate_legend_texture(preview_data, tool_colors);
// removes empty volumes
m_volumes->volumes.erase(std::remove_if(m_volumes->volumes.begin(), m_volumes->volumes.end(),
[](const GLVolume* volume) { return volume->print_zs.empty(); }),
m_volumes->volumes.end());
_load_shells();
}
}
_update_gcode_volumes_visibility(preview_data);
}
}
void GLCanvas3D::register_on_viewport_changed_callback(void* callback)
{
if (callback != nullptr)
@ -1735,10 +1778,10 @@ Point GLCanvas3D::get_local_mouse_position() const
return Point(mouse_pos.x, mouse_pos.y);
}
void GLCanvas3D::_before_first_render()
void GLCanvas3D::_force_zoom_to_bed()
{
zoom_to_bed();
m_first_render = false;
m_force_zoom_to_bed_enabled = false;
}
void GLCanvas3D::_resize(unsigned int w, unsigned int h)
@ -2352,5 +2395,605 @@ void GLCanvas3D::_stop_timer()
m_timer->Stop();
}
static inline int hex_digit_to_int(const char c)
{
return
(c >= '0' && c <= '9') ? int(c - '0') :
(c >= 'A' && c <= 'F') ? int(c - 'A') + 10 :
(c >= 'a' && c <= 'f') ? int(c - 'a') + 10 : -1;
}
std::vector<float> GLCanvas3D::_parse_colors(const std::vector<std::string>& colors)
{
std::vector<float> output(colors.size() * 4, 1.0f);
for (size_t i = 0; i < colors.size(); ++i) {
const std::string& color = colors[i];
const char* c = color.data() + 1;
if ((color.size() == 7) && (color.front() == '#'))
{
for (size_t j = 0; j < 3; ++j)
{
int digit1 = hex_digit_to_int(*c++);
int digit2 = hex_digit_to_int(*c++);
if ((digit1 == -1) || (digit2 == -1))
break;
output[i * 4 + j] = float(digit1 * 16 + digit2) / 255.0f;
}
}
}
return output;
}
void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
// helper functions to select data in dependence of the extrusion view type
struct Helper
{
static float path_filter(GCodePreviewData::Extrusion::EViewType type, const ExtrusionPath& path)
{
switch (type)
{
case GCodePreviewData::Extrusion::FeatureType:
return (float)path.role();
case GCodePreviewData::Extrusion::Height:
return path.height;
case GCodePreviewData::Extrusion::Width:
return path.width;
case GCodePreviewData::Extrusion::Feedrate:
return path.feedrate;
case GCodePreviewData::Extrusion::VolumetricRate:
return path.feedrate * (float)path.mm3_per_mm;
case GCodePreviewData::Extrusion::Tool:
return (float)path.extruder_id;
}
return 0.0f;
}
static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
{
switch (data.extrusion.view_type)
{
case GCodePreviewData::Extrusion::FeatureType:
return data.get_extrusion_role_color((ExtrusionRole)(int)value);
case GCodePreviewData::Extrusion::Height:
return data.get_height_color(value);
case GCodePreviewData::Extrusion::Width:
return data.get_width_color(value);
case GCodePreviewData::Extrusion::Feedrate:
return data.get_feedrate_color(value);
case GCodePreviewData::Extrusion::VolumetricRate:
return data.get_volumetric_rate_color(value);
case GCodePreviewData::Extrusion::Tool:
{
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float));
return color;
}
}
return GCodePreviewData::Color::Dummy;
}
};
// Helper structure for filters
struct Filter
{
float value;
ExtrusionRole role;
GLVolume* volume;
Filter(float value, ExtrusionRole role)
: value(value)
, role(role)
, volume(nullptr)
{
}
bool operator == (const Filter& other) const
{
if (value != other.value)
return false;
if (role != other.role)
return false;
return true;
}
};
typedef std::vector<Filter> FiltersList;
size_t initial_volumes_count = m_volumes->volumes.size();
// detects filters
FiltersList filters;
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
{
for (const ExtrusionPath& path : layer.paths)
{
ExtrusionRole role = path.role();
float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path);
if (std::find(filters.begin(), filters.end(), Filter(path_filter, role)) == filters.end())
filters.emplace_back(path_filter, role);
}
}
// nothing to render, return
if (filters.empty())
return;
// creates a new volume for each filter
for (Filter& filter : filters)
{
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, (unsigned int)filter.role, (unsigned int)m_volumes->volumes.size());
GLVolume* volume = new GLVolume(Helper::path_color(preview_data, tool_colors, filter.value).rgba);
if (volume != nullptr)
{
filter.volume = volume;
m_volumes->volumes.emplace_back(volume);
}
else
{
// an error occourred - restore to previous state and return
m_gcode_preview_volume_index.first_volumes.pop_back();
if (initial_volumes_count != m_volumes->volumes.size())
{
std::vector<GLVolume*>::iterator begin = m_volumes->volumes.begin() + initial_volumes_count;
std::vector<GLVolume*>::iterator end = m_volumes->volumes.end();
for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it)
{
GLVolume* volume = *it;
delete volume;
}
m_volumes->volumes.erase(begin, end);
return;
}
}
}
// populates volumes
for (const GCodePreviewData::Extrusion::Layer& layer : preview_data.extrusion.layers)
{
for (const ExtrusionPath& path : layer.paths)
{
float path_filter = Helper::path_filter(preview_data.extrusion.view_type, path);
FiltersList::iterator filter = std::find(filters.begin(), filters.end(), Filter(path_filter, path.role()));
if (filter != filters.end())
{
filter->volume->print_zs.push_back(layer.z);
filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.quad_indices.size());
filter->volume->offsets.push_back(filter->volume->indexed_vertex_array.triangle_indices.size());
_3DScene::extrusionentity_to_verts(path, layer.z, *filter->volume);
}
}
}
// finalize volumes and sends geometry to gpu
if (m_volumes->volumes.size() > initial_volumes_count)
{
for (size_t i = initial_volumes_count; i < m_volumes->volumes.size(); ++i)
{
GLVolume* volume = m_volumes->volumes[i];
volume->bounding_box = volume->indexed_vertex_array.bounding_box();
volume->indexed_vertex_array.finalize_geometry(m_use_VBOs);
}
}
}
void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
size_t initial_volumes_count = m_volumes->volumes.size();
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Travel, 0, (unsigned int)initial_volumes_count);
bool res = true;
switch (preview_data.extrusion.view_type)
{
case GCodePreviewData::Extrusion::Feedrate:
{
res = _travel_paths_by_feedrate(preview_data);
break;
}
case GCodePreviewData::Extrusion::Tool:
{
res = _travel_paths_by_tool(preview_data, tool_colors);
break;
}
default:
{
res = _travel_paths_by_type(preview_data);
break;
}
}
if (!res)
{
// an error occourred - restore to previous state and return
if (initial_volumes_count != m_volumes->volumes.size())
{
std::vector<GLVolume*>::iterator begin = m_volumes->volumes.begin() + initial_volumes_count;
std::vector<GLVolume*>::iterator end = m_volumes->volumes.end();
for (std::vector<GLVolume*>::iterator it = begin; it < end; ++it)
{
GLVolume* volume = *it;
delete volume;
}
m_volumes->volumes.erase(begin, end);
}
return;
}
// finalize volumes and sends geometry to gpu
if (m_volumes->volumes.size() > initial_volumes_count)
{
for (size_t i = initial_volumes_count; i < m_volumes->volumes.size(); ++i)
{
GLVolume* volume = m_volumes->volumes[i];
volume->bounding_box = volume->indexed_vertex_array.bounding_box();
volume->indexed_vertex_array.finalize_geometry(m_use_VBOs);
}
}
}
bool GLCanvas3D::_travel_paths_by_type(const GCodePreviewData& preview_data)
{
// Helper structure for types
struct Type
{
GCodePreviewData::Travel::EType value;
GLVolume* volume;
explicit Type(GCodePreviewData::Travel::EType value)
: value(value)
, volume(nullptr)
{
}
bool operator == (const Type& other) const
{
return value == other.value;
}
};
typedef std::vector<Type> TypesList;
// colors travels by travel type
// detects types
TypesList types;
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{
if (std::find(types.begin(), types.end(), Type(polyline.type)) == types.end())
types.emplace_back(polyline.type);
}
// nothing to render, return
if (types.empty())
return true;
// creates a new volume for each type
for (Type& type : types)
{
GLVolume* volume = new GLVolume(preview_data.travel.type_colors[type.value].rgba);
if (volume == nullptr)
return false;
else
{
type.volume = volume;
m_volumes->volumes.emplace_back(volume);
}
}
// populates volumes
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{
TypesList::iterator type = std::find(types.begin(), types.end(), Type(polyline.type));
if (type != types.end())
{
type->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
type->volume->offsets.push_back(type->volume->indexed_vertex_array.quad_indices.size());
type->volume->offsets.push_back(type->volume->indexed_vertex_array.triangle_indices.size());
_3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *type->volume);
}
}
return true;
}
bool GLCanvas3D::_travel_paths_by_feedrate(const GCodePreviewData& preview_data)
{
// Helper structure for feedrate
struct Feedrate
{
float value;
GLVolume* volume;
explicit Feedrate(float value)
: value(value)
, volume(nullptr)
{
}
bool operator == (const Feedrate& other) const
{
return value == other.value;
}
};
typedef std::vector<Feedrate> FeedratesList;
// colors travels by feedrate
// detects feedrates
FeedratesList feedrates;
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{
if (std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate)) == feedrates.end())
feedrates.emplace_back(polyline.feedrate);
}
// nothing to render, return
if (feedrates.empty())
return true;
// creates a new volume for each feedrate
for (Feedrate& feedrate : feedrates)
{
GLVolume* volume = new GLVolume(preview_data.get_feedrate_color(feedrate.value).rgba);
if (volume == nullptr)
return false;
else
{
feedrate.volume = volume;
m_volumes->volumes.emplace_back(volume);
}
}
// populates volumes
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{
FeedratesList::iterator feedrate = std::find(feedrates.begin(), feedrates.end(), Feedrate(polyline.feedrate));
if (feedrate != feedrates.end())
{
feedrate->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.quad_indices.size());
feedrate->volume->offsets.push_back(feedrate->volume->indexed_vertex_array.triangle_indices.size());
_3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *feedrate->volume);
}
}
return true;
}
bool GLCanvas3D::_travel_paths_by_tool(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors)
{
// Helper structure for tool
struct Tool
{
unsigned int value;
GLVolume* volume;
explicit Tool(unsigned int value)
: value(value)
, volume(nullptr)
{
}
bool operator == (const Tool& other) const
{
return value == other.value;
}
};
typedef std::vector<Tool> ToolsList;
// colors travels by tool
// detects tools
ToolsList tools;
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{
if (std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id)) == tools.end())
tools.emplace_back(polyline.extruder_id);
}
// nothing to render, return
if (tools.empty())
return true;
// creates a new volume for each tool
for (Tool& tool : tools)
{
GLVolume* volume = new GLVolume(tool_colors.data() + tool.value * 4);
if (volume == nullptr)
return false;
else
{
tool.volume = volume;
m_volumes->volumes.emplace_back(volume);
}
}
// populates volumes
for (const GCodePreviewData::Travel::Polyline& polyline : preview_data.travel.polylines)
{
ToolsList::iterator tool = std::find(tools.begin(), tools.end(), Tool(polyline.extruder_id));
if (tool != tools.end())
{
tool->volume->print_zs.push_back(unscale(polyline.polyline.bounding_box().min.z));
tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.quad_indices.size());
tool->volume->offsets.push_back(tool->volume->indexed_vertex_array.triangle_indices.size());
_3DScene::polyline3_to_verts(polyline.polyline, preview_data.travel.width, preview_data.travel.height, *tool->volume);
}
}
return true;
}
void GLCanvas3D::_load_gcode_retractions(const GCodePreviewData& preview_data)
{
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Retraction, 0, (unsigned int)m_volumes->volumes.size());
// nothing to render, return
if (preview_data.retraction.positions.empty())
return;
GLVolume* volume = new GLVolume(preview_data.retraction.color.rgba);
if (volume != nullptr)
{
m_volumes->volumes.emplace_back(volume);
GCodePreviewData::Retraction::PositionsList copy(preview_data.retraction.positions);
std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
for (const GCodePreviewData::Retraction::Position& position : copy)
{
volume->print_zs.push_back(unscale(position.position.z));
volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
}
// finalize volumes and sends geometry to gpu
volume->bounding_box = volume->indexed_vertex_array.bounding_box();
volume->indexed_vertex_array.finalize_geometry(m_use_VBOs);
}
}
void GLCanvas3D::_load_gcode_unretractions(const GCodePreviewData& preview_data)
{
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Unretraction, 0, (unsigned int)m_volumes->volumes.size());
// nothing to render, return
if (preview_data.unretraction.positions.empty())
return;
GLVolume* volume = new GLVolume(preview_data.unretraction.color.rgba);
if (volume != nullptr)
{
m_volumes->volumes.emplace_back(volume);
GCodePreviewData::Retraction::PositionsList copy(preview_data.unretraction.positions);
std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2){ return p1.position.z < p2.position.z; });
for (const GCodePreviewData::Retraction::Position& position : copy)
{
volume->print_zs.push_back(unscale(position.position.z));
volume->offsets.push_back(volume->indexed_vertex_array.quad_indices.size());
volume->offsets.push_back(volume->indexed_vertex_array.triangle_indices.size());
_3DScene::point3_to_verts(position.position, position.width, position.height, *volume);
}
// finalize volumes and sends geometry to gpu
volume->bounding_box = volume->indexed_vertex_array.bounding_box();
volume->indexed_vertex_array.finalize_geometry(m_use_VBOs);
}
}
void GLCanvas3D::_load_shells()
{
size_t initial_volumes_count = m_volumes->volumes.size();
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Shell, 0, (unsigned int)initial_volumes_count);
if (m_print->objects.empty())
// nothing to render, return
return;
// adds objects' volumes
unsigned int object_id = 0;
for (PrintObject* obj : m_print->objects)
{
ModelObject* model_obj = obj->model_object();
std::vector<int> instance_ids(model_obj->instances.size());
for (int i = 0; i < model_obj->instances.size(); ++i)
{
instance_ids[i] = i;
}
for (ModelInstance* instance : model_obj->instances)
{
m_volumes->load_object(model_obj, object_id, instance_ids, "object", "object", "object", m_use_VBOs);
}
++object_id;
}
// adds wipe tower's volume
coordf_t max_z = m_print->objects[0]->model_object()->get_model()->bounding_box().max.z;
const PrintConfig& config = m_print->config;
unsigned int extruders_count = config.nozzle_diameter.size();
if ((extruders_count > 1) && config.single_extruder_multi_material && config.wipe_tower && !config.complete_objects) {
const float width_per_extruder = 15.f; // a simple workaround after wipe_tower_per_color_wipe got obsolete
m_volumes->load_wipe_tower_preview(1000, config.wipe_tower_x, config.wipe_tower_y, config.wipe_tower_width, width_per_extruder * (extruders_count - 1), max_z, config.wipe_tower_rotation_angle, m_use_VBOs);
}
}
void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& preview_data)
{
unsigned int size = (unsigned int)m_gcode_preview_volume_index.first_volumes.size();
for (unsigned int i = 0; i < size; ++i)
{
std::vector<GLVolume*>::iterator begin = m_volumes->volumes.begin() + m_gcode_preview_volume_index.first_volumes[i].id;
std::vector<GLVolume*>::iterator end = (i + 1 < size) ? m_volumes->volumes.begin() + m_gcode_preview_volume_index.first_volumes[i + 1].id : m_volumes->volumes.end();
for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it)
{
GLVolume* volume = *it;
volume->outside_printer_detection_enabled = false;
switch (m_gcode_preview_volume_index.first_volumes[i].type)
{
case GCodePreviewVolumeIndex::Extrusion:
{
if ((ExtrusionRole)m_gcode_preview_volume_index.first_volumes[i].flag == erCustom)
volume->zoom_to_volumes = false;
volume->is_active = preview_data.extrusion.is_role_flag_set((ExtrusionRole)m_gcode_preview_volume_index.first_volumes[i].flag);
break;
}
case GCodePreviewVolumeIndex::Travel:
{
volume->is_active = preview_data.travel.is_visible;
volume->zoom_to_volumes = false;
break;
}
case GCodePreviewVolumeIndex::Retraction:
{
volume->is_active = preview_data.retraction.is_visible;
volume->zoom_to_volumes = false;
break;
}
case GCodePreviewVolumeIndex::Unretraction:
{
volume->is_active = preview_data.unretraction.is_visible;
volume->zoom_to_volumes = false;
break;
}
case GCodePreviewVolumeIndex::Shell:
{
volume->is_active = preview_data.shell.is_visible;
volume->color[3] = 0.25f;
volume->zoom_to_volumes = false;
break;
}
default:
{
volume->is_active = false;
volume->zoom_to_volumes = false;
break;
}
}
}
}
}
} // namespace GUI
} // namespace Slic3r