mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-07-25 14:34: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
|
// icon for multiple beds
|
||||||
const wchar_t SliceAllBtnIcon = 0x2811;
|
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);
|
// 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)
|
static void update_print_estimated_stats(const GCodeProcessor& processor, const std::vector<Extruder>& extruders, PrintStatistics& print_statistics)
|
||||||
{
|
{
|
||||||
const GCodeProcessorResult& result = processor.get_result();
|
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() ?
|
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
|
// update filament statictics
|
||||||
double total_extruded_volume = 0.0;
|
double total_extruded_volume = 0.0;
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "Print.hpp"
|
#include "Print.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
@ -154,41 +155,114 @@ void MultipleBeds::set_active_bed(int i)
|
|||||||
m_active_bed = i;
|
m_active_bed = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MultipleBeds::move_active_to_first_bed(Model& model, const BuildVolume& build_volume, bool to_or_from) const
|
namespace MultipleBedsUtils {
|
||||||
{
|
InstanceOffsets get_instance_offsets(Model& model) {
|
||||||
static std::vector<std::pair<Vec3d, bool>> old_state;
|
InstanceOffsets result;
|
||||||
size_t i = 0;
|
|
||||||
assert(! to_or_from || old_state.empty());
|
|
||||||
|
|
||||||
for (ModelObject* mo : model.objects) {
|
for (ModelObject* mo : model.objects) {
|
||||||
for (ModelInstance* mi : mo->instances) {
|
for (ModelInstance* mi : mo->instances) {
|
||||||
if (to_or_from) {
|
result.emplace_back(mi->get_offset());
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (! to_or_from)
|
return result;
|
||||||
old_state.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
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
|
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_
|
#ifndef libslic3r_MultipleBeds_hpp_
|
||||||
#define libslic3r_MultipleBeds_hpp_
|
#define libslic3r_MultipleBeds_hpp_
|
||||||
|
|
||||||
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/ObjectID.hpp"
|
#include "libslic3r/ObjectID.hpp"
|
||||||
#include "libslic3r/Point.hpp"
|
#include "libslic3r/Point.hpp"
|
||||||
#include "libslic3r/BoundingBox.hpp"
|
#include "libslic3r/BoundingBox.hpp"
|
||||||
@ -24,6 +25,10 @@ Index grid_coords2index(const GridCoords &coords);
|
|||||||
GridCoords index2grid_coords(Index index);
|
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 {
|
class MultipleBeds {
|
||||||
public:
|
public:
|
||||||
MultipleBeds() = default;
|
MultipleBeds() = default;
|
||||||
@ -44,7 +49,11 @@ public:
|
|||||||
int get_active_bed() const { return m_active_bed; }
|
int get_active_bed() const { return m_active_bed; }
|
||||||
|
|
||||||
void set_active_bed(int i);
|
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; }
|
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; }
|
int get_thumbnail_bed_idx() const { return m_bed_for_thumbnails_generation; }
|
||||||
@ -71,7 +80,7 @@ public:
|
|||||||
void autoslice_next_bed();
|
void autoslice_next_bed();
|
||||||
|
|
||||||
private:
|
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_number_of_beds = 1;
|
||||||
int m_active_bed = 0;
|
int m_active_bed = 0;
|
||||||
@ -91,6 +100,29 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
extern MultipleBeds s_multiple_beds;
|
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
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif // libslic3r_MultipleBeds_hpp_
|
#endif // libslic3r_MultipleBeds_hpp_
|
||||||
|
@ -520,6 +520,8 @@ bool is_toolchange_required(
|
|||||||
struct PrintStatistics
|
struct PrintStatistics
|
||||||
{
|
{
|
||||||
PrintStatistics() { clear(); }
|
PrintStatistics() { clear(); }
|
||||||
|
float normal_print_time_seconds;
|
||||||
|
float silent_print_time_seconds;
|
||||||
std::string estimated_normal_print_time;
|
std::string estimated_normal_print_time;
|
||||||
std::string estimated_silent_print_time;
|
std::string estimated_silent_print_time;
|
||||||
double total_used_filament;
|
double total_used_filament;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
|
|
||||||
#include <igl/unproject.h> // IWYU pragma: keep
|
#include <igl/unproject.h> // IWYU pragma: keep
|
||||||
#include <LocalesUtils.hpp>
|
#include <LocalesUtils.hpp>
|
||||||
|
#include <nanosvgrast.h>
|
||||||
|
|
||||||
#include "libslic3r/BuildVolume.hpp"
|
#include "libslic3r/BuildVolume.hpp"
|
||||||
#include "libslic3r/ClipperUtils.hpp"
|
#include "libslic3r/ClipperUtils.hpp"
|
||||||
@ -44,6 +45,7 @@
|
|||||||
#include "NotificationManager.hpp"
|
#include "NotificationManager.hpp"
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
|
|
||||||
|
#include "slic3r/GUI/BitmapCache.hpp"
|
||||||
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
|
||||||
#include "slic3r/Utils/UndoRedo.hpp"
|
#include "slic3r/Utils/UndoRedo.hpp"
|
||||||
|
|
||||||
@ -73,6 +75,7 @@
|
|||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
#include <boost/crc.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
@ -1823,6 +1826,242 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
|
|||||||
m_volumes.update_colors_by_extruder(m_config);
|
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()
|
void GLCanvas3D::render()
|
||||||
{
|
{
|
||||||
if (m_in_render) {
|
if (m_in_render) {
|
||||||
@ -1964,34 +2203,31 @@ void GLCanvas3D::render()
|
|||||||
if (m_picking_enabled && m_rectangle_selection.is_dragging())
|
if (m_picking_enabled && m_rectangle_selection.is_dragging())
|
||||||
m_rectangle_selection.render(*this);
|
m_rectangle_selection.render(*this);
|
||||||
} else {
|
} else {
|
||||||
// Autoslicing.
|
const auto &prints{
|
||||||
// Render the combined statistics if all is ready.
|
tcb::span{wxGetApp().plater()->get_fff_prints()}
|
||||||
bool valid = true;
|
.subspan(0, s_multiple_beds.get_number_of_beds())
|
||||||
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();
|
|
||||||
|
|
||||||
if (!valid) {
|
const bool all_finished{std::all_of(
|
||||||
if (fff_print()->finished())
|
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();
|
s_multiple_beds.autoslice_next_bed();
|
||||||
else
|
} else {
|
||||||
wxGetApp().plater()->schedule_background_process();
|
wxGetApp().plater()->schedule_background_process();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
render_print_statistics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_render_overlays();
|
_render_overlays();
|
||||||
|
|
||||||
_render_bed_selector();
|
_render_bed_selector();
|
||||||
@ -2537,7 +2773,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
|||||||
#endif // SLIC3R_OPENGL_ES
|
#endif // SLIC3R_OPENGL_ES
|
||||||
const BoundingBoxf3& bb = volume->bounding_box();
|
const BoundingBoxf3& bb = volume->bounding_box();
|
||||||
m_wipe_tower_bounding_boxes[bed_idx] = BoundingBoxf{to_2d(bb.min), to_2d(bb.max)};
|
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);
|
m_volumes.volumes.emplace_back(volume);
|
||||||
const auto volume_idx_wipe_tower_new{static_cast<int>(m_volumes.volumes.size() - 1)};
|
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);
|
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
|
#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()
|
void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
||||||
{
|
{
|
||||||
static float btn_side = 80.f;
|
static float btn_side = 80.f;
|
||||||
static float btn_border = 2.f;
|
static float btn_border = 2.f;
|
||||||
static bool hide_title = true;
|
|
||||||
|
|
||||||
ImVec2 btn_size = ImVec2(btn_side, btn_side);
|
ImVec2 btn_size = ImVec2(btn_side, btn_side);
|
||||||
|
|
||||||
if (s_multiple_beds.get_number_of_beds() != 1 && wxGetApp().plater()->is_preview_shown()) {
|
bool extra_frame{ false };
|
||||||
auto render_bed_button = [btn_size, this](int i)
|
|
||||||
{
|
|
||||||
//ImGui::Text("%d", i);
|
|
||||||
//ImGui::SameLine();
|
|
||||||
|
|
||||||
|
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 empty = ! s_multiple_beds.is_bed_occupied(i);
|
||||||
bool inactive = i != s_multiple_beds.get_active_bed() || s_multiple_beds.is_autoslicing();
|
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;
|
bool clicked = false;
|
||||||
ImVec2 btn_padding = ImVec2(btn_border, btn_border);
|
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);
|
clicked = ImGui::Button(empty ? "empty" : std::to_string(i + 1).c_str(), btn_size + btn_padding);
|
||||||
else
|
} else {
|
||||||
clicked = ImGui::ImageButton((void*)(int64_t)s_th_tex_id[i], btn_size - btn_padding, ImVec2(0, 1), ImVec2(1, 0), btn_border);
|
clicked = bed_selector_thumbnail(
|
||||||
|
btn_size,
|
||||||
|
btn_padding,
|
||||||
|
btn_side,
|
||||||
|
btn_border,
|
||||||
|
s_bed_selector_thumbnail_texture_ids[i],
|
||||||
|
print_status
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (clicked && ! empty)
|
if (clicked && ! empty)
|
||||||
select_bed(i, true);
|
select_bed(i, true);
|
||||||
@ -6367,15 +6690,9 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
|||||||
if (empty)
|
if (empty)
|
||||||
ImGui::PopItemFlag();
|
ImGui::PopItemFlag();
|
||||||
|
|
||||||
//std::string status_text;
|
const std::string status_text{get_status_text(print_status)};
|
||||||
//if (wxGetApp().plater()->get_fff_prints()[i]->finished())
|
if (ImGui::IsItemHovered())
|
||||||
// status_text = "Finished";
|
ImGui::SetTooltip("%s", status_text.c_str());
|
||||||
//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());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||||
@ -6398,11 +6715,11 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
|||||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2());
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2());
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2());
|
||||||
|
|
||||||
// Disable for now.
|
if (imgui.image_button(ImGui::SliceAllBtnIcon, "Slice All")) {
|
||||||
//if (imgui.image_button(ImGui::SliceAllBtnIcon, "Slice All")) {
|
if (!s_multiple_beds.is_autoslicing())
|
||||||
// if (!s_multiple_beds.is_autoslicing())
|
s_multiple_beds.start_autoslice([this](int i, bool user) { this->select_bed(i, user); });
|
||||||
// s_multiple_beds.start_autoslice([this](int i, bool user) { this->select_bed(i, user); });
|
}
|
||||||
//}
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, btn_border);
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, btn_border);
|
||||||
@ -6418,8 +6735,6 @@ void Slic3r::GUI::GLCanvas3D::_render_bed_selector()
|
|||||||
ImGui::PopStyleVar(3);
|
ImGui::PopStyleVar(3);
|
||||||
|
|
||||||
#if use_scrolling
|
#if use_scrolling
|
||||||
bool extra_frame{ false };
|
|
||||||
|
|
||||||
ImVec2 win_size = ImGui::GetCurrentWindow()->ContentSizeIdeal +
|
ImVec2 win_size = ImGui::GetCurrentWindow()->ContentSizeIdeal +
|
||||||
ImGui::GetCurrentWindow()->WindowPadding * 2.f +
|
ImGui::GetCurrentWindow()->WindowPadding * 2.f +
|
||||||
ImGui::GetCurrentWindow()->ScrollbarSizes +
|
ImGui::GetCurrentWindow()->ScrollbarSizes +
|
||||||
|
@ -113,6 +113,9 @@ static const std::map<const wchar_t, std::string> font_icons = {
|
|||||||
{ImGui::HorizontalHide , "horizontal_hide" },
|
{ImGui::HorizontalHide , "horizontal_hide" },
|
||||||
{ImGui::HorizontalShow , "horizontal_show" },
|
{ImGui::HorizontalShow , "horizontal_show" },
|
||||||
{ImGui::SliceAllBtnIcon , "slice_all" },
|
{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 = {
|
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
|
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||||||
///|/
|
///|/
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
|
#include "slic3r/GUI/BitmapCache.hpp"
|
||||||
#include "slic3r/GUI/Jobs/UIThreadWorker.hpp"
|
#include "slic3r/GUI/Jobs/UIThreadWorker.hpp"
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <nanosvgrast.h>
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -155,8 +157,6 @@ using Slic3r::GUI::format_wxstr;
|
|||||||
|
|
||||||
static const std::pair<unsigned int, unsigned int> THUMBNAIL_SIZE_3MF = { 256, 256 };
|
static const std::pair<unsigned int, unsigned int> THUMBNAIL_SIZE_3MF = { 256, 256 };
|
||||||
|
|
||||||
std::vector<GLuint> s_th_tex_id;
|
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
namespace GUI {
|
namespace GUI {
|
||||||
|
|
||||||
@ -603,7 +603,6 @@ private:
|
|||||||
// vector of all warnings generated by last slicing
|
// vector of all warnings generated by last slicing
|
||||||
std::vector<std::pair<Slic3r::PrintStateBase::Warning, size_t>> current_warnings;
|
std::vector<std::pair<Slic3r::PrintStateBase::Warning, size_t>> current_warnings;
|
||||||
bool show_warning_dialog { false };
|
bool show_warning_dialog { false };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::regex Plater::priv::pattern_bundle(".*[.](amf|amf[.]xml|zip[.]amf|3mf|prusa)", std::regex::icase);
|
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.
|
// Update background processing thread from the current config and Model.
|
||||||
// Returns a bitmask of UpdateBackgroundProcessReturnState.
|
// 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.set_gcode_result(&gcode_results[active_bed]);
|
||||||
background_process.select_technology(this->printer_technology);
|
background_process.select_technology(this->printer_technology);
|
||||||
|
|
||||||
|
|
||||||
if (s_beds_just_switched && printer_technology == ptFFF) {
|
if (s_beds_just_switched && printer_technology == ptFFF) {
|
||||||
PrintBase::SlicingStatus status(q->active_fff_print(), -1);
|
PrintBase::SlicingStatus status(q->active_fff_print(), -1);
|
||||||
SlicingStatusEvent evt(EVT_SLICING_UPDATE, 0, status);
|
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);
|
notification_manager->close_notification_of_type(NotificationType::ExportOngoing);
|
||||||
q->sidebar().show_sliced_info_sizer(background_process.finished());
|
q->sidebar().show_sliced_info_sizer(background_process.finished());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// bitmap of enum UpdateBackgroundProcessReturnState
|
// bitmap of enum UpdateBackgroundProcessReturnState
|
||||||
unsigned int return_state = 0;
|
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.
|
// 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();
|
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 the "out of print bed" state of ModelInstances.
|
||||||
update_print_volume_state();
|
update_print_volume_state();
|
||||||
|
|
||||||
// Move all instances according to their active bed:
|
Print::ApplyStatus invalidated{Print::ApplyStatus::APPLY_STATUS_INVALIDATED};
|
||||||
s_multiple_beds.move_active_to_first_bed(q->model(), q->build_volume(), true);
|
bool was_running = background_process.running();
|
||||||
|
using MultipleBedsUtils::with_single_bed_model;
|
||||||
|
|
||||||
// Apply new config to the possibly running background task.
|
std::array<Print::ApplyStatus, MAX_NUMBER_OF_BEDS> apply_statuses{
|
||||||
bool was_running = background_process.running();
|
apply_to_inactive_beds(q->model(), q->p->fff_prints, full_config)
|
||||||
Print::ApplyStatus invalidated = background_process.apply(q->model(), 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.
|
const bool any_status_changed{std::any_of(
|
||||||
s_multiple_beds.move_active_to_first_bed(q->model(), q->build_volume(), false);
|
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 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;
|
ThumbnailData data;
|
||||||
ThumbnailsParams params;
|
ThumbnailsParams params;
|
||||||
params.parts_only = true;
|
params.parts_only = true;
|
||||||
@ -2236,19 +2267,21 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
|
|||||||
int curr_unpack_alignment = 0;
|
int curr_unpack_alignment = 0;
|
||||||
glsafe(glGetIntegerv(GL_UNPACK_ALIGNMENT, &curr_unpack_alignment));
|
glsafe(glGetIntegerv(GL_UNPACK_ALIGNMENT, &curr_unpack_alignment));
|
||||||
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, 1));
|
||||||
glsafe(glDeleteTextures(s_th_tex_id.size(), 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_th_tex_id.resize(num);
|
|
||||||
glsafe(glGenTextures(num, s_th_tex_id.data()));
|
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) {
|
for (int i = 0; i < num; ++i) {
|
||||||
s_multiple_beds.set_thumbnail_bed_idx(i);
|
s_multiple_beds.set_thumbnail_bed_idx(i);
|
||||||
generate_thumbnail(data, w, h, params, GUI::Camera::EType::Ortho);
|
generate_thumbnail(data, w, h, params, GUI::Camera::EType::Ortho);
|
||||||
s_multiple_beds.set_thumbnail_bed_idx(-1);
|
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_MIN_FILTER, GL_NEAREST));
|
||||||
glsafe(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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(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()));
|
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(glBindTexture(GL_TEXTURE_2D, curr_bound_texture));
|
||||||
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, curr_unpack_alignment));
|
glsafe(glPixelStorei(GL_UNPACK_ALIGNMENT, curr_unpack_alignment));
|
||||||
@ -2411,12 +2444,15 @@ bool Plater::priv::restart_background_process(unsigned int state)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( ! this->background_process.empty() &&
|
if (
|
||||||
(state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0 &&
|
!this->background_process.empty()
|
||||||
( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) ||
|
&& (state & priv::UPDATE_BACKGROUND_PROCESS_INVALID) == 0
|
||||||
(state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 ||
|
&& (
|
||||||
(state & UPDATE_BACKGROUND_PROCESS_RESTART) != 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.
|
// The print is valid and it can be started.
|
||||||
|
|
||||||
if (this->background_process.start()) {
|
if (this->background_process.start()) {
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||||
#include "Jobs/Job.hpp"
|
#include "Jobs/Job.hpp"
|
||||||
#include "Jobs/Worker.hpp"
|
#include "Jobs/Worker.hpp"
|
||||||
|
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||||
|
#include "slic3r/GUI/Camera.hpp"
|
||||||
|
|
||||||
class wxString;
|
class wxString;
|
||||||
|
|
||||||
@ -118,6 +120,7 @@ public:
|
|||||||
void convert_gcode_to_binary();
|
void convert_gcode_to_binary();
|
||||||
void reload_print();
|
void reload_print();
|
||||||
void object_list_changed();
|
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);
|
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.
|
// 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