mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-24 06:24:27 +08:00
Autoslicing and aggregated statistics
- Call print apply on all print within update_background_process - Render multiple bed thumnail only when there is a reason to (by requesting extra frame) - Show status of the slicing process for each bed - Add aggregated statistics
This commit is contained in:
parent
3fb8b71627
commit
dbb12e9537
@ -210,6 +210,9 @@ namespace ImGui
|
||||
|
||||
// icon for multiple beds
|
||||
const wchar_t SliceAllBtnIcon = 0x2811;
|
||||
const wchar_t PrintIdle = 0x2812;
|
||||
const wchar_t PrintRunning = 0x2813;
|
||||
const wchar_t PrintFinished = 0x2814;
|
||||
// void MyFunction(const char* name, const MyMatrix44& v);
|
||||
}
|
||||
|
||||
|
4
resources/icons/print_finished.svg
Normal file
4
resources/icons/print_finished.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M3 8.9375L6.45062 11.6772C6.67263 11.8535 6.99663 11.8101 7.16447 11.5817L12 5" stroke="white" stroke-linecap="round"/>
|
||||
<path d="M3 8.9375L6.45062 11.6772C6.67263 11.8535 6.99663 11.8101 7.16447 11.5817L12 5" stroke="white" stroke-opacity="0.2" stroke-linecap="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 382 B |
91
resources/icons/print_idle.svg
Normal file
91
resources/icons/print_idle.svg
Normal file
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 18.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 348.882 348.882"
|
||||
style="enable-background:new 0 0 348.882 348.882;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="edit_button - Copy.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs209">
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
id="namedview207"
|
||||
pagecolor="#505050"
|
||||
bordercolor="#eeeeee"
|
||||
borderopacity="1"
|
||||
inkscape:pageshadow="0"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="2.3274346"
|
||||
inkscape:cx="89.583613"
|
||||
inkscape:cy="139.85355"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1001"
|
||||
inkscape:window-x="3191"
|
||||
inkscape:window-y="-9"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<path
|
||||
d="m 333.988,11.758 -0.42,-0.383 C 325.538,4.04 315.129,0 304.258,0 292.071,0 280.37,5.159 272.154,14.153 L 116.803,184.231 c -1.416,1.55 -2.49,3.379 -3.154,5.37 l -18.267,54.762 c -2.112,6.331 -1.052,13.333 2.835,18.729 3.918,5.438 10.23,8.685 16.886,8.685 0,0 0.001,0 0.001,0 2.879,0 5.693,-0.592 8.362,-1.76 l 52.89,-23.138 c 1.923,-0.841 3.648,-2.076 5.063,-3.626 L 336.771,73.176 C 352.937,55.479 351.69,27.929 333.988,11.758 Z m -203.607,222.489 10.719,-32.134 0.904,-0.99 20.316,18.556 -0.904,0.99 z M 314.621,52.943 182.553,197.53 162.237,178.974 294.305,34.386 c 2.583,-2.828 6.118,-4.386 9.954,-4.386 3.365,0 6.588,1.252 9.082,3.53 l 0.419,0.383 c 5.484,5.009 5.87,13.546 0.861,19.03 z"
|
||||
id="path170"
|
||||
style="fill:#ed6b21;fill-opacity:1" /><path
|
||||
d="m 303.85,138.388 c -8.284,0 -15,6.716 -15,15 v 127.347 c 0,21.034 -17.113,38.147 -38.147,38.147 H 68.904 c -21.035,0 -38.147,-17.113 -38.147,-38.147 V 100.413 c 0,-21.034 17.113,-38.147 38.147,-38.147 h 131.587 c 8.284,0 15,-6.716 15,-15 0,-8.284 -6.716,-15 -15,-15 H 68.904 c -37.577,0 -68.147,30.571 -68.147,68.147 v 180.321 c 0,37.576 30.571,68.147 68.147,68.147 h 181.798 c 37.576,0 68.147,-30.571 68.147,-68.147 V 153.388 c 0.001,-8.284 -6.715,-15 -14.999,-15 z"
|
||||
id="path172"
|
||||
style="fill:#ed6b21;fill-opacity:1" />
|
||||
<g
|
||||
id="g176">
|
||||
</g>
|
||||
<g
|
||||
id="g178">
|
||||
</g>
|
||||
<g
|
||||
id="g180">
|
||||
</g>
|
||||
<g
|
||||
id="g182">
|
||||
</g>
|
||||
<g
|
||||
id="g184">
|
||||
</g>
|
||||
<g
|
||||
id="g186">
|
||||
</g>
|
||||
<g
|
||||
id="g188">
|
||||
</g>
|
||||
<g
|
||||
id="g190">
|
||||
</g>
|
||||
<g
|
||||
id="g192">
|
||||
</g>
|
||||
<g
|
||||
id="g194">
|
||||
</g>
|
||||
<g
|
||||
id="g196">
|
||||
</g>
|
||||
<g
|
||||
id="g198">
|
||||
</g>
|
||||
<g
|
||||
id="g200">
|
||||
</g>
|
||||
<g
|
||||
id="g202">
|
||||
</g>
|
||||
<g
|
||||
id="g204">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.7 KiB |
5
resources/icons/print_running.svg
Normal file
5
resources/icons/print_running.svg
Normal file
@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" viewBox="0 0 800 800">
|
||||
<circle cx="400" cy="400" r="400" fill="#fff"/>
|
||||
<path d="M599.3,186.8c-93.9-93.9-246.1-93.9-340,0s-93.9,246.1,0,340Z" transform="translate(0 0)" fill="#363636"/>
|
||||
<path d="M202.7,612.5c93.9,93.9,246.1,93.9,340,0s93.9-246.1,0-340" transform="translate(0 0)" fill="#ed6b21"/>
|
||||
</svg>
|
After Width: | Height: | Size: 374 B |
@ -466,9 +466,11 @@ namespace DoExport {
|
||||
static void update_print_estimated_stats(const GCodeProcessor& processor, const std::vector<Extruder>& extruders, PrintStatistics& print_statistics)
|
||||
{
|
||||
const GCodeProcessorResult& result = processor.get_result();
|
||||
print_statistics.estimated_normal_print_time = get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time);
|
||||
print_statistics.normal_print_time_seconds = result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Normal)].time;
|
||||
print_statistics.silent_print_time_seconds = result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time;
|
||||
print_statistics.estimated_normal_print_time = get_time_dhms(print_statistics.normal_print_time_seconds);
|
||||
print_statistics.estimated_silent_print_time = processor.is_stealth_time_estimator_enabled() ?
|
||||
get_time_dhms(result.print_statistics.modes[static_cast<size_t>(PrintEstimatedStatistics::ETimeMode::Stealth)].time) : "N/A";
|
||||
get_time_dhms(print_statistics.silent_print_time_seconds) : "N/A";
|
||||
|
||||
// update filament statictics
|
||||
double total_extruded_volume = 0.0;
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include "Print.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@ -154,41 +155,114 @@ void MultipleBeds::set_active_bed(int i)
|
||||
m_active_bed = i;
|
||||
}
|
||||
|
||||
void MultipleBeds::move_active_to_first_bed(Model& model, const BuildVolume& build_volume, bool to_or_from) const
|
||||
{
|
||||
static std::vector<std::pair<Vec3d, bool>> old_state;
|
||||
size_t i = 0;
|
||||
assert(! to_or_from || old_state.empty());
|
||||
|
||||
namespace MultipleBedsUtils {
|
||||
InstanceOffsets get_instance_offsets(Model& model) {
|
||||
InstanceOffsets result;
|
||||
for (ModelObject* mo : model.objects) {
|
||||
for (ModelInstance* mi : mo->instances) {
|
||||
if (to_or_from) {
|
||||
old_state.resize(i+1);
|
||||
old_state[i] = std::make_pair(mi->get_offset(), mi->printable);
|
||||
if (this->is_instance_on_active_bed(mi->id()))
|
||||
mi->set_offset(mi->get_offset() - get_bed_translation(get_active_bed()));
|
||||
else
|
||||
mi->printable = false;
|
||||
} else {
|
||||
mi->set_offset(old_state[i].first);
|
||||
mi->printable = old_state[i].second;
|
||||
}
|
||||
++i;
|
||||
result.emplace_back(mi->get_offset());
|
||||
}
|
||||
}
|
||||
if (! to_or_from)
|
||||
old_state.clear();
|
||||
return result;
|
||||
}
|
||||
|
||||
ObjectInstances get_object_instances(const Model& model) {
|
||||
ObjectInstances result;
|
||||
|
||||
std::transform(
|
||||
model.objects.begin(),
|
||||
model.objects.end(),
|
||||
std::back_inserter(result),
|
||||
[](ModelObject *object){
|
||||
return std::pair{object, object->instances};
|
||||
}
|
||||
);
|
||||
|
||||
bool MultipleBeds::is_instance_on_active_bed(ObjectID id) const
|
||||
return result;
|
||||
}
|
||||
|
||||
void restore_instance_offsets(Model& model, const InstanceOffsets &offsets)
|
||||
{
|
||||
size_t i = 0;
|
||||
for (ModelObject* mo : model.objects) {
|
||||
for (ModelInstance* mi : mo->instances) {
|
||||
mi->set_offset(offsets[i++]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void restore_object_instances(Model& model, const ObjectInstances &object_instances) {
|
||||
ModelObjectPtrs objects;
|
||||
|
||||
std::transform(
|
||||
object_instances.begin(),
|
||||
object_instances.end(),
|
||||
std::back_inserter(objects),
|
||||
[](const std::pair<ModelObject *, ModelInstancePtrs> &key_value){
|
||||
auto [object, instances]{key_value};
|
||||
object->instances = std::move(instances);
|
||||
return object;
|
||||
}
|
||||
);
|
||||
|
||||
model.objects = objects;
|
||||
}
|
||||
|
||||
void with_single_bed_model(Model &model, const int bed_index, const std::function<void()> &callable) {
|
||||
const InstanceOffsets original_offssets{MultipleBedsUtils::get_instance_offsets(model)};
|
||||
const ObjectInstances original_objects{get_object_instances(model)};
|
||||
Slic3r::ScopeGuard guard([&]() {
|
||||
restore_object_instances(model, original_objects);
|
||||
restore_instance_offsets(model, original_offssets);
|
||||
});
|
||||
|
||||
s_multiple_beds.move_from_bed_to_first_bed(model, bed_index);
|
||||
s_multiple_beds.remove_instances_outside_outside_bed(model, bed_index);
|
||||
callable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool MultipleBeds::is_instance_on_bed(const ObjectID id, const int bed_index) const
|
||||
{
|
||||
auto it = m_inst_to_bed.find(id);
|
||||
return (it != m_inst_to_bed.end() && it->second == m_active_bed);
|
||||
return (it != m_inst_to_bed.end() && it->second == bed_index);
|
||||
}
|
||||
|
||||
void MultipleBeds::remove_instances_outside_outside_bed(Model& model, const int bed_index) const {
|
||||
for (ModelObject* mo : model.objects) {
|
||||
mo->instances.erase(std::remove_if(
|
||||
mo->instances.begin(),
|
||||
mo->instances.end(),
|
||||
[&](const ModelInstance* instance){
|
||||
return !this->is_instance_on_bed(instance->id(), bed_index);
|
||||
}
|
||||
), mo->instances.end());
|
||||
}
|
||||
|
||||
model.objects.erase(std::remove_if(
|
||||
model.objects.begin(),
|
||||
model.objects.end(),
|
||||
[](const ModelObject *object){
|
||||
return object->instances.empty();
|
||||
}
|
||||
), model.objects.end());
|
||||
}
|
||||
|
||||
void MultipleBeds::move_from_bed_to_first_bed(Model& model, const int bed_index) const
|
||||
{
|
||||
if (bed_index < 0 || bed_index >= MAX_NUMBER_OF_BEDS) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
for (ModelObject* mo : model.objects) {
|
||||
for (ModelInstance* mi : mo->instances) {
|
||||
if (this->is_instance_on_bed(mi->id(), bed_index)) {
|
||||
mi->set_offset(mi->get_offset() - get_bed_translation(bed_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool MultipleBeds::is_glvolume_on_thumbnail_bed(const Model& model, int obj_idx, int instance_idx) const
|
||||
{
|
||||
|
@ -1,6 +1,7 @@
|
||||
#ifndef libslic3r_MultipleBeds_hpp_
|
||||
#define libslic3r_MultipleBeds_hpp_
|
||||
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ObjectID.hpp"
|
||||
#include "libslic3r/Point.hpp"
|
||||
#include "libslic3r/BoundingBox.hpp"
|
||||
@ -24,6 +25,10 @@ Index grid_coords2index(const GridCoords &coords);
|
||||
GridCoords index2grid_coords(Index index);
|
||||
}
|
||||
|
||||
inline std::vector<unsigned> s_bed_selector_thumbnail_texture_ids;
|
||||
inline std::array<bool, MAX_NUMBER_OF_BEDS> s_bed_selector_thumbnail_changed;
|
||||
inline bool bed_selector_updated{false};
|
||||
|
||||
class MultipleBeds {
|
||||
public:
|
||||
MultipleBeds() = default;
|
||||
@ -44,7 +49,11 @@ public:
|
||||
int get_active_bed() const { return m_active_bed; }
|
||||
|
||||
void set_active_bed(int i);
|
||||
void move_active_to_first_bed(Model& model, const BuildVolume& build_volume, bool to_or_from) const;
|
||||
|
||||
void remove_instances_outside_outside_bed(Model& model, const int bed) const;
|
||||
|
||||
// Sets !printable to all instances outside the active bed.
|
||||
void move_from_bed_to_first_bed(Model& model, const int bed) const;
|
||||
|
||||
void set_thumbnail_bed_idx(int bed_idx) { m_bed_for_thumbnails_generation = bed_idx; }
|
||||
int get_thumbnail_bed_idx() const { return m_bed_for_thumbnails_generation; }
|
||||
@ -71,7 +80,7 @@ public:
|
||||
void autoslice_next_bed();
|
||||
|
||||
private:
|
||||
bool is_instance_on_active_bed(ObjectID id) const;
|
||||
bool is_instance_on_bed(const ObjectID id, const int bed_index) const;
|
||||
|
||||
int m_number_of_beds = 1;
|
||||
int m_active_bed = 0;
|
||||
@ -91,6 +100,29 @@ private:
|
||||
};
|
||||
|
||||
extern MultipleBeds s_multiple_beds;
|
||||
|
||||
namespace MultipleBedsUtils {
|
||||
|
||||
using InstanceOffsets = std::vector<Vec3d>;
|
||||
// The bool is true if the instance is printable.
|
||||
// The order is from 'for o in objects; for i in o.instances.
|
||||
InstanceOffsets get_instance_offsets(Model& model);
|
||||
|
||||
using ObjectInstances = std::vector<std::pair<ModelObject*, ModelInstancePtrs>>;
|
||||
ObjectInstances get_object_instances(const Model& model);
|
||||
void restore_instance_offsets(Model& model, const InstanceOffsets &offsets);
|
||||
void restore_object_instances(Model& model, const ObjectInstances &object_instances);
|
||||
|
||||
|
||||
/**
|
||||
For each print apply call do:
|
||||
- move all instances according to their active bed
|
||||
- apply
|
||||
- move all instances back to their respective beds
|
||||
*/
|
||||
void with_single_bed_model(Model &model, const int bed_index, const std::function<void()> &callable);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // libslic3r_MultipleBeds_hpp_
|
||||
|
@ -520,6 +520,8 @@ bool is_toolchange_required(
|
||||
struct PrintStatistics
|
||||
{
|
||||
PrintStatistics() { clear(); }
|
||||
float normal_print_time_seconds;
|
||||
float silent_print_time_seconds;
|
||||
std::string estimated_normal_print_time;
|
||||
std::string estimated_silent_print_time;
|
||||
double total_used_filament;
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include <igl/unproject.h> // IWYU pragma: keep
|
||||
#include <LocalesUtils.hpp>
|
||||
#include <nanosvgrast.h>
|
||||
|
||||
#include "libslic3r/BuildVolume.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
@ -44,6 +45,7 @@
|
||||
#include "NotificationManager.hpp"
|
||||
#include "format.hpp"
|
||||
|
||||
#include "slic3r/GUI/BitmapCache.hpp"
|
||||
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
|
||||
#include "slic3r/Utils/UndoRedo.hpp"
|
||||
|
||||
@ -73,6 +75,7 @@
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/crc.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <float.h>
|
||||
@ -1823,6 +1826,242 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
|
||||
m_volumes.update_colors_by_extruder(m_config);
|
||||
}
|
||||
|
||||
using PerBedStatistics = std::vector<std::pair<
|
||||
std::size_t,
|
||||
std::reference_wrapper<const PrintStatistics>
|
||||
>>;
|
||||
|
||||
PerBedStatistics get_statistics(){
|
||||
PerBedStatistics result;
|
||||
for (int bed_index=0; bed_index<s_multiple_beds.get_number_of_beds(); ++bed_index) {
|
||||
const Print* print = wxGetApp().plater()->get_fff_prints()[bed_index].get();
|
||||
if (print->empty() || !print->finished()) {
|
||||
continue;
|
||||
}
|
||||
result.emplace_back(bed_index, std::ref(print->print_statistics()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct StatisticsSum {
|
||||
float cost{};
|
||||
float filement_weight{};
|
||||
float filament_length{};
|
||||
float normal_print_time{};
|
||||
float silent_print_time{};
|
||||
};
|
||||
|
||||
StatisticsSum get_statistics_sum() {
|
||||
StatisticsSum result;
|
||||
for (const auto &[_, statistics] : get_statistics()) {
|
||||
result.cost += statistics.get().total_cost;
|
||||
result.filement_weight += statistics.get().total_weight;
|
||||
result.filament_length += statistics.get().total_used_filament;
|
||||
result.normal_print_time += statistics.get().normal_print_time_seconds;
|
||||
result.silent_print_time += statistics.get().silent_print_time_seconds;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void project_overview_table() {
|
||||
ImGui::Text("%s", _u8L("Project overview").c_str());
|
||||
if (ImGui::BeginTable("table1", 6)) {
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0);
|
||||
ImGui::TableSetupColumn(
|
||||
_u8L("Cost").c_str(),
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
60.0f
|
||||
);
|
||||
ImGui::TableSetupColumn(
|
||||
(_u8L("Filament") + " (g)").c_str(),
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
100.0f
|
||||
);
|
||||
ImGui::TableSetupColumn(
|
||||
(_u8L("Filament") + " (m)").c_str(),
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
100.0f
|
||||
);
|
||||
ImGui::TableSetupColumn(
|
||||
(_u8L("Estimate Time") + " (" + _u8L("Stealth mode") +")").c_str(),
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
200.0f
|
||||
);
|
||||
ImGui::TableSetupColumn(
|
||||
(_u8L("Estimate Time") + " (" + _u8L("Normal mode") +")").c_str(),
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
200.0f
|
||||
);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto &[bed_index, statistics] : get_statistics()) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", (_u8L("Plate") + wxString::Format(" %d", bed_index + 1)).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_cost).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_weight).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.get().total_used_filament / 1000).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", statistics.get().estimated_silent_print_time.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", statistics.get().estimated_normal_print_time.c_str());
|
||||
}
|
||||
|
||||
const StatisticsSum statistics_sum{get_statistics_sum()};
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", _u8L("Total").c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics_sum.cost).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics_sum.filement_weight).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics_sum.filament_length / 1000).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", get_time_dhms(statistics_sum.silent_print_time).c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", get_time_dhms(statistics_sum.normal_print_time).c_str());
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
struct ExtruderStatistics {
|
||||
float filament_weight{};
|
||||
float filament_length{};
|
||||
};
|
||||
|
||||
using PerExtruderStatistics = std::map<
|
||||
std::size_t,
|
||||
ExtruderStatistics
|
||||
>;
|
||||
|
||||
PerExtruderStatistics get_extruder_statistics(){
|
||||
PerExtruderStatistics result;
|
||||
for (int bed_index=0; bed_index<s_multiple_beds.get_number_of_beds(); ++bed_index) {
|
||||
const Print* print = wxGetApp().plater()->get_fff_prints()[bed_index].get();
|
||||
if (print->empty() || !print->finished()) {
|
||||
continue;
|
||||
}
|
||||
print->print_statistics();
|
||||
const auto& extruders_filaments{wxGetApp().preset_bundle->extruders_filaments};
|
||||
for (const auto &[filament_id, filament_volume] : print->print_statistics().filament_stats) {
|
||||
const Preset* preset = extruders_filaments[filament_id].get_selected_preset();
|
||||
if (preset == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const double filament_density = preset->config.opt_float("filament_density", 0);
|
||||
const double diameter = preset->config.opt_float("filament_diameter", filament_id);
|
||||
result[filament_id].filament_weight += filament_volume * filament_density / 1000.0f;
|
||||
result[filament_id].filament_length += filament_volume / (M_PI * diameter * diameter / 4.0) / 1000.0;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
ExtruderStatistics sum_extruder_statistics(
|
||||
const PerExtruderStatistics &per_extruder_statistics
|
||||
) {
|
||||
ExtruderStatistics result;
|
||||
for (const auto &[_, statistics] : per_extruder_statistics) {
|
||||
result.filament_weight += statistics.filament_weight;
|
||||
result.filament_length += statistics.filament_length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void extruder_usage_table(const PerExtruderStatistics &extruder_statistics) {
|
||||
ImGui::Text("%s", _u8L("Extruder usage breakdown").c_str());
|
||||
if (ImGui::BeginTable("table1", 3)) {
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, 100.0f);
|
||||
ImGui::TableSetupColumn(
|
||||
(_u8L("Filament") + " (g)").c_str(),
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
100.0f
|
||||
);
|
||||
ImGui::TableSetupColumn(
|
||||
(_u8L("Filament") + " (m)").c_str(),
|
||||
ImGuiTableColumnFlags_WidthFixed,
|
||||
100.0f
|
||||
);
|
||||
ImGui::TableHeadersRow();
|
||||
|
||||
for (const auto &[extruder_index, statistics] : extruder_statistics) {
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", (_u8L("Extruder") + wxString::Format(" %d", extruder_index + 1)).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.filament_weight).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", statistics.filament_length).ToStdString().c_str());
|
||||
}
|
||||
|
||||
const ExtruderStatistics extruder_statistics_sum{sum_extruder_statistics(extruder_statistics)};
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", _u8L("Total").c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", extruder_statistics_sum.filament_weight).ToStdString().c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", wxString::Format("%.2f", extruder_statistics_sum.filament_length).ToStdString().c_str());
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
void begin_statistics(const char *window_name) {
|
||||
ImGuiWindowFlags windows_flags =
|
||||
ImGuiWindowFlags_NoCollapse
|
||||
| ImGuiWindowFlags_NoMove
|
||||
| ImGuiWindowFlags_AlwaysAutoResize
|
||||
| ImGuiWindowFlags_NoScrollbar
|
||||
| ImGuiWindowFlags_NoScrollWithMouse;
|
||||
|
||||
const ImVec2 center{ImGui::GetMainViewport()->GetCenter()};
|
||||
const ImVec2 position{center + ImVec2{0, -150.0f}};
|
||||
ImGui::SetNextWindowPos(position, ImGuiCond_Appearing, ImVec2{0.5f, 0.5f});
|
||||
|
||||
ImGui::Begin(window_name, nullptr, windows_flags);
|
||||
}
|
||||
|
||||
void render_print_statistics() {
|
||||
begin_statistics(_u8L("Statistics").c_str());
|
||||
ImGui::Spacing();
|
||||
project_overview_table();
|
||||
ImGui::Separator();
|
||||
|
||||
const PerExtruderStatistics extruder_statistics{get_extruder_statistics()};
|
||||
if (extruder_statistics.size() > 1) {
|
||||
ImGui::NewLine();
|
||||
extruder_usage_table(extruder_statistics);
|
||||
ImGui::Separator();
|
||||
}
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void render_autoslicing_wait() {
|
||||
const std::string text{_u8L("Statistics will be available once all beds are sliced")};
|
||||
const float text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(text_width + 50, 110.0));
|
||||
begin_statistics(_u8L("Waiting for statistics").c_str());
|
||||
ImGui::NewLine();
|
||||
const float width{ImGui::GetContentRegionAvail().x};
|
||||
float offset_x = (width - text_width) / 2.0f;
|
||||
if (offset_x > 0.0f){
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + offset_x);
|
||||
}
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::NewLine();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void GLCanvas3D::render()
|
||||
{
|
||||
if (m_in_render) {
|
||||
@ -1964,34 +2203,31 @@ void GLCanvas3D::render()
|
||||
if (m_picking_enabled && m_rectangle_selection.is_dragging())
|
||||
m_rectangle_selection.render(*this);
|
||||
} else {
|
||||
// Autoslicing.
|
||||
// Render the combined statistics if all is ready.
|
||||
bool valid = true;
|
||||
double total_g = 0;
|
||||
for (size_t i=0; i<s_multiple_beds.get_number_of_beds(); ++i) {
|
||||
const Print* print = wxGetApp().plater()->get_fff_prints()[i].get();
|
||||
if (!print->finished()) {
|
||||
// TODO: Only active bed invalidation can be detected here.
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
total_g += print->print_statistics().total_used_filament;
|
||||
}
|
||||
ImGui::Begin("Total stats");
|
||||
if (valid)
|
||||
ImGui::Text("%s", std::to_string(total_g).c_str());
|
||||
else
|
||||
ImGui::Text("Wait until all beds are sliced...");
|
||||
ImGui::End();
|
||||
const auto &prints{
|
||||
tcb::span{wxGetApp().plater()->get_fff_prints()}
|
||||
.subspan(0, s_multiple_beds.get_number_of_beds())
|
||||
};
|
||||
|
||||
if (!valid) {
|
||||
if (fff_print()->finished())
|
||||
const bool all_finished{std::all_of(
|
||||
prints.begin(),
|
||||
prints.end(),
|
||||
[](const std::unique_ptr<Print> &print){
|
||||
return print->finished() || print->empty();
|
||||
}
|
||||
)};
|
||||
|
||||
if (!all_finished) {
|
||||
render_autoslicing_wait();
|
||||
if (fff_print()->finished() || fff_print()->empty()) {
|
||||
s_multiple_beds.autoslice_next_bed();
|
||||
else
|
||||
} else {
|
||||
wxGetApp().plater()->schedule_background_process();
|
||||
}
|
||||
} else {
|
||||
render_print_statistics();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_render_overlays();
|
||||
|
||||
_render_bed_selector();
|
||||
@ -2537,7 +2773,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||
#endif // SLIC3R_OPENGL_ES
|
||||
const BoundingBoxf3& bb = volume->bounding_box();
|
||||
m_wipe_tower_bounding_boxes[bed_idx] = BoundingBoxf{to_2d(bb.min), to_2d(bb.max)};
|
||||
if(bed_idx < s_multiple_beds.get_number_of_beds()) {
|
||||
if(static_cast<int>(bed_idx) < s_multiple_beds.get_number_of_beds()) {
|
||||
m_volumes.volumes.emplace_back(volume);
|
||||
const auto volume_idx_wipe_tower_new{static_cast<int>(m_volumes.volumes.size() - 1)};
|
||||
auto it = volume_idxs_wipe_towers_old.find(m_volumes.volumes.back()->geometry_id.second);
|
||||
@ -6330,20 +6566,81 @@ void GLCanvas3D::_render_overlays()
|
||||
|
||||
#define use_scrolling 1
|
||||
|
||||
enum class PrintStatus {
|
||||
idle,
|
||||
running,
|
||||
finished
|
||||
};
|
||||
|
||||
std::string get_status_text(PrintStatus status) {
|
||||
switch(status) {
|
||||
case PrintStatus::idle: return "Idle";
|
||||
case PrintStatus::running: return "Running";
|
||||
case PrintStatus::finished: return "Finished";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
wchar_t get_raw_status_icon(const PrintStatus status) {
|
||||
switch(status) {
|
||||
case PrintStatus::finished: return ImGui::PrintFinished;
|
||||
case PrintStatus::running: return ImGui::PrintRunning;
|
||||
case PrintStatus::idle: return ImGui::PrintIdle;
|
||||
}
|
||||
return ImGui::PrintIdle;
|
||||
}
|
||||
|
||||
std::string get_status_icon(const PrintStatus status) {
|
||||
return boost::nowide::narrow(std::wstring{get_raw_status_icon(status)});
|
||||
}
|
||||
|
||||
bool bed_selector_thumbnail(
|
||||
const ImVec2 size,
|
||||
const ImVec2 padding,
|
||||
const float side,
|
||||
const float border,
|
||||
const GLuint texture_id,
|
||||
const PrintStatus status
|
||||
) {
|
||||
ImGuiWindow* window = GImGui->CurrentWindow;
|
||||
const ImVec2 current_position = GImGui->CurrentWindow->DC.CursorPos;
|
||||
const ImVec2 state_pos = current_position + ImVec2(border, side - 20.f - border);
|
||||
|
||||
const bool clicked{ImGui::ImageButton(
|
||||
(void*)(int64_t)texture_id,
|
||||
size - padding,
|
||||
ImVec2(0, 1),
|
||||
ImVec2(1, 0),
|
||||
border
|
||||
)};
|
||||
|
||||
const std::string icon{get_status_icon(status)};
|
||||
|
||||
window->DrawList->AddText(
|
||||
GImGui->Font,
|
||||
GImGui->FontSize,
|
||||
state_pos,
|
||||
ImGui::GetColorU32(ImGuiCol_Text),
|
||||
icon.c_str(),
|
||||
icon.c_str() + icon.size()
|
||||
);
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
{
|
||||
static float btn_side = 80.f;
|
||||
static float btn_border = 2.f;
|
||||
static bool hide_title = true;
|
||||
|
||||
ImVec2 btn_size = ImVec2(btn_side, btn_side);
|
||||
|
||||
if (s_multiple_beds.get_number_of_beds() != 1 && wxGetApp().plater()->is_preview_shown()) {
|
||||
auto render_bed_button = [btn_size, this](int i)
|
||||
{
|
||||
//ImGui::Text("%d", i);
|
||||
//ImGui::SameLine();
|
||||
bool extra_frame{ false };
|
||||
|
||||
static std::array<std::optional<PrintStatus>, MAX_NUMBER_OF_BEDS> previous_print_status;
|
||||
if (s_multiple_beds.get_number_of_beds() != 1 && wxGetApp().plater()->is_preview_shown()) {
|
||||
auto render_bed_button = [btn_size, this, &extra_frame](int i)
|
||||
{
|
||||
bool empty = ! s_multiple_beds.is_bed_occupied(i);
|
||||
bool inactive = i != s_multiple_beds.get_active_bed() || s_multiple_beds.is_autoslicing();
|
||||
|
||||
@ -6355,10 +6652,36 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
|
||||
bool clicked = false;
|
||||
ImVec2 btn_padding = ImVec2(btn_border, btn_border);
|
||||
if (i >= int(s_th_tex_id.size()) || empty)
|
||||
|
||||
PrintStatus print_status{PrintStatus::idle};
|
||||
if (wxGetApp().plater()->get_fff_prints()[i]->finished()) {
|
||||
print_status = PrintStatus::finished;
|
||||
} else if (m_process->fff_print() == wxGetApp().plater()->get_fff_prints()[i].get() && m_process->running()) {
|
||||
print_status = PrintStatus::running;
|
||||
}
|
||||
|
||||
if (!previous_print_status[i] || print_status != previous_print_status[i]) {
|
||||
extra_frame = true;
|
||||
}
|
||||
previous_print_status[i] = print_status;
|
||||
|
||||
if (s_bed_selector_thumbnail_changed[i]) {
|
||||
extra_frame = true;
|
||||
s_bed_selector_thumbnail_changed[i] = false;
|
||||
}
|
||||
|
||||
if (i >= int(s_bed_selector_thumbnail_texture_ids.size()) || empty) {
|
||||
clicked = ImGui::Button(empty ? "empty" : std::to_string(i + 1).c_str(), btn_size + btn_padding);
|
||||
else
|
||||
clicked = ImGui::ImageButton((void*)(int64_t)s_th_tex_id[i], btn_size - btn_padding, ImVec2(0, 1), ImVec2(1, 0), btn_border);
|
||||
} else {
|
||||
clicked = bed_selector_thumbnail(
|
||||
btn_size,
|
||||
btn_padding,
|
||||
btn_side,
|
||||
btn_border,
|
||||
s_bed_selector_thumbnail_texture_ids[i],
|
||||
print_status
|
||||
);
|
||||
}
|
||||
|
||||
if (clicked && ! empty)
|
||||
select_bed(i, true);
|
||||
@ -6367,15 +6690,9 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
if (empty)
|
||||
ImGui::PopItemFlag();
|
||||
|
||||
//std::string status_text;
|
||||
//if (wxGetApp().plater()->get_fff_prints()[i]->finished())
|
||||
// status_text = "Finished";
|
||||
//else if (m_process->fff_print() == wxGetApp().plater()->get_fff_prints()[i].get() && m_process->running())
|
||||
// status_text = "Running";
|
||||
//else
|
||||
// status_text = "Idle";
|
||||
//if (ImGui::IsItemHovered())
|
||||
// ImGui::SetTooltip(status_text.c_str());
|
||||
const std::string status_text{get_status_text(print_status)};
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", status_text.c_str());
|
||||
};
|
||||
|
||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||
@ -6398,11 +6715,11 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2());
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
|
||||
|
||||
// Disable for now.
|
||||
//if (imgui.image_button(ImGui::SliceAllBtnIcon, "Slice All")) {
|
||||
// if (!s_multiple_beds.is_autoslicing())
|
||||
// s_multiple_beds.start_autoslice([this](int i, bool user) { this->select_bed(i, user); });
|
||||
//}
|
||||
if (imgui.image_button(ImGui::SliceAllBtnIcon, "Slice All")) {
|
||||
if (!s_multiple_beds.is_autoslicing())
|
||||
s_multiple_beds.start_autoslice([this](int i, bool user) { this->select_bed(i, user); });
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, btn_border);
|
||||
@ -6418,8 +6735,6 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||
ImGui::PopStyleVar(3);
|
||||
|
||||
#if use_scrolling
|
||||
bool extra_frame{ false };
|
||||
|
||||
ImVec2 win_size = ImGui::GetCurrentWindow()->ContentSizeIdeal +
|
||||
ImGui::GetCurrentWindow()->WindowPadding * 2.f +
|
||||
ImGui::GetCurrentWindow()->ScrollbarSizes +
|
||||
|
@ -113,6 +113,9 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
||||
{ImGui::HorizontalHide , "horizontal_hide" },
|
||||
{ImGui::HorizontalShow , "horizontal_show" },
|
||||
{ImGui::SliceAllBtnIcon , "slice_all" },
|
||||
{ImGui::PrintIdle , "print_idle" },
|
||||
{ImGui::PrintRunning , "print_running" },
|
||||
{ImGui::PrintFinished , "print_finished" },
|
||||
};
|
||||
|
||||
static const std::map<const wchar_t, std::string> font_icons_large = {
|
||||
|
@ -18,10 +18,12 @@
|
||||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||
///|/
|
||||
#include "Plater.hpp"
|
||||
#include "slic3r/GUI/BitmapCache.hpp"
|
||||
#include "slic3r/GUI/Jobs/UIThreadWorker.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <algorithm>
|
||||
#include <nanosvgrast.h>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@ -155,8 +157,6 @@ using Slic3r::GUI::format_wxstr;
|
||||
|
||||
static const std::pair<unsigned int, unsigned int> THUMBNAIL_SIZE_3MF = { 256, 256 };
|
||||
|
||||
std::vector<GLuint> s_th_tex_id;
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
@ -603,7 +603,6 @@ private:
|
||||
// vector of all warnings generated by last slicing
|
||||
std::vector<std::pair<Slic3r::PrintStateBase::Warning, size_t>> current_warnings;
|
||||
bool show_warning_dialog { false };
|
||||
|
||||
};
|
||||
|
||||
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
|
||||
@ -2160,6 +2159,24 @@ void Plater::priv::process_validation_warning(const std::vector<std::string>& wa
|
||||
}
|
||||
}
|
||||
|
||||
std::array<Print::ApplyStatus, MAX_NUMBER_OF_BEDS> apply_to_inactive_beds(
|
||||
Model &model,
|
||||
std::vector<std::unique_ptr<Print>> &prints,
|
||||
const DynamicPrintConfig &config
|
||||
) {
|
||||
std::array<Print::ApplyStatus, MAX_NUMBER_OF_BEDS> result;
|
||||
for (std::size_t bed_index{0}; bed_index < prints.size(); ++bed_index) {
|
||||
const std::unique_ptr<Print> &print{prints[bed_index]};
|
||||
if (!print || bed_index == s_multiple_beds.get_active_bed()) {
|
||||
continue;
|
||||
}
|
||||
using MultipleBedsUtils::with_single_bed_model;
|
||||
with_single_bed_model(model, bed_index, [&](){
|
||||
result[bed_index] = print->apply(model, config);
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Update background processing thread from the current config and Model.
|
||||
// Returns a bitmask of UpdateBackgroundProcessReturnState.
|
||||
@ -2174,6 +2191,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
background_process.set_gcode_result(&gcode_results[active_bed]);
|
||||
background_process.select_technology(this->printer_technology);
|
||||
|
||||
|
||||
if (s_beds_just_switched && printer_technology == ptFFF) {
|
||||
PrintBase::SlicingStatus status(q->active_fff_print(), -1);
|
||||
SlicingStatusEvent evt(EVT_SLICING_UPDATE, 0, status);
|
||||
@ -2182,10 +2200,12 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
notification_manager->close_notification_of_type(NotificationType::ExportOngoing);
|
||||
q->sidebar().show_sliced_info_sizer(background_process.finished());
|
||||
}
|
||||
|
||||
|
||||
// bitmap of enum UpdateBackgroundProcessReturnState
|
||||
unsigned int return_state = 0;
|
||||
if (s_multiple_beds.is_autoslicing()) {
|
||||
return_state = return_state | UPDATE_BACKGROUND_PROCESS_FORCE_RESTART;
|
||||
}
|
||||
|
||||
// Get the config ready. The binary gcode flag depends on Preferences, which the backend has no access to.
|
||||
DynamicPrintConfig full_config = wxGetApp().preset_bundle->full_config();
|
||||
@ -2211,18 +2231,29 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
// Update the "out of print bed" state of ModelInstances.
|
||||
update_print_volume_state();
|
||||
|
||||
// Move all instances according to their active bed:
|
||||
s_multiple_beds.move_active_to_first_bed(q->model(), q->build_volume(), true);
|
||||
Print::ApplyStatus invalidated{Print::ApplyStatus::APPLY_STATUS_INVALIDATED};
|
||||
bool was_running = background_process.running();
|
||||
using MultipleBedsUtils::with_single_bed_model;
|
||||
|
||||
// Apply new config to the possibly running background task.
|
||||
bool was_running = background_process.running();
|
||||
Print::ApplyStatus invalidated = background_process.apply(q->model(), full_config);
|
||||
std::array<Print::ApplyStatus, MAX_NUMBER_OF_BEDS> apply_statuses{
|
||||
apply_to_inactive_beds(q->model(), q->p->fff_prints, full_config)
|
||||
};
|
||||
with_single_bed_model(q->model(), s_multiple_beds.get_active_bed(), [&](){
|
||||
// Apply new config to the possibly running background task.
|
||||
invalidated = background_process.apply(q->model(), full_config);
|
||||
apply_statuses[s_multiple_beds.get_active_bed()] = invalidated;
|
||||
});
|
||||
|
||||
// Move all instances back to their respective beds.
|
||||
s_multiple_beds.move_active_to_first_bed(q->model(), q->build_volume(), false);
|
||||
const bool any_status_changed{std::any_of(
|
||||
apply_statuses.begin(),
|
||||
apply_statuses.end(),
|
||||
[](Print::ApplyStatus status){
|
||||
return status != Print::ApplyStatus::APPLY_STATUS_UNCHANGED;
|
||||
}
|
||||
)};
|
||||
|
||||
// If current bed was invalidated, update thumbnails for all beds:
|
||||
if (int num = s_multiple_beds.get_number_of_beds(); num > 1 && ! (invalidated & Print::ApplyStatus::APPLY_STATUS_UNCHANGED)) {
|
||||
if (int num = s_multiple_beds.get_number_of_beds(); num > 1 && any_status_changed) {
|
||||
ThumbnailData data;
|
||||
ThumbnailsParams params;
|
||||
params.parts_only = true;
|
||||
@ -2236,19 +2267,21 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
||||
int curr_unpack_alignment = 0;
|
||||
glsafe(glGetIntegerv(GL_UNPACK_ALIGNMENT, &curr_unpack_alignment));
|
||||
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||
glsafe(glDeleteTextures(s_th_tex_id.size(), s_th_tex_id.data()));
|
||||
|
||||
s_th_tex_id.resize(num);
|
||||
glsafe(glGenTextures(num, s_th_tex_id.data()));
|
||||
glsafe(glDeleteTextures(s_bed_selector_thumbnail_texture_ids.size(), s_bed_selector_thumbnail_texture_ids.data()));
|
||||
s_bed_selector_thumbnail_changed.fill(false);
|
||||
|
||||
s_bed_selector_thumbnail_texture_ids.resize(num);
|
||||
glsafe(glGenTextures(num, s_bed_selector_thumbnail_texture_ids.data()));
|
||||
for (int i = 0; i < num; ++i) {
|
||||
s_multiple_beds.set_thumbnail_bed_idx(i);
|
||||
generate_thumbnail(data, w, h, params, GUI::Camera::EType::Ortho);
|
||||
s_multiple_beds.set_thumbnail_bed_idx(-1);
|
||||
glsafe(glBindTexture(GL_TEXTURE_2D, s_th_tex_id[i]));
|
||||
glsafe(glBindTexture(GL_TEXTURE_2D, s_bed_selector_thumbnail_texture_ids[i]));
|
||||
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST));
|
||||
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
||||
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0));
|
||||
glsafe(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB32F, static_cast<GLsizei>(w), static_cast<GLsizei>(h), 0, GL_RGBA, GL_UNSIGNED_BYTE, data.pixels.data()));
|
||||
s_bed_selector_thumbnail_changed[i] = true;
|
||||
}
|
||||
glsafe(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
||||
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, curr_unpack_alignment));
|
||||
@ -2411,12 +2444,15 @@ bool Plater::priv::restart_background_process(unsigned int state)
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ! this->background_process.empty() &&
|
||||
(state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 &&
|
||||
( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) ||
|
||||
(state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 ||
|
||||
(state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) {
|
||||
|
||||
if (
|
||||
!this->background_process.empty()
|
||||
&& (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0
|
||||
&& (
|
||||
((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && !this->background_process.finished())
|
||||
|| (state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0
|
||||
|| (state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0
|
||||
)
|
||||
) {
|
||||
// The print is valid and it can be started.
|
||||
|
||||
if (this->background_process.start()) {
|
||||
|
@ -29,6 +29,8 @@
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include "Jobs/Job.hpp"
|
||||
#include "Jobs/Worker.hpp"
|
||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||
#include "slic3r/GUI/Camera.hpp"
|
||||
|
||||
class wxString;
|
||||
|
||||
@ -118,6 +120,7 @@ public:
|
||||
void convert_gcode_to_binary();
|
||||
void reload_print();
|
||||
void object_list_changed();
|
||||
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, Camera::EType camera_type);
|
||||
|
||||
std::vector<size_t> load_files(const std::vector<boost::filesystem::path>& input_files, bool load_model = true, bool load_config = true, bool imperial_units = false);
|
||||
// To be called when providing a list of files to the GUI slic3r on command line.
|
||||
|
Loading…
x
Reference in New Issue
Block a user