Out of print volume detection for toolpaths

This commit is contained in:
Enrico Turri 2018-07-24 13:39:17 +02:00
parent 21a59ce710
commit d4adcd4077
8 changed files with 473 additions and 452 deletions

View File

@ -25,6 +25,7 @@ sub new {
# init GUI elements
my $canvas = Slic3r::GUI::3DScene->new($self);
Slic3r::GUI::_3DScene::enable_shader($canvas, 1);
Slic3r::GUI::_3DScene::set_config($canvas, $config);
$self->canvas($canvas);
my $slider_low = Wx::Slider->new(
$self, -1,
@ -365,16 +366,8 @@ sub load_print {
if ($self->gcode_preview_data->empty) {
# load skirt and brim
Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print);
Slic3r::GUI::_3DScene::load_print_toolpaths($self->canvas);
Slic3r::GUI::_3DScene::load_wipe_tower_toolpaths($self->canvas, \@colors);
foreach my $object (@{$self->print->objects}) {
Slic3r::GUI::_3DScene::load_print_object_toolpaths($self->canvas, $object, \@colors);
# Show the objects in very transparent color.
#my @volume_ids = $self->canvas->load_object($object->model_object);
#$self->canvas->volumes->[$_]->color->[3] = 0.2 for @volume_ids;
}
Slic3r::GUI::_3DScene::load_preview($self->canvas, \@colors);
$self->show_hide_ui_elements('simple');
Slic3r::GUI::_3DScene::reset_legend_texture();
} else {
$self->{force_sliders_full_range} = (Slic3r::GUI::_3DScene::get_volumes_count($self->canvas) == 0);
Slic3r::GUI::_3DScene::set_print($self->canvas, $self->print);

View File

@ -250,7 +250,7 @@ void GLVolume::set_render_color()
set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4);
else if (hover)
set_render_color(HOVER_COLOR, 4);
else if (is_outside)
else if (is_outside && outside_printer_detection_enabled)
set_render_color(OUTSIDE_COLOR, 4);
else
set_render_color(color, 4);
@ -1968,26 +1968,16 @@ void _3DScene::reload_scene(wxGLCanvas* canvas, bool force)
s_canvas_mgr.reload_scene(canvas, force);
}
void _3DScene::load_print_toolpaths(wxGLCanvas* canvas)
{
s_canvas_mgr.load_print_toolpaths(canvas);
}
void _3DScene::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& str_tool_colors)
{
s_canvas_mgr.load_print_object_toolpaths(canvas, print_object, str_tool_colors);
}
void _3DScene::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
{
s_canvas_mgr.load_wipe_tower_toolpaths(canvas, str_tool_colors);
}
void _3DScene::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
{
s_canvas_mgr.load_gcode_preview(canvas, preview_data, str_tool_colors);
}
void _3DScene::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
{
s_canvas_mgr.load_preview(canvas, str_tool_colors);
}
void _3DScene::reset_legend_texture()
{
s_canvas_mgr.reset_legend_texture();

View File

@ -536,10 +536,8 @@ public:
static void reload_scene(wxGLCanvas* canvas, bool force);
static void load_print_toolpaths(wxGLCanvas* canvas);
static void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& str_tool_colors);
static void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
static void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
static void load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
static void reset_legend_texture();

View File

@ -2326,372 +2326,6 @@ void GLCanvas3D::reload_scene(bool force)
}
}
void GLCanvas3D::load_print_toolpaths()
{
// ensures this canvas is current
if (!set_current())
return;
if (m_print == nullptr)
return;
if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim))
return;
if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0))
return;
const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish
// number of skirt layers
size_t total_layer_count = 0;
for (const PrintObject* print_object : m_print->objects)
{
total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
}
size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config.skirt_height.value, total_layer_count);
if ((skirt_height == 0) && (m_print->config.brim_width.value > 0))
skirt_height = 1;
// get first skirt_height layers (maybe this should be moved to a PrintObject method?)
const PrintObject* object0 = m_print->objects.front();
std::vector<float> print_zs;
print_zs.reserve(skirt_height * 2);
for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i)
{
print_zs.push_back(float(object0->layers[i]->print_z));
}
//FIXME why there are support layers?
for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i)
{
print_zs.push_back(float(object0->support_layers[i]->print_z));
}
sort_remove_duplicates(print_zs);
if (print_zs.size() > skirt_height)
print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
m_volumes.volumes.emplace_back(new GLVolume(color));
GLVolume& volume = *m_volumes.volumes.back();
for (size_t i = 0; i < skirt_height; ++i) {
volume.print_zs.push_back(print_zs[i]);
volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
if (i == 0)
_3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume);
_3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume);
}
volume.bounding_box = volume.indexed_vertex_array.bounding_box();
volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
}
void GLCanvas3D::load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors)
{
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
struct Ctxt
{
const Points *shifted_copies;
std::vector<const Layer*> layers;
bool has_perimeters;
bool has_infill;
bool has_support;
const std::vector<float>* tool_colors;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max() { return 131072; } // 3.15MB
// static const size_t alloc_size_max () { return 65536; } // 1.57MB
// static const size_t alloc_size_max () { return 32768; } // 786kB
static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
// For cloring by a tool, return a parsed color.
bool color_by_tool() const { return tool_colors != nullptr; }
size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
int volume_idx(int extruder, int feature) const
{
return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature;
}
} ctxt;
ctxt.shifted_copies = &print_object._shifted_copies;
// order layers by print_z
ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size());
for (const Layer *layer : print_object.layers)
ctxt.layers.push_back(layer);
for (const Layer *layer : print_object.support_layers)
ctxt.layers.push_back(layer);
std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
// Maximum size of an allocation block: 32MB / sizeof(float)
ctxt.has_perimeters = print_object.state.is_done(posPerimeters);
ctxt.has_infill = print_object.state.is_done(posInfill);
ctxt.has_support = print_object.state.is_done(posSupportMaterial);
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
//FIXME Improve the heuristics for a grain size.
size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
tbb::spin_mutex new_volume_mutex;
auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
auto *volume = new GLVolume(color);
new_volume_mutex.lock();
volume->outside_printer_detection_enabled = false;
m_volumes.volumes.emplace_back(volume);
new_volume_mutex.unlock();
return volume;
};
const size_t volumes_cnt_initial = m_volumes.volumes.size();
std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
[&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
std::vector<GLVolume*> vols;
if (ctxt.color_by_tool()) {
for (size_t i = 0; i < ctxt.number_tools(); ++i)
vols.emplace_back(new_volume(ctxt.color_tool(i)));
}
else
vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
for (GLVolume *vol : vols)
vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
const Layer *layer = ctxt.layers[idx_layer];
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) {
vol.print_zs.push_back(layer->print_z);
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
}
}
for (const Point &copy : *ctxt.shifted_copies) {
for (const LayerRegion *layerm : layer->regions) {
if (ctxt.has_perimeters)
_3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
*vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
if (ctxt.has_infill) {
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!fill->entities.empty())
_3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
*vols[ctxt.volume_idx(
is_solid_infill(fill->entities.front()->role()) ?
layerm->region()->config.solid_infill_extruder :
layerm->region()->config.infill_extruder,
1)]);
}
}
}
if (ctxt.has_support) {
const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
if (support_layer) {
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
_3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
*vols[ctxt.volume_idx(
(extrusion_entity->role() == erSupportMaterial) ?
support_layer->object()->config.support_material_extruder :
support_layer->object()->config.support_material_interface_extruder,
2)]);
}
}
}
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
// Store the vertex arrays and restart their containers,
vols[i] = new_volume(vol.color);
GLVolume &vol_new = *vols[i];
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
// Copy the content back to the old GLVolume.
vol.indexed_vertex_array = vol_new.indexed_vertex_array;
// Finalize a bounding box of the old GLVolume.
vol.bounding_box = vol.indexed_vertex_array.bounding_box();
// Clear the buffers, but keep them pre-allocated.
vol_new.indexed_vertex_array.clear();
// Just make sure that clear did not clear the reserved memory.
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
}
}
}
for (GLVolume *vol : vols) {
vol->bounding_box = vol->indexed_vertex_array.bounding_box();
vol->indexed_vertex_array.shrink_to_fit();
}
});
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
// Remove empty volumes from the newly added volumes.
m_volumes.volumes.erase(
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
}
void GLCanvas3D::load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
{
if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty())
return;
if (!m_print->state.is_done(psWipeTower))
return;
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
struct Ctxt
{
const Print *print;
const std::vector<float> *tool_colors;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max() { return 131072; } // 3.15MB
static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
// For cloring by a tool, return a parsed color.
bool color_by_tool() const { return tool_colors != nullptr; }
size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
int volume_idx(int tool, int feature) const
{
return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature;
}
const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
return priming.empty() ?
((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
}
std::vector<WipeTower::ToolChangeResult> priming;
std::vector<WipeTower::ToolChangeResult> final;
} ctxt;
ctxt.print = m_print;
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
if (m_print->m_wipe_tower_priming)
ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
if (m_print->m_wipe_tower_final_purge)
ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
//FIXME Improve the heuristics for a grain size.
size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
size_t grain_size = std::max(n_items / 128, size_t(1));
tbb::spin_mutex new_volume_mutex;
auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
auto *volume = new GLVolume(color);
new_volume_mutex.lock();
volume->outside_printer_detection_enabled = false;
m_volumes.volumes.emplace_back(volume);
new_volume_mutex.unlock();
return volume;
};
const size_t volumes_cnt_initial = m_volumes.volumes.size();
std::vector<GLVolumeCollection> volumes_per_thread(n_items);
tbb::parallel_for(
tbb::blocked_range<size_t>(0, n_items, grain_size),
[&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
// Bounding box of this slab of a wipe tower.
std::vector<GLVolume*> vols;
if (ctxt.color_by_tool()) {
for (size_t i = 0; i < ctxt.number_tools(); ++i)
vols.emplace_back(new_volume(ctxt.color_tool(i)));
}
else
vols = { new_volume(ctxt.color_support()) };
for (GLVolume *volume : vols)
volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
vol.print_zs.push_back(layer.front().print_z);
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
}
}
for (const WipeTower::ToolChangeResult &extrusions : layer) {
for (size_t i = 1; i < extrusions.extrusions.size();) {
const WipeTower::Extrusion &e = extrusions.extrusions[i];
if (e.width == 0.) {
++i;
continue;
}
size_t j = i + 1;
if (ctxt.color_by_tool())
for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j);
else
for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j);
size_t n_lines = j - i;
Lines lines;
std::vector<double> widths;
std::vector<double> heights;
lines.reserve(n_lines);
widths.reserve(n_lines);
heights.assign(n_lines, extrusions.layer_height);
for (; i < j; ++i) {
const WipeTower::Extrusion &e = extrusions.extrusions[i];
assert(e.width > 0.f);
const WipeTower::Extrusion &e_prev = *(&e - 1);
lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
widths.emplace_back(e.width);
}
_3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
*vols[ctxt.volume_idx(e.tool, 0)]);
}
}
}
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
// Store the vertex arrays and restart their containers,
vols[i] = new_volume(vol.color);
GLVolume &vol_new = *vols[i];
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
// Copy the content back to the old GLVolume.
vol.indexed_vertex_array = vol_new.indexed_vertex_array;
// Finalize a bounding box of the old GLVolume.
vol.bounding_box = vol.indexed_vertex_array.bounding_box();
// Clear the buffers, but keep them pre-allocated.
vol_new.indexed_vertex_array.clear();
// Just make sure that clear did not clear the reserved memory.
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
}
}
for (GLVolume *vol : vols) {
vol->bounding_box = vol->indexed_vertex_array.bounding_box();
vol->indexed_vertex_array.shrink_to_fit();
}
});
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
// Remove empty volumes from the newly added volumes.
m_volumes.volumes.erase(
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
}
void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors)
{
if ((m_canvas != nullptr) && (m_print != nullptr))
@ -2723,12 +2357,31 @@ void GLCanvas3D::load_gcode_preview(const GCodePreviewData& preview_data, const
_load_shells();
}
_update_toolpath_volumes_outside_state();
}
_update_gcode_volumes_visibility(preview_data);
_show_warning_texture_if_needed();
}
}
void GLCanvas3D::load_preview(const std::vector<std::string>& str_tool_colors)
{
if (m_print == nullptr)
return;
_load_print_toolpaths();
_load_wipe_tower_toolpaths(str_tool_colors);
for (const PrintObject* object : m_print->objects)
{
if (object != nullptr)
_load_print_object_toolpaths(*object, str_tool_colors);
}
_update_toolpath_volumes_outside_state();
_show_warning_texture_if_needed();
reset_legend_texture();
}
void GLCanvas3D::register_on_viewport_changed_callback(void* callback)
{
if (callback != nullptr)
@ -4112,6 +3765,372 @@ int GLCanvas3D::_get_first_selected_volume_id() const
return -1;
}
void GLCanvas3D::_load_print_toolpaths()
{
// ensures this canvas is current
if (!set_current())
return;
if (m_print == nullptr)
return;
if (!m_print->state.is_done(psSkirt) || !m_print->state.is_done(psBrim))
return;
if (!m_print->has_skirt() && (m_print->config.brim_width.value == 0))
return;
const float color[] = { 0.5f, 1.0f, 0.5f, 1.0f }; // greenish
// number of skirt layers
size_t total_layer_count = 0;
for (const PrintObject* print_object : m_print->objects)
{
total_layer_count = std::max(total_layer_count, print_object->total_layer_count());
}
size_t skirt_height = m_print->has_infinite_skirt() ? total_layer_count : std::min<size_t>(m_print->config.skirt_height.value, total_layer_count);
if ((skirt_height == 0) && (m_print->config.brim_width.value > 0))
skirt_height = 1;
// get first skirt_height layers (maybe this should be moved to a PrintObject method?)
const PrintObject* object0 = m_print->objects.front();
std::vector<float> print_zs;
print_zs.reserve(skirt_height * 2);
for (size_t i = 0; i < std::min(skirt_height, object0->layers.size()); ++i)
{
print_zs.push_back(float(object0->layers[i]->print_z));
}
//FIXME why there are support layers?
for (size_t i = 0; i < std::min(skirt_height, object0->support_layers.size()); ++i)
{
print_zs.push_back(float(object0->support_layers[i]->print_z));
}
sort_remove_duplicates(print_zs);
if (print_zs.size() > skirt_height)
print_zs.erase(print_zs.begin() + skirt_height, print_zs.end());
m_volumes.volumes.emplace_back(new GLVolume(color));
GLVolume& volume = *m_volumes.volumes.back();
for (size_t i = 0; i < skirt_height; ++i) {
volume.print_zs.push_back(print_zs[i]);
volume.offsets.push_back(volume.indexed_vertex_array.quad_indices.size());
volume.offsets.push_back(volume.indexed_vertex_array.triangle_indices.size());
if (i == 0)
_3DScene::extrusionentity_to_verts(m_print->brim, print_zs[i], Point(0, 0), volume);
_3DScene::extrusionentity_to_verts(m_print->skirt, print_zs[i], Point(0, 0), volume);
}
volume.bounding_box = volume.indexed_vertex_array.bounding_box();
volume.indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
}
void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors)
{
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
struct Ctxt
{
const Points *shifted_copies;
std::vector<const Layer*> layers;
bool has_perimeters;
bool has_infill;
bool has_support;
const std::vector<float>* tool_colors;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max() { return 131072; } // 3.15MB
// static const size_t alloc_size_max () { return 65536; } // 1.57MB
// static const size_t alloc_size_max () { return 32768; } // 786kB
static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
static const float* color_perimeters() { static float color[4] = { 1.0f, 1.0f, 0.0f, 1.f }; return color; } // yellow
static const float* color_infill() { static float color[4] = { 1.0f, 0.5f, 0.5f, 1.f }; return color; } // redish
static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
// For cloring by a tool, return a parsed color.
bool color_by_tool() const { return tool_colors != nullptr; }
size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
int volume_idx(int extruder, int feature) const
{
return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(extruder - 1, 0)) : feature;
}
} ctxt;
ctxt.shifted_copies = &print_object._shifted_copies;
// order layers by print_z
ctxt.layers.reserve(print_object.layers.size() + print_object.support_layers.size());
for (const Layer *layer : print_object.layers)
ctxt.layers.push_back(layer);
for (const Layer *layer : print_object.support_layers)
ctxt.layers.push_back(layer);
std::sort(ctxt.layers.begin(), ctxt.layers.end(), [](const Layer *l1, const Layer *l2) { return l1->print_z < l2->print_z; });
// Maximum size of an allocation block: 32MB / sizeof(float)
ctxt.has_perimeters = print_object.state.is_done(posPerimeters);
ctxt.has_infill = print_object.state.is_done(posInfill);
ctxt.has_support = print_object.state.is_done(posSupportMaterial);
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - start";
//FIXME Improve the heuristics for a grain size.
size_t grain_size = std::max(ctxt.layers.size() / 16, size_t(1));
tbb::spin_mutex new_volume_mutex;
auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
auto *volume = new GLVolume(color);
new_volume_mutex.lock();
volume->outside_printer_detection_enabled = false;
m_volumes.volumes.emplace_back(volume);
new_volume_mutex.unlock();
return volume;
};
const size_t volumes_cnt_initial = m_volumes.volumes.size();
std::vector<GLVolumeCollection> volumes_per_thread(ctxt.layers.size());
tbb::parallel_for(
tbb::blocked_range<size_t>(0, ctxt.layers.size(), grain_size),
[&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
std::vector<GLVolume*> vols;
if (ctxt.color_by_tool()) {
for (size_t i = 0; i < ctxt.number_tools(); ++i)
vols.emplace_back(new_volume(ctxt.color_tool(i)));
}
else
vols = { new_volume(ctxt.color_perimeters()), new_volume(ctxt.color_infill()), new_volume(ctxt.color_support()) };
for (GLVolume *vol : vols)
vol->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
const Layer *layer = ctxt.layers[idx_layer];
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.print_zs.empty() || vol.print_zs.back() != layer->print_z) {
vol.print_zs.push_back(layer->print_z);
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
}
}
for (const Point &copy : *ctxt.shifted_copies) {
for (const LayerRegion *layerm : layer->regions) {
if (ctxt.has_perimeters)
_3DScene::extrusionentity_to_verts(layerm->perimeters, float(layer->print_z), copy,
*vols[ctxt.volume_idx(layerm->region()->config.perimeter_extruder.value, 0)]);
if (ctxt.has_infill) {
for (const ExtrusionEntity *ee : layerm->fills.entities) {
// fill represents infill extrusions of a single island.
const auto *fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
if (!fill->entities.empty())
_3DScene::extrusionentity_to_verts(*fill, float(layer->print_z), copy,
*vols[ctxt.volume_idx(
is_solid_infill(fill->entities.front()->role()) ?
layerm->region()->config.solid_infill_extruder :
layerm->region()->config.infill_extruder,
1)]);
}
}
}
if (ctxt.has_support) {
const SupportLayer *support_layer = dynamic_cast<const SupportLayer*>(layer);
if (support_layer) {
for (const ExtrusionEntity *extrusion_entity : support_layer->support_fills.entities)
_3DScene::extrusionentity_to_verts(extrusion_entity, float(layer->print_z), copy,
*vols[ctxt.volume_idx(
(extrusion_entity->role() == erSupportMaterial) ?
support_layer->object()->config.support_material_extruder :
support_layer->object()->config.support_material_interface_extruder,
2)]);
}
}
}
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
// Store the vertex arrays and restart their containers,
vols[i] = new_volume(vol.color);
GLVolume &vol_new = *vols[i];
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
// Copy the content back to the old GLVolume.
vol.indexed_vertex_array = vol_new.indexed_vertex_array;
// Finalize a bounding box of the old GLVolume.
vol.bounding_box = vol.indexed_vertex_array.bounding_box();
// Clear the buffers, but keep them pre-allocated.
vol_new.indexed_vertex_array.clear();
// Just make sure that clear did not clear the reserved memory.
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
}
}
}
for (GLVolume *vol : vols) {
vol->bounding_box = vol->indexed_vertex_array.bounding_box();
vol->indexed_vertex_array.shrink_to_fit();
}
});
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - finalizing results";
// Remove empty volumes from the newly added volumes.
m_volumes.volumes.erase(
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading print object toolpaths in parallel - end";
}
void GLCanvas3D::_load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors)
{
if ((m_print == nullptr) || m_print->m_wipe_tower_tool_changes.empty())
return;
if (!m_print->state.is_done(psWipeTower))
return;
std::vector<float> tool_colors = _parse_colors(str_tool_colors);
struct Ctxt
{
const Print *print;
const std::vector<float> *tool_colors;
// Number of vertices (each vertex is 6x4=24 bytes long)
static const size_t alloc_size_max() { return 131072; } // 3.15MB
static const size_t alloc_size_reserve() { return alloc_size_max() * 2; }
static const float* color_support() { static float color[4] = { 0.5f, 1.0f, 0.5f, 1.f }; return color; } // greenish
// For cloring by a tool, return a parsed color.
bool color_by_tool() const { return tool_colors != nullptr; }
size_t number_tools() const { return this->color_by_tool() ? tool_colors->size() / 4 : 0; }
const float* color_tool(size_t tool) const { return tool_colors->data() + tool * 4; }
int volume_idx(int tool, int feature) const
{
return this->color_by_tool() ? std::min<int>(this->number_tools() - 1, std::max<int>(tool, 0)) : feature;
}
const std::vector<WipeTower::ToolChangeResult>& tool_change(size_t idx) {
return priming.empty() ?
((idx == print->m_wipe_tower_tool_changes.size()) ? final : print->m_wipe_tower_tool_changes[idx]) :
((idx == 0) ? priming : (idx == print->m_wipe_tower_tool_changes.size() + 1) ? final : print->m_wipe_tower_tool_changes[idx - 1]);
}
std::vector<WipeTower::ToolChangeResult> priming;
std::vector<WipeTower::ToolChangeResult> final;
} ctxt;
ctxt.print = m_print;
ctxt.tool_colors = tool_colors.empty() ? nullptr : &tool_colors;
if (m_print->m_wipe_tower_priming)
ctxt.priming.emplace_back(*m_print->m_wipe_tower_priming.get());
if (m_print->m_wipe_tower_final_purge)
ctxt.final.emplace_back(*m_print->m_wipe_tower_final_purge.get());
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - start";
//FIXME Improve the heuristics for a grain size.
size_t n_items = m_print->m_wipe_tower_tool_changes.size() + (ctxt.priming.empty() ? 0 : 1);
size_t grain_size = std::max(n_items / 128, size_t(1));
tbb::spin_mutex new_volume_mutex;
auto new_volume = [this, &new_volume_mutex](const float *color) -> GLVolume* {
auto *volume = new GLVolume(color);
new_volume_mutex.lock();
volume->outside_printer_detection_enabled = false;
m_volumes.volumes.emplace_back(volume);
new_volume_mutex.unlock();
return volume;
};
const size_t volumes_cnt_initial = m_volumes.volumes.size();
std::vector<GLVolumeCollection> volumes_per_thread(n_items);
tbb::parallel_for(
tbb::blocked_range<size_t>(0, n_items, grain_size),
[&ctxt, &new_volume](const tbb::blocked_range<size_t>& range) {
// Bounding box of this slab of a wipe tower.
std::vector<GLVolume*> vols;
if (ctxt.color_by_tool()) {
for (size_t i = 0; i < ctxt.number_tools(); ++i)
vols.emplace_back(new_volume(ctxt.color_tool(i)));
}
else
vols = { new_volume(ctxt.color_support()) };
for (GLVolume *volume : vols)
volume->indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
for (size_t idx_layer = range.begin(); idx_layer < range.end(); ++idx_layer) {
const std::vector<WipeTower::ToolChangeResult> &layer = ctxt.tool_change(idx_layer);
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.print_zs.empty() || vol.print_zs.back() != layer.front().print_z) {
vol.print_zs.push_back(layer.front().print_z);
vol.offsets.push_back(vol.indexed_vertex_array.quad_indices.size());
vol.offsets.push_back(vol.indexed_vertex_array.triangle_indices.size());
}
}
for (const WipeTower::ToolChangeResult &extrusions : layer) {
for (size_t i = 1; i < extrusions.extrusions.size();) {
const WipeTower::Extrusion &e = extrusions.extrusions[i];
if (e.width == 0.) {
++i;
continue;
}
size_t j = i + 1;
if (ctxt.color_by_tool())
for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].tool == e.tool && extrusions.extrusions[j].width > 0.f; ++j);
else
for (; j < extrusions.extrusions.size() && extrusions.extrusions[j].width > 0.f; ++j);
size_t n_lines = j - i;
Lines lines;
std::vector<double> widths;
std::vector<double> heights;
lines.reserve(n_lines);
widths.reserve(n_lines);
heights.assign(n_lines, extrusions.layer_height);
for (; i < j; ++i) {
const WipeTower::Extrusion &e = extrusions.extrusions[i];
assert(e.width > 0.f);
const WipeTower::Extrusion &e_prev = *(&e - 1);
lines.emplace_back(Point::new_scale(e_prev.pos.x, e_prev.pos.y), Point::new_scale(e.pos.x, e.pos.y));
widths.emplace_back(e.width);
}
_3DScene::thick_lines_to_verts(lines, widths, heights, lines.front().a == lines.back().b, extrusions.print_z,
*vols[ctxt.volume_idx(e.tool, 0)]);
}
}
}
for (size_t i = 0; i < vols.size(); ++i) {
GLVolume &vol = *vols[i];
if (vol.indexed_vertex_array.vertices_and_normals_interleaved.size() / 6 > ctxt.alloc_size_max()) {
// Store the vertex arrays and restart their containers,
vols[i] = new_volume(vol.color);
GLVolume &vol_new = *vols[i];
// Assign the large pre-allocated buffers to the new GLVolume.
vol_new.indexed_vertex_array = std::move(vol.indexed_vertex_array);
// Copy the content back to the old GLVolume.
vol.indexed_vertex_array = vol_new.indexed_vertex_array;
// Finalize a bounding box of the old GLVolume.
vol.bounding_box = vol.indexed_vertex_array.bounding_box();
// Clear the buffers, but keep them pre-allocated.
vol_new.indexed_vertex_array.clear();
// Just make sure that clear did not clear the reserved memory.
vol_new.indexed_vertex_array.reserve(ctxt.alloc_size_reserve());
}
}
for (GLVolume *vol : vols) {
vol->bounding_box = vol->indexed_vertex_array.bounding_box();
vol->indexed_vertex_array.shrink_to_fit();
}
});
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - finalizing results";
// Remove empty volumes from the newly added volumes.
m_volumes.volumes.erase(
std::remove_if(m_volumes.volumes.begin() + volumes_cnt_initial, m_volumes.volumes.end(),
[](const GLVolume *volume) { return volume->empty(); }),
m_volumes.volumes.end());
for (size_t i = volumes_cnt_initial; i < m_volumes.volumes.size(); ++i)
m_volumes.volumes[i]->indexed_vertex_array.finalize_geometry(m_use_VBOs && m_initialized);
BOOST_LOG_TRIVIAL(debug) << "Loading wipe tower toolpaths in parallel - end";
}
static inline int hex_digit_to_int(const char c)
{
return
@ -4643,6 +4662,7 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe
for (std::vector<GLVolume*>::iterator it = begin; it != end; ++it)
{
GLVolume* volume = *it;
// to avoid the shader to change the color of this volume if outside the print volume
volume->outside_printer_detection_enabled = false;
switch (m_gcode_preview_volume_index.first_volumes[i].type)
@ -4691,6 +4711,55 @@ void GLCanvas3D::_update_gcode_volumes_visibility(const GCodePreviewData& previe
}
}
void GLCanvas3D::_update_toolpath_volumes_outside_state()
{
// tolerance to avoid false detection at bed edges
static const coordf_t tolerance_x = 0.05;
static const coordf_t tolerance_y = 0.05;
BoundingBoxf3 print_volume;
if (m_config != nullptr)
{
const ConfigOptionPoints* opt = dynamic_cast<const ConfigOptionPoints*>(m_config->option("bed_shape"));
if (opt != nullptr)
{
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(opt->values));
print_volume = BoundingBoxf3(Pointf3(unscale(bed_box_2D.min.x) - tolerance_x, unscale(bed_box_2D.min.y) - tolerance_y, 0.0), Pointf3(unscale(bed_box_2D.max.x) + tolerance_x, unscale(bed_box_2D.max.y) + tolerance_y, m_config->opt_float("max_print_height")));
// Allow the objects to protrude below the print bed
print_volume.min.z = -1e10;
}
}
for (GLVolume* volume : m_volumes.volumes)
{
volume->is_outside = (print_volume.radius() > 0.0) ? !print_volume.contains(volume->transformed_bounding_box()) : false;
}
}
void GLCanvas3D::_show_warning_texture_if_needed()
{
bool detected_outside = false;
for (const GLVolume* volume : m_volumes.volumes)
{
if ((volume != nullptr) && volume->is_outside)
{
detected_outside = true;
break;
}
}
if (detected_outside)
{
enable_warning_texture(true);
_generate_warning_texture(L("Detected toolpath outside print volume"));
}
else
{
enable_warning_texture(false);
_reset_warning_texture();
}
}
void GLCanvas3D::_on_move(const std::vector<int>& volume_idxs)
{
if (m_model == nullptr)

View File

@ -559,16 +559,8 @@ public:
void reload_scene(bool force);
// Create 3D thick extrusion lines for a skirt and brim.
// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
void load_print_toolpaths();
// Create 3D thick extrusion lines for object forming extrusions.
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
// one for perimeters, one for infill and one for supports.
void load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors);
// Create 3D thick extrusion lines for wipe tower extrusions
void load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
void load_gcode_preview(const GCodePreviewData& preview_data, const std::vector<std::string>& str_tool_colors);
void load_preview(const std::vector<std::string>& str_tool_colors);
void register_on_viewport_changed_callback(void* callback);
void register_on_double_click_callback(void* callback);
@ -652,6 +644,16 @@ private:
int _get_first_selected_object_id() const;
int _get_first_selected_volume_id() const;
// Create 3D thick extrusion lines for a skirt and brim.
// Adds a new Slic3r::GUI::3DScene::Volume to volumes.
void _load_print_toolpaths();
// Create 3D thick extrusion lines for object forming extrusions.
// Adds a new Slic3r::GUI::3DScene::Volume to $self->volumes,
// one for perimeters, one for infill and one for supports.
void _load_print_object_toolpaths(const PrintObject& print_object, const std::vector<std::string>& str_tool_colors);
// Create 3D thick extrusion lines for wipe tower extrusions
void _load_wipe_tower_toolpaths(const std::vector<std::string>& str_tool_colors);
// generates gcode extrusion paths geometry
void _load_gcode_extrusion_paths(const GCodePreviewData& preview_data, const std::vector<float>& tool_colors);
// generates gcode travel paths geometry
@ -667,6 +669,8 @@ private:
void _load_shells();
// sets gcode geometry visibility according to user selection
void _update_gcode_volumes_visibility(const GCodePreviewData& preview_data);
void _update_toolpath_volumes_outside_state();
void _show_warning_texture_if_needed();
void _on_move(const std::vector<int>& volume_idxs);
void _on_select(int volume_idx);

View File

@ -516,30 +516,6 @@ void GLCanvas3DManager::reload_scene(wxGLCanvas* canvas, bool force)
it->second->reload_scene(force);
}
void GLCanvas3DManager::load_print_toolpaths(wxGLCanvas* canvas)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->load_print_toolpaths();
}
void GLCanvas3DManager::load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& tool_colors)
{
if (print_object == nullptr)
return;
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->load_print_object_toolpaths(*print_object, tool_colors);
}
void GLCanvas3DManager::load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->load_wipe_tower_toolpaths(str_tool_colors);
}
void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors)
{
if (preview_data == nullptr)
@ -550,6 +526,13 @@ void GLCanvas3DManager::load_gcode_preview(wxGLCanvas* canvas, const GCodePrevie
it->second->load_gcode_preview(*preview_data, str_tool_colors);
}
void GLCanvas3DManager::load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors)
{
CanvasesMap::iterator it = _get_canvas(canvas);
if (it != m_canvases.end())
it->second->load_preview(str_tool_colors);
}
void GLCanvas3DManager::reset_legend_texture()
{
for (CanvasesMap::value_type& canvas : m_canvases)

View File

@ -132,10 +132,8 @@ public:
void reload_scene(wxGLCanvas* canvas, bool force);
void load_print_toolpaths(wxGLCanvas* canvas);
void load_print_object_toolpaths(wxGLCanvas* canvas, const PrintObject* print_object, const std::vector<std::string>& tool_colors);
void load_wipe_tower_toolpaths(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
void load_gcode_preview(wxGLCanvas* canvas, const GCodePreviewData* preview_data, const std::vector<std::string>& str_tool_colors);
void load_preview(wxGLCanvas* canvas, const std::vector<std::string>& str_tool_colors);
void reset_legend_texture();

View File

@ -657,27 +657,6 @@ reload_scene(canvas, force)
CODE:
_3DScene::reload_scene((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), force);
void
load_print_toolpaths(canvas)
SV *canvas;
CODE:
_3DScene::load_print_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"));
void
load_print_object_toolpaths(canvas, print_object, tool_colors)
SV *canvas;
PrintObject *print_object;
std::vector<std::string> tool_colors;
CODE:
_3DScene::load_print_object_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), print_object, tool_colors);
void
load_wipe_tower_toolpaths(canvas, tool_colors)
SV *canvas;
std::vector<std::string> tool_colors;
CODE:
_3DScene::load_wipe_tower_toolpaths((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), tool_colors);
void
load_gcode_preview(canvas, preview_data, str_tool_colors)
SV *canvas;
@ -685,5 +664,12 @@ load_gcode_preview(canvas, preview_data, str_tool_colors)
std::vector<std::string> str_tool_colors;
CODE:
_3DScene::load_gcode_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), preview_data, str_tool_colors);
void
load_preview(canvas, str_tool_colors)
SV *canvas;
std::vector<std::string> str_tool_colors;
CODE:
_3DScene::load_preview((wxGLCanvas*)wxPli_sv_2_object(aTHX_ canvas, "Wx::GLCanvas"), str_tool_colors);
%}