Merge branch 'master' into upstream2

This commit is contained in:
Oleksandra Yushchenko 2020-01-21 15:12:32 +01:00 committed by GitHub
commit 749a06a092
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
253 changed files with 61495 additions and 5223 deletions

View file

@ -189,9 +189,6 @@ void Bed3D::Axes::render_axis(double length) const
Bed3D::Bed3D()
: m_type(Custom)
, m_custom_texture("")
, m_custom_model("")
, m_requires_canvas_update(false)
, m_vbo_id(0)
, m_scale_factor(1.0f)
{
@ -199,33 +196,31 @@ Bed3D::Bed3D()
bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
{
EType new_type = detect_type(shape);
auto check_texture = [](const std::string& texture) {
return !texture.empty() && (boost::algorithm::iends_with(texture, ".png") || boost::algorithm::iends_with(texture, ".svg")) && boost::filesystem::exists(texture);
};
// check that the passed custom texture filename is valid
std::string cst_texture(custom_texture);
if (!cst_texture.empty())
{
std::replace(cst_texture.begin(), cst_texture.end(), '\\', '/');
if ((!boost::algorithm::iends_with(custom_texture, ".png") && !boost::algorithm::iends_with(custom_texture, ".svg")) || !boost::filesystem::exists(custom_texture))
cst_texture = "";
}
auto check_model = [](const std::string& model) {
return !model.empty() && boost::algorithm::iends_with(model, ".stl") && boost::filesystem::exists(model);
};
// check that the passed custom texture filename is valid
std::string cst_model(custom_model);
if (!cst_model.empty())
{
std::replace(cst_model.begin(), cst_model.end(), '\\', '/');
if (!boost::algorithm::iends_with(custom_model, ".stl") || !boost::filesystem::exists(custom_model))
cst_model = "";
}
auto [new_type, system_model, system_texture] = detect_type(shape);
if ((m_shape == shape) && (m_type == new_type) && (m_custom_texture == cst_texture) && (m_custom_model == cst_model))
std::string texture_filename = custom_texture.empty() ? system_texture : custom_texture;
if (!check_texture(texture_filename))
texture_filename.clear();
std::string model_filename = custom_model.empty() ? system_model : custom_model;
if (!check_model(model_filename))
model_filename.clear();
if ((m_shape == shape) && (m_type == new_type) && (m_texture_filename == texture_filename) && (m_model_filename == model_filename))
// No change, no need to update the UI.
return false;
m_shape = shape;
m_custom_texture = cst_texture;
m_custom_model = cst_model;
m_texture_filename = texture_filename;
m_model_filename = model_filename;
m_type = new_type;
calc_bounding_boxes();
@ -247,7 +242,7 @@ bool Bed3D::set_shape(const Pointfs& shape, const std::string& custom_texture, c
m_texture.reset();
m_model.reset();
// Set the origin and size for painting of the coordinate system axes.
// Set the origin and size for rendering the coordinate system axes.
m_axes.origin = Vec3d(0.0, 0.0, (double)GROUND_Z);
m_axes.length = 0.1 * m_bounding_box.max_size() * Vec3d::Ones();
@ -265,7 +260,11 @@ Point Bed3D::point_projection(const Point& point) const
return m_polygon.point_projection(point);
}
#if ENABLE_6DOF_CAMERA
void Bed3D::render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const
#else
void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor, bool show_axes) const
#endif // ENABLE_6DOF_CAMERA
{
m_scale_factor = scale_factor;
@ -276,13 +275,15 @@ void Bed3D::render(GLCanvas3D& canvas, float theta, float scale_factor, bool sho
switch (m_type)
{
case MK2: { render_prusa(canvas, "mk2", theta > 90.0f); break; }
case MK3: { render_prusa(canvas, "mk3", theta > 90.0f); break; }
case SL1: { render_prusa(canvas, "sl1", theta > 90.0f); break; }
case MINI: { render_prusa(canvas, "mini", theta > 90.0f); break; }
case ENDER3: { render_prusa(canvas, "ender3", theta > 90.0f); break; }
#if ENABLE_6DOF_CAMERA
case System: { render_system(canvas, bottom); break; }
default:
case Custom: { render_custom(canvas, bottom); break; }
#else
case System: { render_system(canvas, theta > 90.0f); break; }
default:
case Custom: { render_custom(canvas, theta > 90.0f); break; }
#endif // ENABLE_6DOF_CAMERA
}
glsafe(::glDisable(GL_DEPTH_TEST));
@ -344,10 +345,26 @@ void Bed3D::calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox)
printf("Unable to create bed grid lines\n");
}
Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const
static std::string system_print_bed_model(const Preset &preset)
{
EType type = Custom;
std::string out;
const VendorProfile::PrinterModel *pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && ! pm->bed_model.empty())
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_model;
return out;
}
static std::string system_print_bed_texture(const Preset &preset)
{
std::string out;
const VendorProfile::PrinterModel *pm = PresetUtils::system_printer_model(preset);
if (pm != nullptr && ! pm->bed_texture.empty())
out = Slic3r::resources_dir() + "/profiles/" + preset.vendor->id + "/" + pm->bed_texture;
return out;
}
std::tuple<Bed3D::EType, std::string, std::string> Bed3D::detect_type(const Pointfs& shape) const
{
auto bundle = wxGetApp().preset_bundle;
if (bundle != nullptr)
{
@ -356,39 +373,12 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const
{
if (curr->config.has("bed_shape"))
{
if (curr->vendor != nullptr)
if (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values)
{
if ((curr->vendor->name == "Prusa Research") && (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values))
{
if (boost::contains(curr->name, "SL1"))
{
type = SL1;
break;
}
else if (boost::contains(curr->name, "MK3") || boost::contains(curr->name, "MK2.5"))
{
type = MK3;
break;
}
else if (boost::contains(curr->name, "MK2"))
{
type = MK2;
break;
}
else if (boost::contains(curr->name, "MINI"))
{
type = MINI;
break;
}
}
else if ((curr->vendor->name == "Creality") && (shape == dynamic_cast<const ConfigOptionPoints*>(curr->config.option("bed_shape"))->values))
{
if (boost::contains(curr->name, "ENDER-3"))
{
type = ENDER3;
break;
}
}
std::string model_filename = system_print_bed_model(*curr);
std::string texture_filename = system_print_bed_texture(*curr);
if (!model_filename.empty() && !texture_filename.empty())
return std::make_tuple(System, model_filename, texture_filename);
}
}
@ -396,7 +386,7 @@ Bed3D::EType Bed3D::detect_type(const Pointfs& shape) const
}
}
return type;
return std::make_tuple(Custom, "", "");
}
void Bed3D::render_axes() const
@ -405,62 +395,64 @@ void Bed3D::render_axes() const
m_axes.render();
}
void Bed3D::render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const
void Bed3D::render_system(GLCanvas3D& canvas, bool bottom) const
{
if (!bottom)
render_model(m_custom_model.empty() ? resources_dir() + "/models/" + key + "_bed.stl" : m_custom_model);
render_model();
render_texture(m_custom_texture.empty() ? resources_dir() + "/icons/bed/" + key + ".svg" : m_custom_texture, bottom, canvas);
render_texture(bottom, canvas);
}
void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const
void Bed3D::render_texture(bool bottom, GLCanvas3D& canvas) const
{
if (filename.empty())
if (m_texture_filename.empty())
{
m_texture.reset();
render_default(bottom);
return;
}
if ((m_texture.get_id() == 0) || (m_texture.get_source() != filename))
if ((m_texture.get_id() == 0) || (m_texture.get_source() != m_texture_filename))
{
m_texture.reset();
if (boost::algorithm::iends_with(filename, ".svg"))
if (boost::algorithm::iends_with(m_texture_filename, ".svg"))
{
// use higher resolution images if graphic card and opengl version allow
GLint max_tex_size = GLCanvas3DManager::get_gl_info().get_max_tex_size();
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename))
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename))
{
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if (!m_temp_texture.load_from_svg_file(filename, false, false, false, max_tex_size / 8))
if (!m_temp_texture.load_from_svg_file(m_texture_filename, false, false, false, max_tex_size / 8))
{
render_default(bottom);
return;
}
canvas.request_extra_frame();
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_svg_file(filename, true, true, true, max_tex_size))
if (!m_texture.load_from_svg_file(m_texture_filename, true, true, true, max_tex_size))
{
render_default(bottom);
return;
}
}
else if (boost::algorithm::iends_with(filename, ".png"))
else if (boost::algorithm::iends_with(m_texture_filename, ".png"))
{
// generate a temporary lower resolution texture to show while no main texture levels have been compressed
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != filename))
if ((m_temp_texture.get_id() == 0) || (m_temp_texture.get_source() != m_texture_filename))
{
if (!m_temp_texture.load_from_file(filename, false, GLTexture::None, false))
if (!m_temp_texture.load_from_file(m_texture_filename, false, GLTexture::None, false))
{
render_default(bottom);
return;
}
canvas.request_extra_frame();
}
// starts generating the main texture, compression will run asynchronously
if (!m_texture.load_from_file(filename, true, GLTexture::MultiThreaded, true))
if (!m_texture.load_from_file(m_texture_filename, true, GLTexture::MultiThreaded, true))
{
render_default(bottom);
return;
@ -481,13 +473,9 @@ void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D&
if (m_temp_texture.get_id() != 0)
m_temp_texture.reset();
m_requires_canvas_update = true;
}
else if (m_requires_canvas_update && m_texture.all_compressed_data_sent_to_gpu())
m_requires_canvas_update = false;
canvas.request_extra_frame();
if (m_texture.all_compressed_data_sent_to_gpu() && canvas.is_keeping_dirty())
canvas.stop_keeping_dirty();
}
if (m_triangles.get_vertices_count() > 0)
{
@ -563,12 +551,12 @@ void Bed3D::render_texture(const std::string& filename, bool bottom, GLCanvas3D&
}
}
void Bed3D::render_model(const std::string& filename) const
void Bed3D::render_model() const
{
if (filename.empty())
if (m_model_filename.empty())
return;
if ((m_model.get_filename() != filename) && m_model.init_from_file(filename))
if ((m_model.get_filename() != m_model_filename) && m_model.init_from_file(m_model_filename))
{
// move the model so that its origin (0.0, 0.0, 0.0) goes into the bed shape center and a bit down to avoid z-fighting with the texture quad
Vec3d shift = m_bounding_box.center();
@ -589,16 +577,16 @@ void Bed3D::render_model(const std::string& filename) const
void Bed3D::render_custom(GLCanvas3D& canvas, bool bottom) const
{
if (m_custom_texture.empty() && m_custom_model.empty())
if (m_texture_filename.empty() && m_model_filename.empty())
{
render_default(bottom);
return;
}
if (!bottom)
render_model(m_custom_model);
render_model();
render_texture(m_custom_texture, bottom, canvas);
render_texture(bottom, canvas);
}
void Bed3D::render_default(bool bottom) const

View file

@ -5,6 +5,8 @@
#include "3DScene.hpp"
#include "GLShader.hpp"
#include <tuple>
class GLUquadric;
typedef class GLUquadric GLUquadricObj;
@ -64,11 +66,7 @@ class Bed3D
public:
enum EType : unsigned char
{
MK2,
MK3,
SL1,
MINI,
ENDER3,
System,
Custom,
Num_Types
};
@ -76,21 +74,19 @@ public:
private:
EType m_type;
Pointfs m_shape;
std::string m_custom_texture;
std::string m_custom_model;
std::string m_texture_filename;
std::string m_model_filename;
mutable BoundingBoxf3 m_bounding_box;
mutable BoundingBoxf3 m_extended_bounding_box;
Polygon m_polygon;
GeometryBuffer m_triangles;
GeometryBuffer m_gridlines;
mutable GLTexture m_texture;
mutable GLBed m_model;
// temporary texture shown until the main texture has still no levels compressed
mutable GLTexture m_temp_texture;
// used to trigger 3D scene update once all compressed textures have been sent to GPU
mutable bool m_requires_canvas_update;
mutable Shader m_shader;
mutable unsigned int m_vbo_id;
mutable GLBed m_model;
Axes m_axes;
mutable float m_scale_factor;
@ -101,7 +97,6 @@ public:
EType get_type() const { return m_type; }
bool is_prusa() const { return (m_type == MK2) || (m_type == MK3) || (m_type == SL1); }
bool is_custom() const { return m_type == Custom; }
const Pointfs& get_shape() const { return m_shape; }
@ -112,17 +107,21 @@ public:
bool contains(const Point& point) const;
Point point_projection(const Point& point) const;
#if ENABLE_6DOF_CAMERA
void render(GLCanvas3D& canvas, bool bottom, float scale_factor, bool show_axes) const;
#else
void render(GLCanvas3D& canvas, float theta, float scale_factor, bool show_axes) const;
#endif // ENABLE_6DOF_CAMERA
private:
void calc_bounding_boxes() const;
void calc_triangles(const ExPolygon& poly);
void calc_gridlines(const ExPolygon& poly, const BoundingBox& bed_bbox);
EType detect_type(const Pointfs& shape) const;
std::tuple<EType, std::string, std::string> detect_type(const Pointfs& shape) const;
void render_axes() const;
void render_prusa(GLCanvas3D& canvas, const std::string& key, bool bottom) const;
void render_texture(const std::string& filename, bool bottom, GLCanvas3D& canvas) const;
void render_model(const std::string& filename) const;
void render_system(GLCanvas3D& canvas, bool bottom) const;
void render_texture(bool bottom, GLCanvas3D& canvas) const;
void render_model() const;
void render_custom(GLCanvas3D& canvas, bool bottom) const;
void render_default(bool bottom) const;
void reset();

View file

@ -877,13 +877,10 @@ bool can_export_to_obj(const GLVolume& volume)
if (!volume.is_active || !volume.is_extrusion_path)
return false;
if (volume.indexed_vertex_array.triangle_indices.empty() && (std::min(volume.indexed_vertex_array.triangle_indices_size, volume.tverts_range.second - volume.tverts_range.first) == 0))
return false;
bool has_triangles = !volume.indexed_vertex_array.triangle_indices.empty() || (std::min(volume.indexed_vertex_array.triangle_indices_size, volume.tverts_range.second - volume.tverts_range.first) > 0);
bool has_quads = !volume.indexed_vertex_array.quad_indices.empty() || (std::min(volume.indexed_vertex_array.quad_indices_size, volume.qverts_range.second - volume.qverts_range.first) > 0);
if (volume.indexed_vertex_array.quad_indices.empty() && (std::min(volume.indexed_vertex_array.quad_indices_size, volume.qverts_range.second - volume.qverts_range.first) == 0))
return false;
return true;
return has_triangles || has_quads;
}
bool GLVolumeCollection::has_toolpaths_to_export() const

View file

@ -303,6 +303,8 @@ public:
int instance_id;
bool operator==(const CompositeID &rhs) const { return object_id == rhs.object_id && volume_id == rhs.volume_id && instance_id == rhs.instance_id; }
bool operator!=(const CompositeID &rhs) const { return ! (*this == rhs); }
bool operator< (const CompositeID &rhs) const
{ return object_id < rhs.object_id || (object_id == rhs.object_id && (volume_id < rhs.volume_id || (volume_id == rhs.volume_id && instance_id < rhs.instance_id))); }
};
CompositeID composite_id;
// Fingerprint of the source geometry. For ModelVolumes, it is the ModelVolume::ID and ModelInstanceID,

View file

@ -61,6 +61,11 @@ void AppConfig::set_defaults()
if (get("preset_update").empty())
set("preset_update", "1");
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
if (get("export_sources_full_pathnames").empty())
set("export_sources_full_pathnames", "0");
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// remove old 'use_legacy_opengl' parameter from this config, if present
if (!get("use_legacy_opengl").empty())
erase("", "use_legacy_opengl");
@ -73,6 +78,9 @@ void AppConfig::set_defaults()
if (get("remember_output_path").empty())
set("remember_output_path", "1");
if (get("remember_output_path_removable").empty())
set("remember_output_path_removable", "1");
if (get("use_custom_toolbar_size").empty())
set("use_custom_toolbar_size", "0");
@ -82,6 +90,11 @@ void AppConfig::set_defaults()
if (get("use_perspective_camera").empty())
set("use_perspective_camera", "1");
#if ENABLE_6DOF_CAMERA
if (get("use_free_camera").empty())
set("use_free_camera", "0");
#endif // ENABLE_6DOF_CAMERA
// Remove legacy window positions/sizes
erase("", "main_frame_maximized");
erase("", "main_frame_pos");
@ -271,7 +284,11 @@ void AppConfig::set_recent_projects(const std::vector<std::string>& recent_proje
}
}
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed)
#else
void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone)
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
{
std::string key = std::string("mouse_device:") + name;
auto it = m_storage.find(key);
@ -283,6 +300,9 @@ void AppConfig::set_mouse_device(const std::string& name, double translation_spe
it->second["translation_deadzone"] = std::to_string(translation_deadzone);
it->second["rotation_speed"] = std::to_string(rotation_speed);
it->second["rotation_deadzone"] = std::to_string(rotation_deadzone);
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
it->second["zoom_speed"] = std::to_string(zoom_speed);
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
}
bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed)
@ -345,6 +365,23 @@ bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, floa
return true;
}
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
bool AppConfig::get_mouse_device_zoom_speed(const std::string& name, double& speed)
{
std::string key = std::string("mouse_device:") + name;
auto it = m_storage.find(key);
if (it == m_storage.end())
return false;
auto it_val = it->second.find("zoom_speed");
if (it_val == it->second.end())
return false;
speed = (float)::atof(it_val->second.c_str());
return true;
}
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
void AppConfig::update_config_dir(const std::string &dir)
{
this->set("recent", "config_directory", dir);
@ -354,9 +391,10 @@ void AppConfig::update_skein_dir(const std::string &dir)
{
this->set("recent", "skein_directory", dir);
}
/*
std::string AppConfig::get_last_output_dir(const std::string &alt) const
{
const auto it = m_storage.find("");
if (it != m_storage.end()) {
const auto it2 = it->second.find("last_output_path");
@ -371,6 +409,26 @@ void AppConfig::update_last_output_dir(const std::string &dir)
{
this->set("", "last_output_path", dir);
}
*/
std::string AppConfig::get_last_output_dir(const std::string& alt, const bool removable) const
{
std::string s1 = (removable ? "last_output_path_removable" : "last_output_path");
std::string s2 = (removable ? "remember_output_path_removable" : "remember_output_path");
const auto it = m_storage.find("");
if (it != m_storage.end()) {
const auto it2 = it->second.find(s1);
const auto it3 = it->second.find(s2);
if (it2 != it->second.end() && it3 != it->second.end() && !it2->second.empty() && it3->second == "1")
return it2->second;
}
return alt;
}
void AppConfig::update_last_output_dir(const std::string& dir, const bool removable)
{
this->set("", (removable ? "last_output_path_removable" : "last_output_path"), dir);
}
void AppConfig::reset_selections()
{

View file

@ -102,8 +102,10 @@ public:
void update_config_dir(const std::string &dir);
void update_skein_dir(const std::string &dir);
std::string get_last_output_dir(const std::string &alt) const;
void update_last_output_dir(const std::string &dir);
//std::string get_last_output_dir(const std::string &alt) const;
//void update_last_output_dir(const std::string &dir);
std::string get_last_output_dir(const std::string& alt, const bool removable = false) const;
void update_last_output_dir(const std::string &dir, const bool removable = false);
// reset the current print / filament / printer selections, so that
// the PresetBundle::load_selections(const AppConfig &config) call will select
@ -131,11 +133,18 @@ public:
std::vector<std::string> get_recent_projects() const;
void set_recent_projects(const std::vector<std::string>& recent_projects);
void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone);
bool get_mouse_device_translation_speed(const std::string& name, double& speed);
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone, double zoom_speed);
#else
void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone);
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
bool get_mouse_device_translation_speed(const std::string& name, double& speed);
bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone);
bool get_mouse_device_rotation_speed(const std::string& name, float& speed);
bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone);
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
bool get_mouse_device_zoom_speed(const std::string& name, double& speed);
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
static const std::string SECTION_FILAMENTS;
static const std::string SECTION_MATERIALS;

View file

@ -34,6 +34,7 @@
#include <boost/nowide/cstdio.hpp>
#include "I18N.hpp"
#include "GUI.hpp"
#include "RemovableDriveManager.hpp"
namespace Slic3r {
@ -94,20 +95,28 @@ void BackgroundSlicingProcess::process_fff()
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
#endif // ENABLE_THUMBNAIL_GENERATOR
if (m_fff_print->model().custom_gcode_per_height != GUI::wxGetApp().model().custom_gcode_per_height) {
GUI::wxGetApp().model().custom_gcode_per_height = m_fff_print->model().custom_gcode_per_height;
// #ys_FIXME : controll text
GUI::show_info(nullptr, _(L("To except of redundant tool manipulation, \n"
"Color change(s) for unused extruder(s) was(were) deleted")), _(L("Info")));
}
if (this->set_step_started(bspsGCodeFinalize)) {
if (! m_export_path.empty()) {
//FIXME localize the messages
// Perform the final post-processing of the export path by applying the print statistics over the file name.
std::string export_path = m_fff_print->print_statistics().finalize_output_path(m_export_path);
if (copy_file(m_temp_output_path, export_path) != 0)
GUI::RemovableDriveManager::get_instance().update();
bool with_check = GUI::RemovableDriveManager::get_instance().is_path_on_removable_drive(export_path);
int copy_ret_val = copy_file(m_temp_output_path, export_path, with_check);
if (with_check && copy_ret_val == -2)
{
std::string err_msg = "Copying of the temporary G-code to the output G-code failed. There might be problem with target device, please try exporting again or using different device. The corrupted output G-code is at " + export_path + ".tmp.";
throw std::runtime_error(_utf8(L(err_msg)));
}
else if (copy_ret_val == -3)
{
std::string err_msg = "Renaming of the G-code after copying to the selected destination folder has failed. Current path is " + export_path + ".tmp. Please try exporting again.";
throw std::runtime_error(_utf8(L(err_msg)));
}
else if ( copy_ret_val != 0)
{
throw std::runtime_error(_utf8(L("Copying of the temporary G-code to the output G-code failed. Maybe the SD card is write locked?")));
}
m_print->set_status(95, _utf8(L("Running post-processing scripts")));
run_post_process_scripts(export_path, m_fff_print->config());
m_print->set_status(100, (boost::format(_utf8(L("G-code file exported to %1%"))) % export_path).str());

View file

@ -53,11 +53,11 @@ public:
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
#endif // ENABLE_THUMBNAIL_GENERATOR
// The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the slicing is finished
// and the background processing will transition into G-code export.
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
void set_slicing_completed_event(int event_id) { m_event_slicing_completed_id = event_id; }
// The following wxCommandEvent will be sent to the UI thread / Platter window, when the G-code export is finished.
// The following wxCommandEvent will be sent to the UI thread / Plater window, when the G-code export is finished.
// The wxCommandEvent is sent to the UI thread asynchronously without waiting for the event to be processed.
void set_finished_event(int event_id) { m_event_finished_id = event_id; }
@ -132,11 +132,6 @@ public:
// This "finished" flag does not account for the final export of the output file (.gcode or zipped PNGs),
// and it does not account for the OctoPrint scheduling.
bool finished() const { return m_print->finished(); }
void set_force_update_print_regions(bool force_update_print_regions) {
if (m_fff_print)
m_fff_print->set_force_update_print_regions(force_update_print_regions);
}
private:
void thread_proc();
@ -191,9 +186,9 @@ private:
void throw_if_canceled() const { if (m_print->canceled()) throw CanceledException(); }
void prepare_upload();
// wxWidgets command ID to be sent to the platter to inform that the slicing is finished, and the G-code export will continue.
// wxWidgets command ID to be sent to the plater to inform that the slicing is finished, and the G-code export will continue.
int m_event_slicing_completed_id = 0;
// wxWidgets command ID to be sent to the platter to inform that the task finished.
// wxWidgets command ID to be sent to the plater to inform that the task finished.
int m_event_finished_id = 0;
};

View file

@ -61,9 +61,7 @@ void BedShapePanel::build_panel(const ConfigOptionPoints& default_pt, const Conf
{
m_shape = default_pt.values;
m_custom_texture = custom_texture.value.empty() ? NONE : custom_texture.value;
std::replace(m_custom_texture.begin(), m_custom_texture.end(), '\\', '/');
m_custom_model = custom_model.value.empty() ? NONE : custom_model.value;
std::replace(m_custom_model.begin(), m_custom_model.end(), '\\', '/');
auto sbsizer = new wxStaticBoxSizer(wxVERTICAL, this, _(L("Shape")));
sbsizer->GetStaticBox()->SetFont(wxGetApp().bold_font());
@ -546,8 +544,6 @@ void BedShapePanel::load_texture()
return;
}
std::replace(file_name.begin(), file_name.end(), '\\', '/');
wxBusyCursor wait;
m_custom_texture = file_name;
@ -571,8 +567,6 @@ void BedShapePanel::load_model()
return;
}
std::replace(file_name.begin(), file_name.end(), '\\', '/');
wxBusyCursor wait;
m_custom_model = file_name;

View file

@ -1,6 +1,7 @@
#include "BitmapCache.hpp"
#include "libslic3r/Utils.hpp"
#include <boost/filesystem.hpp>
#if ! defined(WIN32) && ! defined(__APPLE__)
#define BROKEN_ALPHA
@ -15,7 +16,7 @@
#include "nanosvg/nanosvg.h"
#define NANOSVGRAST_IMPLEMENTATION
#include "nanosvg/nanosvgrast.h"
#include "GUI_App.hpp"
//#include "GUI_App.hpp"
namespace Slic3r { namespace GUI {
@ -226,7 +227,7 @@ wxBitmap* BitmapCache::load_png(const std::string &bitmap_name, unsigned width,
}
wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_width, unsigned target_height,
float scale /* = 1.0f */, const bool grayscale/* = false*/)
float scale /* = 1.0f */, const bool grayscale/* = false*/, const bool dark_mode/* = false*/)
{
std::string bitmap_key = bitmap_name + ( target_height !=0 ?
"-h" + std::to_string(target_height) :
@ -234,16 +235,45 @@ wxBitmap* BitmapCache::load_svg(const std::string &bitmap_name, unsigned target_
+ (scale != 1.0f ? "-s" + std::to_string(scale) : "")
+ (grayscale ? "-gs" : "");
target_height != 0 ? target_height *= scale : target_width *= scale;
/* For the Dark mode of any platform, we should draw icons in respect to OS background
* Note: All standard(regular) icons are collected in "icons" folder,
* SVG-icons, which have "Dark mode" variant, are collected in "icons/white" folder
*/
std::string folder;
if (dark_mode)
{
#ifdef __WXMSW__
folder = "white\\";
#else
folder = "white/";
#endif
auto it = m_map.find(folder + bitmap_key);
if (it != m_map.end())
return it->second;
else {
it = m_map.find(bitmap_key);
if (it != m_map.end())
return it->second;
}
auto it = m_map.find(bitmap_key);
if (it != m_map.end())
return it->second;
if (!boost::filesystem::exists(Slic3r::var(folder + bitmap_name + ".svg")))
folder.clear();
else
bitmap_key = folder + bitmap_key;
}
else
{
auto it = m_map.find(bitmap_key);
if (it != m_map.end())
return it->second;
}
NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(bitmap_name + ".svg").c_str(), "px", 96.0f);
NSVGimage *image = ::nsvgParseFromFile(Slic3r::var(folder + bitmap_name + ".svg").c_str(), "px", 96.0f);
if (image == nullptr)
return nullptr;
target_height != 0 ? target_height *= scale : target_width *= scale;
float svg_scale = target_height != 0 ?
(float)target_height / image->height : target_width != 0 ?
(float)target_width / image->width : 1;

View file

@ -34,7 +34,7 @@ public:
// Load png from resources/icons. bitmap_key is given without the .png suffix. Bitmap will be rescaled to provided height/width if nonzero.
wxBitmap* load_png(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, const bool grayscale = false);
// Load svg from resources/icons. bitmap_key is given without the .svg suffix. SVG will be rasterized to provided height/width.
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false);
wxBitmap* load_svg(const std::string &bitmap_key, unsigned width = 0, unsigned height = 0, float scale = 1.0f, const bool grayscale = false, const bool dark_mode = false);
static wxBitmap mksolid(size_t width, size_t height, unsigned char r, unsigned char g, unsigned char b, unsigned char transparency);
static wxBitmap mksolid(size_t width, size_t height, const unsigned char rgb[3]) { return mksolid(width, height, rgb[0], rgb[1], rgb[2], wxALPHA_OPAQUE); }

View file

@ -6,6 +6,11 @@
#endif // !ENABLE_THUMBNAIL_GENERATOR
#include "GUI_App.hpp"
#include "AppConfig.hpp"
#if ENABLE_CAMERA_STATISTICS
#if ENABLE_6DOF_CAMERA
#include "Mouse3DController.hpp"
#endif // ENABLE_6DOF_CAMERA
#endif // ENABLE_CAMERA_STATISTICS
#include <GL/glew.h>
@ -34,18 +39,27 @@ double Camera::FrustrumZMargin = 10.0;
double Camera::MaxFovDeg = 60.0;
Camera::Camera()
#if ENABLE_6DOF_CAMERA
: requires_zoom_to_bed(false)
#else
: phi(45.0f)
, requires_zoom_to_bed(false)
, inverted_phi(false)
#endif // ENABLE_6DOF_CAMERA
, m_type(Perspective)
, m_target(Vec3d::Zero())
#if !ENABLE_6DOF_CAMERA
, m_theta(45.0f)
#endif // !ENABLE_6DOF_CAMERA
, m_zoom(1.0)
, m_distance(DefaultDistance)
, m_gui_scale(1.0)
, m_view_matrix(Transform3d::Identity())
, m_projection_matrix(Transform3d::Identity())
{
#if ENABLE_6DOF_CAMERA
set_default_orientation();
#endif // ENABLE_6DOF_CAMERA
}
std::string Camera::get_type_as_string() const
@ -91,6 +105,9 @@ void Camera::select_next_type()
void Camera::set_target(const Vec3d& target)
{
#if ENABLE_6DOF_CAMERA
translate_world(target - m_target);
#else
BoundingBoxf3 test_box = m_scene_box;
test_box.translate(-m_scene_box.center());
// We may let this factor be customizable
@ -101,8 +118,10 @@ void Camera::set_target(const Vec3d& target)
m_target(0) = clamp(test_box.min(0), test_box.max(0), target(0));
m_target(1) = clamp(test_box.min(1), test_box.max(1), target(1));
m_target(2) = clamp(test_box.min(2), test_box.max(2), target(2));
#endif // ENABLE_6DOF_CAMERA
}
#if !ENABLE_6DOF_CAMERA
void Camera::set_theta(float theta, bool apply_limit)
{
if (apply_limit)
@ -114,6 +133,7 @@ void Camera::set_theta(float theta, bool apply_limit)
m_theta += 360.0f;
}
}
#endif // !ENABLE_6DOF_CAMERA
void Camera::update_zoom(double delta_zoom)
{
@ -123,14 +143,33 @@ void Camera::update_zoom(double delta_zoom)
void Camera::set_zoom(double zoom)
{
// Don't allow to zoom too far outside the scene.
double zoom_min = calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]);
double zoom_min = min_zoom();
if (zoom_min > 0.0)
zoom = std::max(zoom, zoom_min * 0.7);
zoom = std::max(zoom, zoom_min);
// Don't allow to zoom too close to the scene.
m_zoom = std::min(zoom, 100.0);
m_zoom = std::min(zoom, max_zoom());
}
#if ENABLE_6DOF_CAMERA
void Camera::select_view(const std::string& direction)
{
if (direction == "iso")
set_default_orientation();
else if (direction == "left")
m_view_matrix = look_at(m_target - m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
else if (direction == "right")
m_view_matrix = look_at(m_target + m_distance * Vec3d::UnitX(), m_target, Vec3d::UnitZ());
else if (direction == "top")
m_view_matrix = look_at(m_target + m_distance * Vec3d::UnitZ(), m_target, Vec3d::UnitY());
else if (direction == "bottom")
m_view_matrix = look_at(m_target - m_distance * Vec3d::UnitZ(), m_target, -Vec3d::UnitY());
else if (direction == "front")
m_view_matrix = look_at(m_target - m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
else if (direction == "rear")
m_view_matrix = look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ());
}
#else
bool Camera::select_view(const std::string& direction)
{
const float* dir_vec = nullptr;
@ -159,6 +198,7 @@ bool Camera::select_view(const std::string& direction)
else
return false;
}
#endif // ENABLE_6DOF_CAMERA
double Camera::get_fov() const
{
@ -180,20 +220,26 @@ void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) const
void Camera::apply_view_matrix() const
{
#if !ENABLE_6DOF_CAMERA
double theta_rad = Geometry::deg2rad(-(double)m_theta);
double phi_rad = Geometry::deg2rad((double)phi);
double sin_theta = ::sin(theta_rad);
Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
#endif // !ENABLE_6DOF_CAMERA
glsafe(::glMatrixMode(GL_MODELVIEW));
glsafe(::glLoadIdentity());
#if ENABLE_6DOF_CAMERA
glsafe(::glMultMatrixd(m_view_matrix.data()));
#else
glsafe(::glRotatef(-m_theta, 1.0f, 0.0f, 0.0f)); // pitch
glsafe(::glRotatef(phi, 0.0f, 0.0f, 1.0f)); // yaw
glsafe(::glTranslated(-camera_pos(0), -camera_pos(1), -camera_pos(2)));
glsafe(::glGetDoublev(GL_MODELVIEW_MATRIX, m_view_matrix.data()));
#endif // ENABLE_6DOF_CAMERA
}
void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double far_z) const
@ -300,7 +346,11 @@ void Camera::zoom_to_box(const BoundingBoxf3& box, int canvas_w, int canvas_h)
{
m_zoom = zoom;
// center view around box center
#if ENABLE_6DOF_CAMERA
set_target(box.center());
#else
m_target = box.center();
#endif // ENABLE_6DOF_CAMERA
}
}
@ -313,7 +363,11 @@ void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, int canvas_w, int canv
{
m_zoom = zoom;
// center view around the calculated center
#if ENABLE_6DOF_CAMERA
set_target(center);
#else
m_target = center;
#endif // ENABLE_6DOF_CAMERA
}
}
#endif // ENABLE_THUMBNAIL_GENERATOR
@ -322,10 +376,15 @@ void Camera::zoom_to_volumes(const GLVolumePtrs& volumes, int canvas_w, int canv
void Camera::debug_render() const
{
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.set_next_window_bg_alpha(0.5f);
imgui.begin(std::string("Camera statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
std::string type = get_type_as_string();
#if ENABLE_6DOF_CAMERA
if (wxGetApp().plater()->get_mouse3d_controller().is_running() || (wxGetApp().app_config->get("use_free_camera") == "1"))
type += "/free";
else
type += "/constrained";
#endif // ENABLE_6DOF_CAMERA
Vec3f position = get_position().cast<float>();
Vec3f target = m_target.cast<float>();
float distance = (float)get_distance();
@ -339,7 +398,7 @@ void Camera::debug_render() const
float fov = (float)get_fov();
float gui_scale = (float)get_gui_scale();
ImGui::InputText("Type", const_cast<char*>(type.data()), type.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::InputText("Type", type.data(), type.length(), ImGuiInputTextFlags_ReadOnly);
ImGui::Separator();
ImGui::InputFloat3("Position", position.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Target", target.data(), "%.6f", ImGuiInputTextFlags_ReadOnly);
@ -361,6 +420,50 @@ void Camera::debug_render() const
}
#endif // ENABLE_CAMERA_STATISTICS
#if ENABLE_6DOF_CAMERA
void Camera::translate_world(const Vec3d& displacement)
{
Vec3d new_target = validate_target(m_target + displacement);
Vec3d new_displacement = new_target - m_target;
if (!new_displacement.isApprox(Vec3d::Zero()))
{
m_target += new_displacement;
m_view_matrix.translate(-new_displacement);
}
}
void Camera::rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad)
{
Vec3d target = m_target;
translate_world(-target);
m_view_matrix.rotate(Eigen::AngleAxisd(delta_zenit_rad, get_dir_right()));
m_view_matrix.rotate(Eigen::AngleAxisd(delta_azimut_rad, Vec3d::UnitZ()));
translate_world(target);
}
void Camera::rotate_local_around_target(const Vec3d& rotation_rad)
{
rotate_local_around_pivot(rotation_rad, m_target);
}
void Camera::rotate_local_around_pivot(const Vec3d& rotation_rad, const Vec3d& pivot)
{
// we use a copy of the pivot because a reference to the current m_target may be passed in (see i.e. rotate_local_around_target())
// and m_target is modified by the translate_world() calls
Vec3d center = pivot;
translate_world(-center);
m_view_matrix.rotate(Eigen::AngleAxisd(rotation_rad(0), get_dir_right()));
m_view_matrix.rotate(Eigen::AngleAxisd(rotation_rad(1), get_dir_up()));
m_view_matrix.rotate(Eigen::AngleAxisd(rotation_rad(2), get_dir_forward()));
translate_world(center);
}
double Camera::min_zoom() const
{
return 0.7 * calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]);
}
#endif // ENABLE_6DOF_CAMERA
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box) const
{
std::pair<double, double> ret;
@ -541,6 +644,63 @@ void Camera::set_distance(double distance) const
apply_view_matrix();
}
#if ENABLE_6DOF_CAMERA
Transform3d Camera::look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up) const
{
Vec3d unit_z = (position - target).normalized();
Vec3d unit_x = up.cross(unit_z).normalized();
Vec3d unit_y = unit_z.cross(unit_x).normalized();
Transform3d matrix;
matrix(0, 0) = unit_x(0);
matrix(0, 1) = unit_x(1);
matrix(0, 2) = unit_x(2);
matrix(0, 3) = -unit_x.dot(position);
matrix(1, 0) = unit_y(0);
matrix(1, 1) = unit_y(1);
matrix(1, 2) = unit_y(2);
matrix(1, 3) = -unit_y.dot(position);
matrix(2, 0) = unit_z(0);
matrix(2, 1) = unit_z(1);
matrix(2, 2) = unit_z(2);
matrix(2, 3) = -unit_z.dot(position);
matrix(3, 0) = 0.0;
matrix(3, 1) = 0.0;
matrix(3, 2) = 0.0;
matrix(3, 3) = 1.0;
return matrix;
}
void Camera::set_default_orientation()
{
double theta_rad = Geometry::deg2rad(-45.0);
double phi_rad = Geometry::deg2rad(45.0);
double sin_theta = ::sin(theta_rad);
Vec3d camera_pos = m_target + m_distance * Vec3d(sin_theta * ::sin(phi_rad), sin_theta * ::cos(phi_rad), ::cos(theta_rad));
m_view_matrix = Transform3d::Identity();
m_view_matrix.rotate(Eigen::AngleAxisd(theta_rad, Vec3d::UnitX())).rotate(Eigen::AngleAxisd(phi_rad, Vec3d::UnitZ())).translate(-camera_pos);
}
Vec3d Camera::validate_target(const Vec3d& target) const
{
BoundingBoxf3 test_box = m_scene_box;
test_box.translate(-m_scene_box.center());
// We may let this factor be customizable
static const double ScaleFactor = 1.5;
test_box.scale(ScaleFactor);
test_box.translate(m_scene_box.center());
return Vec3d(std::clamp(target(0), test_box.min(0), test_box.max(0)),
std::clamp(target(1), test_box.min(1), test_box.max(1)),
std::clamp(target(2), test_box.min(2), test_box.max(2)));
}
#endif // ENABLE_6DOF_CAMERA
} // GUI
} // Slic3r

View file

@ -30,21 +30,29 @@ struct Camera
Num_types
};
#if !ENABLE_6DOF_CAMERA
float phi;
bool requires_zoom_to_bed;
bool inverted_phi;
#endif // !ENABLE_6DOF_CAMERA
bool requires_zoom_to_bed;
private:
EType m_type;
Vec3d m_target;
#if !ENABLE_6DOF_CAMERA
float m_theta;
#endif // !ENABLE_6DOF_CAMERA
double m_zoom;
// Distance between camera position and camera target measured along the camera Z axis
mutable double m_distance;
mutable double m_gui_scale;
mutable std::array<int, 4> m_viewport;
#if ENABLE_6DOF_CAMERA
Transform3d m_view_matrix;
#else
mutable Transform3d m_view_matrix;
#endif // ENABLE_6DOF_CAMERA
mutable Transform3d m_projection_matrix;
mutable std::pair<double, double> m_frustrum_zs;
@ -66,17 +74,24 @@ public:
double get_distance() const { return m_distance; }
double get_gui_scale() const { return m_gui_scale; }
#if !ENABLE_6DOF_CAMERA
float get_theta() const { return m_theta; }
void set_theta(float theta, bool apply_limit);
#endif // !ENABLE_6DOF_CAMERA
double get_zoom() const { return m_zoom; }
double get_inv_zoom() const { assert(m_zoom != 0.0); return 1.0 / m_zoom; }
void update_zoom(double delta_zoom);
void set_zoom(double zoom);
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; }
#if ENABLE_6DOF_CAMERA
void select_view(const std::string& direction);
#else
bool select_view(const std::string& direction);
#endif // ENABLE_6DOF_CAMERA
const std::array<int, 4>& get_viewport() const { return m_viewport; }
const Transform3d& get_view_matrix() const { return m_view_matrix; }
@ -110,6 +125,27 @@ public:
void debug_render() const;
#endif // ENABLE_CAMERA_STATISTICS
#if ENABLE_6DOF_CAMERA
// translate the camera in world space
void translate_world(const Vec3d& displacement);
// rotate the camera on a sphere having center == m_target and radius == m_distance
// using the given variations of spherical coordinates
void rotate_on_sphere(double delta_azimut_rad, double delta_zenit_rad);
// rotate the camera around three axes parallel to the camera local axes and passing through m_target
void rotate_local_around_target(const Vec3d& rotation_rad);
// rotate the camera around three axes parallel to the camera local axes and passing through the given pivot point
void rotate_local_around_pivot(const Vec3d& rotation_rad, const Vec3d& pivot);
// returns true if the camera z axis (forward) is pointing in the negative direction of the world z axis
bool is_looking_downward() const { return get_dir_forward().dot(Vec3d::UnitZ()) < 0.0; }
double max_zoom() const { return 100.0; }
double min_zoom() const;
#endif // ENABLE_6DOF_CAMERA
private:
// returns tight values for nearZ and farZ plane around the given bounding box
// the camera MUST be outside of the bounding box in eye coordinate of the given box
@ -121,6 +157,12 @@ private:
double calc_zoom_to_bounding_box_factor(const BoundingBoxf3& box, int canvas_w, int canvas_h) const;
#endif // ENABLE_THUMBNAIL_GENERATOR
void set_distance(double distance) const;
#if ENABLE_6DOF_CAMERA
Transform3d look_at(const Vec3d& position, const Vec3d& target, const Vec3d& up) const;
void set_default_orientation();
Vec3d validate_target(const Vec3d& target) const;
#endif // ENABLE_6DOF_CAMERA
};
} // GUI

View file

@ -350,6 +350,21 @@ bool PrinterPicker::any_selected() const
return false;
}
std::set<std::string> PrinterPicker::get_selected_models() const
{
std::set<std::string> ret_set;
for (const auto& cb : cboxes)
if (cb->GetValue())
ret_set.emplace(cb->model);
for (const auto& cb : cboxes_alt)
if (cb->GetValue())
ret_set.emplace(cb->model);
return ret_set;
}
void PrinterPicker::on_checkbox(const Checkbox *cbox, bool checked)
{
PrinterPickerEvent evt(EVT_PRINTER_PICK, GetId(), vendor_id, cbox->model, cbox->variant, checked);
@ -500,6 +515,19 @@ bool PagePrinters::any_selected() const
return false;
}
std::set<std::string> PagePrinters::get_selected_models()
{
std::set<std::string> ret_set;
for (const auto *picker : printer_pickers)
{
std::set<std::string> tmp_models = picker->get_selected_models();
ret_set.insert(tmp_models.begin(), tmp_models.end());
}
return ret_set;
}
void PagePrinters::set_run_reason(ConfigWizard::RunReason run_reason)
{
if (technology == T_FFF
@ -655,14 +683,6 @@ void PageMaterials::update_lists(int sel1, int sel2)
sel2_prev = sel2;
}
// for the very begining
if ((wizard_p()->run_reason == ConfigWizard::RR_DATA_EMPTY || wizard_p()->run_reason == ConfigWizard::RR_DATA_LEGACY)
&& list_l3->size() > 0 )
{
list_l3->Check(0, true);
wizard_p()->update_presets_in_config(materials->appconfig_section(), list_l3->get_data(0), true);
}
}
void PageMaterials::select_material(int i)
@ -773,6 +793,23 @@ PageUpdate::PageUpdate(ConfigWizard *parent)
box_presets->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent &event) { this->preset_update = event.IsChecked(); });
}
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
PageReloadFromDisk::PageReloadFromDisk(ConfigWizard* parent)
: ConfigWizardPage(parent, _(L("Reload from disk")), _(L("Reload from disk")))
, full_pathnames(false)
{
auto* box_pathnames = new wxCheckBox(this, wxID_ANY, _(L("Export full pathnames of models and parts sources into 3mf and amf files")));
box_pathnames->SetValue(wxGetApp().app_config->get("export_sources_full_pathnames") == "1");
append(box_pathnames);
append_text(_(L(
"If enabled, allows the Reload from disk command to automatically find and load the files when invoked.\n"
"If not enabled, the Reload from disk command will ask to select each file using an open file dialog."
)));
box_pathnames->Bind(wxEVT_CHECKBOX, [this](wxCommandEvent& event) { this->full_pathnames = event.IsChecked(); });
}
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
PageMode::PageMode(ConfigWizard *parent)
: ConfigWizardPage(parent, _(L("View mode")), _(L("View mode")))
{
@ -821,7 +858,7 @@ PageVendors::PageVendors(ConfigWizard *parent)
{
const AppConfig &appconfig = this->wizard_p()->appconfig_new;
append_text(wxString::Format(_(L("Pick another vendor supported by %s: (FIXME: this text)")), SLIC3R_APP_NAME));
append_text(wxString::Format(_(L("Pick another vendor supported by %s")), SLIC3R_APP_NAME) + ":");
auto boldfont = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
boldfont.SetWeight(wxFONTWEIGHT_BOLD);
@ -1254,7 +1291,7 @@ const std::string Materials::UNKNOWN = "(Unknown)";
void Materials::push(const Preset *preset)
{
presets.insert(preset);
presets.push_back(preset);
types.insert(technology & T_FFF
? Materials::get_filament_type(preset)
: Materials::get_material_type(preset));
@ -1364,6 +1401,9 @@ void ConfigWizard::priv::load_pages()
btn_finish->Enable(any_fff_selected || any_sla_selected);
index->add_page(page_update);
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
index->add_page(page_reload_from_disk);
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
index->add_page(page_mode);
index->go_to(former_active); // Will restore the active item/page if possible
@ -1516,23 +1556,21 @@ void ConfigWizard::priv::update_materials(Technology technology)
for (const auto &pair : bundles) {
for (const auto &filament : pair.second.preset_bundle->filaments) {
// Check if filament is already added
if (filaments.containts(&filament)) { continue; }
if (filaments.containts(&filament))
continue;
// Iterate printers in all bundles
for (const auto &pair : bundles) {
for (const auto &printer : pair.second.preset_bundle->printers) {
// For now, we only allow the profiles to be compatible with another profiles inside the same bundle.
// for (const auto &pair : bundles)
for (const auto &printer : pair.second.preset_bundle->printers)
// Filter out inapplicable printers
if (!printer.is_visible || printer.printer_technology() != ptFFF) {
continue;
}
if (filament.is_compatible_with_printer(printer)) {
if (printer.is_visible && printer.printer_technology() == ptFFF &&
is_compatible_with_printer(PresetWithVendorProfile(filament, nullptr), PresetWithVendorProfile(printer, nullptr)) &&
// Check if filament is already added
! filaments.containts(&filament)) {
filaments.push(&filament);
if (!filament.alias.empty())
aliases_fff[filament.alias].insert(filament.name);
}
}
}
}
}
}
@ -1545,23 +1583,21 @@ void ConfigWizard::priv::update_materials(Technology technology)
for (const auto &pair : bundles) {
for (const auto &material : pair.second.preset_bundle->sla_materials) {
// Check if material is already added
if (sla_materials.containts(&material)) { continue; }
if (sla_materials.containts(&material))
continue;
// Iterate printers in all bundles
for (const auto &pair : bundles) {
for (const auto &printer : pair.second.preset_bundle->printers) {
// For now, we only allow the profiles to be compatible with another profiles inside the same bundle.
// for (const auto &pair : bundles)
for (const auto &printer : pair.second.preset_bundle->printers)
// Filter out inapplicable printers
if (!printer.is_visible || printer.printer_technology() != ptSLA) {
continue;
}
if (material.is_compatible_with_printer(printer)) {
if (printer.is_visible && printer.printer_technology() == ptSLA &&
is_compatible_with_printer(PresetWithVendorProfile(material, nullptr), PresetWithVendorProfile(printer, nullptr)) &&
// Check if material is already added
! sla_materials.containts(&material)) {
sla_materials.push(&material);
if (!material.alias.empty())
aliases_sla[material.alias].insert(material.name);
}
}
}
}
}
}
@ -1592,6 +1628,10 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPicker
preset.is_visible = evt.enable;
}
}
// if at list one printer is selected but there in no one selected material,
// select materials which is default for selected printer(s)
select_default_materials_if_needed(pair.second.vendor_profile, page->technology, evt.model_id);
}
if (page->technology & T_FFF) {
@ -1601,6 +1641,57 @@ void ConfigWizard::priv::on_printer_pick(PagePrinters *page, const PrinterPicker
}
}
void ConfigWizard::priv::select_default_materials_for_printer_model(const std::vector<VendorProfile::PrinterModel>& models, Technology technology, const std::string& model_id)
{
PageMaterials* page_materials = technology & T_FFF ? page_filaments : page_sla_materials;
auto it = std::find_if(models.begin(), models.end(), [model_id](VendorProfile::PrinterModel model) {return model_id == model.id; });
if (it != models.end())
for (const std::string& material : it->default_materials)
appconfig_new.set(page_materials->materials->appconfig_section(), material, "1");
}
void ConfigWizard::priv::select_default_materials_if_needed(VendorProfile* vendor_profile, Technology technology, const std::string& model_id)
{
if ((technology & T_FFF && !any_fff_selected) ||
(technology & T_SLA && !any_sla_selected) ||
check_materials_in_config(technology, false))
return;
select_default_materials_for_printer_model(vendor_profile->models, technology, model_id);
}
void ConfigWizard::priv::selected_default_materials(Technology technology)
{
auto select_default_materials_for_printer_page = [this](PagePrinters * page_printers, Technology technology)
{
std::set<std::string> selected_models = page_printers->get_selected_models();
const std::string vendor_id = page_printers->get_vendor_id();
for (auto& pair : bundles)
{
if (pair.first != vendor_id)
continue;
for (const std::string& model_id : selected_models)
select_default_materials_for_printer_model(pair.second.vendor_profile->models, technology, model_id);
}
};
PagePrinters* page_printers = technology & T_FFF ? page_fff : page_msla;
select_default_materials_for_printer_page(page_printers, technology);
for (const auto& printer : pages_3rdparty)
{
page_printers = technology & T_FFF ? printer.second.first : printer.second.second;
if (page_printers)
select_default_materials_for_printer_page(page_printers, technology);
}
update_materials(technology);
(technology& T_FFF ? page_filaments : page_sla_materials)->reload_presets();
}
void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool install)
{
auto it = pages_3rdparty.find(vendor->id);
@ -1617,7 +1708,27 @@ void ConfigWizard::priv::on_3rdparty_install(const VendorProfile *vendor, bool i
load_pages();
}
bool ConfigWizard::priv::check_material_config()
bool ConfigWizard::priv::on_bnt_finish()
{
/* When Filaments or Sla Materials pages are activated,
* materials for this pages are automaticaly updated and presets are reloaded.
*
* But, if _Finish_ button was clicked without activation of those pages
* (for example, just some printers were added/deleted),
* than last changes wouldn't be updated for filaments/materials.
* SO, do that before close of Wizard
*/
update_materials(T_ANY);
if (any_fff_selected)
page_filaments->reload_presets();
if (any_sla_selected)
page_sla_materials->reload_presets();
// check, that there is selected at least one filament/material
return check_materials_in_config(T_ANY);
}
bool ConfigWizard::priv::check_materials_in_config(Technology technology, bool show_info_msg)
{
const auto exist_preset = [this](const std::string& section, const Materials& materials)
{
@ -1632,15 +1743,32 @@ bool ConfigWizard::priv::check_material_config()
return false;
};
if (any_fff_selected && !exist_preset(AppConfig::SECTION_FILAMENTS, filaments))
const auto ask_and_selected_default_materials = [this](wxString message, Technology technology)
{
show_info(q, _(L("You have to select at least one filament for selected printers")), "");
wxMessageDialog msg(q, message, _(L("Notice")), wxYES_NO);
if (msg.ShowModal() == wxID_YES)
selected_default_materials(technology);
};
if (any_fff_selected && technology & T_FFF && !exist_preset(AppConfig::SECTION_FILAMENTS, filaments))
{
if (show_info_msg)
{
wxString message = _(L("You have to select at least one filament for selected printers")) + "\n\n\t" +
_(L("Do you want to automatic select default filaments?"));
ask_and_selected_default_materials(message, T_FFF);
}
return false;
}
if (any_sla_selected && !exist_preset(AppConfig::SECTION_MATERIALS, sla_materials))
if (any_sla_selected && technology & T_SLA && !exist_preset(AppConfig::SECTION_MATERIALS, sla_materials))
{
show_info(q, _(L("You have to select at least one material for selected printers")), "");
if (show_info_msg)
{
wxString message = _(L("You have to select at least one material for selected printers")) + "\n\n\t" +
_(L("Do you want to automatic select default materials?"));
ask_and_selected_default_materials(message, T_SLA);
}
return false;
}
@ -1722,6 +1850,11 @@ void ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
}
app_config->set("version_check", page_update->version_check ? "1" : "0");
app_config->set("preset_update", page_update->preset_update ? "1" : "0");
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
app_config->set("export_sources_full_pathnames", page_reload_from_disk->full_pathnames ? "1" : "0");
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
page_mode->serialize_mode(app_config);
std::string preferred_model;
@ -1813,7 +1946,7 @@ bool ConfigWizard::priv::check_sla_selected()
// Public
ConfigWizard::ConfigWizard(wxWindow *parent)
: DPIDialog(parent, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + name(), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
: DPIDialog(parent, wxID_ANY, wxString(SLIC3R_APP_NAME) + " - " + _(name().ToStdString()), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
, p(new priv(this))
{
this->SetFont(wxGetApp().normal_font());
@ -1873,10 +2006,13 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
p->add_page(p->page_filaments = new PageMaterials(this, &p->filaments,
_(L("Filament Profiles Selection")), _(L("Filaments")), _(L("Type:")) ));
p->add_page(p->page_sla_materials = new PageMaterials(this, &p->sla_materials,
_(L("SLA Material Profiles Selection")), _(L("SLA Materials")), _(L("Layer height:")) ));
_(L("SLA Material Profiles Selection")) + " ", _(L("SLA Materials")), _(L("Layer height:")) ));
p->add_page(p->page_custom = new PageCustom(this));
p->add_page(p->page_update = new PageUpdate(this));
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
p->add_page(p->page_reload_from_disk = new PageReloadFromDisk(this));
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
p->add_page(p->page_mode = new PageMode(this));
p->add_page(p->page_firmware = new PageFirmware(this));
p->add_page(p->page_bed = new PageBedShape(this));
@ -1905,14 +2041,22 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
});
p->btn_prev->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_prev(); });
p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) { this->p->index->go_next(); });
p->btn_next->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &)
{
// check, that there is selected at least one filament/material
ConfigWizardPage* active_page = this->p->index->active_page();
if ( (active_page == p->page_filaments || active_page == p->page_sla_materials)
&& !p->check_materials_in_config(dynamic_cast<PageMaterials*>(active_page)->materials->technology))
return;
this->p->index->go_next();
});
p->btn_finish->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &)
{
if (!p->check_material_config())
return;
this->EndModal(wxID_OK);
if (p->on_bnt_finish())
this->EndModal(wxID_OK);
});
// p->btn_finish->Hide();
p->btn_sel_all->Bind(wxEVT_BUTTON, [this](const wxCommandEvent &) {
p->any_sla_selected = true;
@ -1925,7 +2069,6 @@ ConfigWizard::ConfigWizard(wxWindow *parent)
p->index->Bind(EVT_INDEX_PAGE, [this](const wxCommandEvent &) {
const bool is_last = p->index->active_is_last();
p->btn_next->Show(! is_last);
// p->btn_finish->Show(is_last);
if (is_last)
p->btn_finish->SetFocus();

View file

@ -58,15 +58,16 @@ enum Technology {
struct Materials
{
Technology technology;
std::set<const Preset*> presets;
// use vector for the presets to purpose of save of presets sorting in the bundle
std::vector<const Preset*> presets;
std::set<std::string> types;
Materials(Technology technology) : technology(technology) {}
void push(const Preset *preset);
void clear();
bool containts(const Preset *preset) {
return presets.find(preset) != presets.end();
bool containts(const Preset *preset) const {
return std::find(presets.begin(), presets.end(), preset) != presets.end();
}
const std::string& appconfig_section() const;
@ -148,6 +149,7 @@ struct PrinterPicker: wxPanel
void select_all(bool select, bool alternates = false);
void select_one(size_t i, bool select);
bool any_selected() const;
std::set<std::string> get_selected_models() const ;
int get_width() const { return width; }
const std::vector<int>& get_button_indexes() { return m_button_indexes; }
@ -214,6 +216,9 @@ struct PagePrinters: ConfigWizardPage
void select_all(bool select, bool alternates = false);
int get_width() const;
bool any_selected() const;
std::set<std::string> get_selected_models();
std::string get_vendor_id() const { return printer_pickers.empty() ? "" : printer_pickers[0]->vendor_id; }
virtual void set_run_reason(ConfigWizard::RunReason run_reason) override;
};
@ -300,6 +305,17 @@ struct PageUpdate: ConfigWizardPage
PageUpdate(ConfigWizard *parent);
};
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
struct PageReloadFromDisk : ConfigWizardPage
{
bool full_pathnames;
PageReloadFromDisk(ConfigWizard* parent);
};
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
struct PageMode: ConfigWizardPage
{
wxRadioButton *radio_simple;
@ -454,6 +470,11 @@ struct ConfigWizard::priv
PageMaterials *page_sla_materials = nullptr;
PageCustom *page_custom = nullptr;
PageUpdate *page_update = nullptr;
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
PageReloadFromDisk *page_reload_from_disk = nullptr;
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
PageMode *page_mode = nullptr;
PageVendors *page_vendors = nullptr;
Pages3rdparty pages_3rdparty;
@ -486,9 +507,17 @@ struct ConfigWizard::priv
void on_custom_setup();
void on_printer_pick(PagePrinters *page, const PrinterPickerEvent &evt);
void select_default_materials_for_printer_model(const std::vector<VendorProfile::PrinterModel> &models,
Technology technology,
const std::string & model_id);
void select_default_materials_if_needed(VendorProfile* vendor_profile,
Technology technology,
const std::string &model_id);
void selected_default_materials(Technology technology);
void on_3rdparty_install(const VendorProfile *vendor, bool install);
bool check_material_config();
bool on_bnt_finish();
bool check_materials_in_config(Technology technology, bool show_info_msg = true);
void apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater);
// #ys_FIXME_alise
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);

View file

@ -132,9 +132,7 @@ GLCanvas3D::LayersEditing::LayersEditing()
, m_object_max_z(0.f)
, m_slicing_parameters(nullptr)
, m_layer_height_profile_modified(false)
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
, m_adaptive_cusp(0.2f)
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
, m_adaptive_quality(0.5f)
, state(Unknown)
, band_width(2.0f)
, strength(0.005f)
@ -155,9 +153,6 @@ GLCanvas3D::LayersEditing::~LayersEditing()
}
const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f;
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
{
@ -224,8 +219,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
if (!m_enabled)
return;
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
static const ImVec4 orange(0.757f, 0.404f, 0.216f, 1.0f);
static const ImVec4 ORANGE(1.0f, 0.49f, 0.22f, 1.0f);
const Size& cnv_size = canvas.get_canvas_size();
float canvas_w = (float)cnv_size.get_width();
@ -233,37 +227,34 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f);
imgui.set_next_window_bg_alpha(0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
imgui.begin(_(L("Variable layer height")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
imgui.begin(_(L("Layer height profile")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
ImGui::PushStyleColor(ImGuiCol_Text, orange);
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
imgui.text(_(L("Left mouse button:")));
ImGui::PopStyleColor();
ImGui::SameLine();
imgui.text(_(L("Add detail")));
ImGui::PushStyleColor(ImGuiCol_Text, orange);
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
imgui.text(_(L("Right mouse button:")));
ImGui::PopStyleColor();
ImGui::SameLine();
imgui.text(_(L("Remove detail")));
ImGui::PushStyleColor(ImGuiCol_Text, orange);
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
imgui.text(_(L("Shift + Left mouse button:")));
ImGui::PopStyleColor();
ImGui::SameLine();
imgui.text(_(L("Reset to base")));
ImGui::PushStyleColor(ImGuiCol_Text, orange);
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
imgui.text(_(L("Shift + Right mouse button:")));
ImGui::PopStyleColor();
ImGui::SameLine();
imgui.text(_(L("Smoothing")));
ImGui::PushStyleColor(ImGuiCol_Text, orange);
ImGui::PushStyleColor(ImGuiCol_Text, ORANGE);
imgui.text(_(L("Mouse wheel:")));
ImGui::PopStyleColor();
ImGui::SameLine();
@ -271,16 +262,24 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
ImGui::Separator();
if (imgui.button(_(L("Adaptive"))))
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event<float>(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp));
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event<float>(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_quality));
ImGui::SameLine();
float text_align = ImGui::GetCursorPosX();
imgui.text(_(L("Cusp (mm)")));
ImGui::AlignTextToFramePadding();
imgui.text(_(L("Quality / Speed")));
if (ImGui::IsItemHovered())
{
ImGui::BeginTooltip();
ImGui::TextUnformatted(_(L("Higher print quality versus higher print speed.")));
ImGui::EndTooltip();
}
ImGui::SameLine();
float widget_align = ImGui::GetCursorPosX();
ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
m_adaptive_cusp = clamp((float)m_slicing_parameters->min_layer_height, (float)m_slicing_parameters->max_layer_height, m_adaptive_cusp);
ImGui::SliderFloat("", &m_adaptive_cusp, (float)m_slicing_parameters->min_layer_height, (float)m_slicing_parameters->max_layer_height, "%.2f");
m_adaptive_quality = clamp(0.0f, 1.f, m_adaptive_quality);
ImGui::SliderFloat("", &m_adaptive_quality, 0.0f, 1.f, "%.2f");
ImGui::Separator();
if (imgui.button(_(L("Smooth"))))
@ -288,6 +287,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
ImGui::SameLine();
ImGui::SetCursorPosX(text_align);
ImGui::AlignTextToFramePadding();
imgui.text(_(L("Radius")));
ImGui::SameLine();
ImGui::SetCursorPosX(widget_align);
@ -297,9 +297,12 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
m_smooth_params.radius = (unsigned int)radius;
ImGui::SetCursorPosX(text_align);
ImGui::AlignTextToFramePadding();
imgui.text(_(L("Keep min")));
ImGui::SameLine();
ImGui::SetCursorPosX(widget_align);
if (ImGui::GetCursorPosX() < widget_align) // because of line lenght after localization
ImGui::SetCursorPosX(widget_align);
ImGui::PushItemWidth(imgui.get_style_scaling() * 120.0f);
imgui.checkbox("##2", m_smooth_params.keep_min);
@ -309,16 +312,7 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
imgui.end();
ImGui::PopStyleVar();
const Rect& bar_rect = get_bar_rect_viewport(canvas);
#else
const Rect& bar_rect = get_bar_rect_viewport(canvas);
const Rect& reset_rect = get_reset_rect_viewport(canvas);
_render_tooltip_texture(canvas, bar_rect, reset_rect);
_render_reset_texture(reset_rect);
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
render_active_object_annotations(canvas, bar_rect);
render_profile(bar_rect);
}
@ -345,68 +339,26 @@ bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, floa
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
}
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y)
{
const Rect& rect = get_reset_rect_screen(canvas);
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
}
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
{
const Size& cnv_size = canvas.get_canvas_size();
float w = (float)cnv_size.get_width();
float h = (float)cnv_size.get_height();
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h);
#else
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas));
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
}
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
{
const Size& cnv_size = canvas.get_canvas_size();
float w = (float)cnv_size.get_width();
float h = (float)cnv_size.get_height();
return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h);
}
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
{
const Size& cnv_size = canvas.get_canvas_size();
float half_w = 0.5f * (float)cnv_size.get_width();
float half_h = 0.5f * (float)cnv_size.get_height();
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)canvas.get_camera().get_inv_zoom();
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
#else
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom);
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
}
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas)
{
const Size& cnv_size = canvas.get_canvas_size();
float half_w = 0.5f * (float)cnv_size.get_width();
float half_h = 0.5f * (float)cnv_size.get_height();
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
}
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
bool GLCanvas3D::LayersEditing::is_initialized() const
{
return m_shader.is_initialized();
@ -441,54 +393,6 @@ std::string GLCanvas3D::LayersEditing::get_tooltip(const GLCanvas3D& canvas) con
return ret;
}
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const
{
// TODO: do this with ImGui
if (m_tooltip_texture.get_id() == 0)
{
std::string filename = resources_dir() + "/icons/variable_layer_height_tooltip.png";
if (!m_tooltip_texture.load_from_file(filename, false, GLTexture::SingleThreaded, false))
return;
}
#if ENABLE_RETINA_GL
const float scale = canvas.get_canvas_size().get_scale_factor();
#else
const float scale = canvas.get_wxglcanvas()->GetContentScaleFactor();
#endif
const float width = (float)m_tooltip_texture.get_width() * scale;
const float height = (float)m_tooltip_texture.get_height() * scale;
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float gap = 10.0f * inv_zoom;
float bar_left = bar_rect.get_left();
float reset_bottom = reset_rect.get_bottom();
float l = bar_left - width * inv_zoom - gap;
float r = bar_left - gap;
float t = reset_bottom + height * inv_zoom + gap;
float b = reset_bottom + gap;
GLTexture::render_texture(m_tooltip_texture.get_id(), l, r, b, t);
}
void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) const
{
if (m_reset_texture.get_id() == 0)
{
std::string filename = resources_dir() + "/icons/variable_layer_height_reset.png";
if (!m_reset_texture.load_from_file(filename, false, GLTexture::SingleThreaded, false))
return;
}
GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top());
}
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const
{
m_shader.start_using();
@ -637,11 +541,10 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas)
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp)
void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor)
{
this->update_slicing_parameters();
m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp);
m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, quality_factor);
const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
m_layers_texture.valid = false;
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
@ -650,13 +553,11 @@ void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas
void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params)
{
this->update_slicing_parameters();
m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params);
const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
m_layers_texture.valid = false;
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void GLCanvas3D::LayersEditing::generate_layer_height_texture()
{
@ -692,7 +593,7 @@ void GLCanvas3D::LayersEditing::accept_changes(GLCanvas3D& canvas)
{
if (last_object_id >= 0) {
if (m_layer_height_profile_modified) {
wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Manual edit")));
wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Manual edit")));
const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
}
@ -719,19 +620,6 @@ float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas)
* THICKNESS_BAR_WIDTH;
}
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas)
{
return
#if ENABLE_RETINA_GL
canvas.get_canvas_size().get_scale_factor()
#else
canvas.get_wxglcanvas()->GetContentScaleFactor()
#endif
* THICKNESS_RESET_BUTTON_HEIGHT;
}
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX);
const Vec3d GLCanvas3D::Mouse::Drag::Invalid_3D_Point(DBL_MAX, DBL_MAX, DBL_MAX);
@ -958,8 +846,7 @@ void GLCanvas3D::WarningTexture::render(const GLCanvas3D& canvas) const
if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0))
{
const Size& cnv_size = canvas.get_canvas_size();
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)canvas.get_camera().get_inv_zoom();
float left = (-0.5f * (float)m_original_width) * inv_zoom;
float top = (-0.5f * (float)cnv_size.get_height() + (float)m_original_height + 2.0f) * inv_zoom;
float right = left + (float)m_original_width * inv_zoom;
@ -1005,24 +892,25 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D
std::vector<float>& colors,
std::vector<std::string>& cp_legend_items)
{
std::vector<Model::CustomGCode> custom_gcode_per_height = wxGetApp().plater()->model().custom_gcode_per_height;
std::vector<Model::CustomGCode> custom_gcode_per_print_z = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
const int extruders_cnt = wxGetApp().extruders_edited_cnt();
if (extruders_cnt == 1)
{
if (custom_gcode_per_height.empty()) {
cp_legend_items.push_back(I18N::translate_utf8(L("Default print color")));
if (custom_gcode_per_print_z.empty()) {
cp_legend_items.emplace_back(I18N::translate_utf8(L("Default print color")));
colors = colors_in;
return;
}
std::vector<std::pair<double, double>> cp_values;
cp_values.reserve(custom_gcode_per_print_z.size());
std::vector<double> print_zs = canvas.get_current_print_zs(true);
for (auto custom_code : custom_gcode_per_height)
for (auto custom_code : custom_gcode_per_print_z)
{
if (custom_code.gcode != ColorChangeCode)
continue;
auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.height - DoubleSlider::epsilon());
auto lower_b = std::lower_bound(print_zs.begin(), print_zs.end(), custom_code.print_z - DoubleSlider::epsilon());
if (lower_b == print_zs.end())
continue;
@ -1033,14 +921,14 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D
// to avoid duplicate values, check adding values
if (cp_values.empty() ||
!(cp_values.back().first == previous_z && cp_values.back().second == current_z))
cp_values.push_back(std::pair<double, double>(previous_z, current_z));
cp_values.emplace_back(std::pair<double, double>(previous_z, current_z));
}
const auto items_cnt = (int)cp_values.size();
if (items_cnt == 0) // There is no one color change, but there is/are some pause print or custom Gcode
{
cp_legend_items.push_back(I18N::translate_utf8(L("Default print color")));
cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
cp_legend_items.emplace_back(I18N::translate_utf8(L("Default print color")));
cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code")));
colors = colors_in;
return;
}
@ -1049,7 +937,7 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D
colors.resize(colors_in.size(), 0.0);
::memcpy((void*)(colors.data()), (const void*)(colors_in.data() + (color_cnt - 1) * 4), 4 * sizeof(float));
cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code")));
size_t color_pos = 4;
for (int i = items_cnt; i >= 0; --i, color_pos+=4)
@ -1061,15 +949,15 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D
std::string id_str = std::to_string(i + 1) + ": ";
if (i == 0) {
cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values[0].first).str());
cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("up to %.2f mm"))) % cp_values[0].first).str());
break;
}
if (i == items_cnt) {
cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str());
cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("above %.2f mm"))) % cp_values[i - 1].second).str());
continue;
}
cp_legend_items.push_back(id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str());
cp_legend_items.emplace_back(id_str + (boost::format(I18N::translate_utf8(L("%.2f - %.2f mm"))) % cp_values[i - 1].second % cp_values[i].first).str());
}
}
else
@ -1083,20 +971,20 @@ void GLCanvas3D::LegendTexture::fill_color_print_legend_items( const GLCanvas3D
size_t color_in_pos = 4 * (color_cnt - 1);
for (unsigned int i = 0; i < (unsigned int)extruders_cnt; ++i)
cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str());
cp_legend_items.emplace_back((boost::format(I18N::translate_utf8(L("Extruder %d"))) % (i + 1)).str());
::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float));
color_pos += 4;
color_in_pos -= 4;
cp_legend_items.push_back(I18N::translate_utf8(L("Pause print or custom G-code")));
cp_legend_items.emplace_back(I18N::translate_utf8(L("Pause print or custom G-code")));
int cnt = custom_gcode_per_height.size();
int cnt = custom_gcode_per_print_z.size();
for (int i = cnt-1; i >= 0; --i)
if (custom_gcode_per_height[i].gcode == ColorChangeCode) {
if (custom_gcode_per_print_z[i].gcode == ColorChangeCode) {
::memcpy((void*)(colors.data() + color_pos), (const void*)(colors_in.data() + color_in_pos), 4 * sizeof(float));
color_pos += 4;
color_in_pos -= 4;
cp_legend_items.push_back((boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_height[i].extruder % custom_gcode_per_height[i].height).str());
cp_legend_items.emplace_back((boost::format(I18N::translate_utf8(L("Color change for Extruder %d at %.2f mm"))) % custom_gcode_per_print_z[i].extruder % custom_gcode_per_print_z[i].print_z).str());
}
}
}
@ -1130,6 +1018,12 @@ bool GLCanvas3D::LegendTexture::generate(const GCodePreviewData& preview_data, c
// calculate scaling
const float scale_gl = canvas.get_canvas_size().get_scale_factor();
#if ENABLE_RETINA_GL
// For non-visible or non-created window getBackingScaleFactor function return 0.0 value.
// And using of the zero scale causes a crash, when we trying to draw text to the (0,0) rectangle
if (scale_gl <= 0.0f)
return false;
#endif
const float scale = scale_gl * wxGetApp().em_unit()*0.1; // get scale from em_unit() value, because of get_scale_factor() return 1
const int scaled_square = std::floor((float)Px_Square * scale);
const int scaled_title_offset = Px_Title_Offset * scale;
@ -1314,8 +1208,7 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
if ((m_id > 0) && (m_original_width > 0) && (m_original_height > 0) && (m_width > 0) && (m_height > 0))
{
const Size& cnv_size = canvas.get_canvas_size();
float zoom = (float)canvas.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)canvas.get_camera().get_inv_zoom();
float left = (-0.5f * (float)cnv_size.get_width()) * inv_zoom;
float top = (0.5f * (float)cnv_size.get_height()) * inv_zoom;
float right = left + (float)m_original_width * inv_zoom;
@ -1336,7 +1229,6 @@ void GLCanvas3D::LegendTexture::render(const GLCanvas3D& canvas) const
}
}
wxDEFINE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_OBJECT_SELECT, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
@ -1360,11 +1252,9 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
#if ENABLE_THUMBNAIL_GENERATOR
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
@ -1377,7 +1267,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_retina_helper(nullptr)
#endif
, m_in_render(false)
, m_render_enabled(true)
, m_bed(bed)
, m_camera(camera)
, m_view_toolbar(view_toolbar)
@ -1386,7 +1275,7 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D& bed, Camera& camera, GLToolbar
, m_gizmos(*this)
, m_use_clipping_planes(false)
, m_sidebar_field("")
, m_keep_dirty(false)
, m_extra_frame_requested(false)
, m_config(nullptr)
, m_process(nullptr)
, m_model(nullptr)
@ -1507,8 +1396,6 @@ bool GLCanvas3D::init()
if (m_selection.is_enabled() && !m_selection.init())
return false;
post_event(SimpleEvent(EVT_GLCANVAS_INIT));
m_initialized = true;
return true;
@ -1622,8 +1509,6 @@ void GLCanvas3D::bed_shape_changed()
refresh_camera_scene_box();
m_camera.requires_zoom_to_bed = true;
m_dirty = true;
if (m_bed.is_prusa())
start_keeping_dirty();
}
void GLCanvas3D::set_color_by(const std::string& value)
@ -1667,31 +1552,29 @@ bool GLCanvas3D::is_layers_editing_allowed() const
return m_layers_editing.is_allowed();
}
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void GLCanvas3D::reset_layer_height_profile()
{
wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Reset")));
wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Reset")));
m_layers_editing.reset_layer_height_profile(*this);
m_layers_editing.state = LayersEditing::Completed;
m_dirty = true;
}
void GLCanvas3D::adaptive_layer_height_profile(float cusp)
void GLCanvas3D::adaptive_layer_height_profile(float quality_factor)
{
wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Adaptive")));
m_layers_editing.adaptive_layer_height_profile(*this, cusp);
wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Adaptive")));
m_layers_editing.adaptive_layer_height_profile(*this, quality_factor);
m_layers_editing.state = LayersEditing::Completed;
m_dirty = true;
}
void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params)
{
wxGetApp().plater()->take_snapshot(_(L("Layer height profile-Smooth all")));
wxGetApp().plater()->take_snapshot(_(L("Variable layer height - Smooth all")));
m_layers_editing.smooth_layer_height_profile(*this, smoothing_params);
m_layers_editing.state = LayersEditing::Completed;
m_dirty = true;
}
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
bool GLCanvas3D::is_reload_delayed() const
{
@ -1778,8 +1661,14 @@ void GLCanvas3D::zoom_to_selection()
void GLCanvas3D::select_view(const std::string& direction)
{
#if ENABLE_6DOF_CAMERA
m_camera.select_view(direction);
if (m_canvas != nullptr)
m_canvas->Refresh();
#else
if (m_camera.select_view(direction) && (m_canvas != nullptr))
m_canvas->Refresh();
#endif // ENABLE_6DOF_CAMERA
}
void GLCanvas3D::update_volumes_colors_by_extruder()
@ -1790,7 +1679,7 @@ void GLCanvas3D::update_volumes_colors_by_extruder()
void GLCanvas3D::render()
{
if (!m_render_enabled || m_in_render)
if (m_in_render)
{
// if called recursively, return
m_dirty = true;
@ -1836,10 +1725,12 @@ void GLCanvas3D::render()
GLfloat position_top[4] = { -0.5f, -0.5f, 1.0f, 0.0f };
glsafe(::glLightfv(GL_LIGHT0, GL_POSITION, position_top));
#if !ENABLE_6DOF_CAMERA
float theta = m_camera.get_theta();
if (theta > 180.f)
// absolute value of the rotation
theta = 360.f - theta;
#endif // !ENABLE_6DOF_CAMERA
wxGetApp().imgui()->new_frame();
@ -1864,7 +1755,11 @@ void GLCanvas3D::render()
_render_objects();
_render_sla_slices();
_render_selection();
#if ENABLE_6DOF_CAMERA
_render_bed(!m_camera.is_looking_downward(), true);
#else
_render_bed(theta, true);
#endif // ENABLE_6DOF_CAMERA
#if ENABLE_RENDER_SELECTION_CENTER
_render_selection_center();
@ -1893,7 +1788,6 @@ void GLCanvas3D::render()
#if ENABLE_RENDER_STATISTICS
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.set_next_window_bg_alpha(0.5f);
imgui.begin(std::string("Render statistics"), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
imgui.text("Last frame: ");
ImGui::SameLine();
@ -1914,7 +1808,7 @@ void GLCanvas3D::render()
m_camera.debug_render();
#endif // ENABLE_CAMERA_STATISTICS
wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog(*this);
wxGetApp().imgui()->render();
@ -1927,7 +1821,7 @@ void GLCanvas3D::render()
}
#if ENABLE_THUMBNAIL_GENERATOR
void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
void GLCanvas3D::render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const
{
switch (GLCanvas3DManager::get_framebuffers_type())
{
@ -2062,9 +1956,11 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
struct GLVolumeState {
GLVolumeState() :
volume_idx(-1) {}
volume_idx(size_t(-1)) {}
GLVolumeState(const GLVolume* volume, unsigned int volume_idx) :
composite_id(volume->composite_id), volume_idx(volume_idx) {}
GLVolumeState(const GLVolume::CompositeID &composite_id) :
composite_id(composite_id), volume_idx(size_t(-1)) {}
GLVolume::CompositeID composite_id;
// Volume index in the old GLVolume vector.
@ -2190,22 +2086,13 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
}
}
sort_remove_duplicates(instance_ids_selected);
auto deleted_volumes_lower = [](const GLVolumeState &v1, const GLVolumeState &v2) { return v1.composite_id < v2.composite_id; };
std::sort(deleted_volumes.begin(), deleted_volumes.end(), deleted_volumes_lower);
if (m_reload_delayed)
return;
bool update_object_list = false;
auto find_old_volume_id = [&deleted_volumes](const GLVolume::CompositeID& id) -> unsigned int {
for (unsigned int i = 0; i < (unsigned int)deleted_volumes.size(); ++i)
{
const GLVolumeState& v = deleted_volumes[i];
if (v.composite_id == id)
return v.volume_idx;
}
return (unsigned int)-1;
};
if (m_volumes.volumes != glvolumes_new)
update_object_list = true;
m_volumes.volumes = std::move(glvolumes_new);
@ -2220,9 +2107,10 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
assert(it != model_volume_state.end() && it->geometry_id == key.geometry_id);
if (it->new_geometry()) {
// New volume.
unsigned int old_id = find_old_volume_id(it->composite_id);
if (old_id != (unsigned int)-1)
map_glvolume_old_to_new[old_id] = m_volumes.volumes.size();
auto it_old_volume = std::lower_bound(deleted_volumes.begin(), deleted_volumes.end(), GLVolumeState(it->composite_id), deleted_volumes_lower);
if (it_old_volume != deleted_volumes.end() && it_old_volume->composite_id == it->composite_id)
// If a volume changed its ObjectID, but it reuses a GLVolume's CompositeID, maintain its selection.
map_glvolume_old_to_new[it_old_volume->volume_idx] = m_volumes.volumes.size();
m_volumes.load_object_volume(&model_object, obj_idx, volume_idx, instance_idx, m_color_by, m_initialized);
m_volumes.volumes.back()->geometry_id = key.geometry_id;
update_object_list = true;
@ -2416,7 +2304,7 @@ static void load_gcode_retractions(const GCodePreviewData::Retraction& retractio
volume_index.first_volumes.emplace_back(extrusion_type, 0, (unsigned int)volumes.volumes.size());
GLVolume *volume = volumes.new_nontoolpath_volume(retractions.color.rgba, VERTEX_BUFFER_RESERVE_SIZE);
GLVolume *volume = volumes.new_nontoolpath_volume(retractions.color.rgba.data(), VERTEX_BUFFER_RESERVE_SIZE);
GCodePreviewData::Retraction::PositionsList copy(retractions.positions);
std::sort(copy.begin(), copy.end(), [](const GCodePreviewData::Retraction::Position& p1, const GCodePreviewData::Retraction::Position& p2) { return p1.position(2) < p2.position(2); });
@ -2625,9 +2513,10 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
_refresh_if_shown_on_screen();
if (m_keep_dirty || mouse3d_controller_applied)
if (m_extra_frame_requested || mouse3d_controller_applied)
{
m_dirty = true;
m_extra_frame_requested = false;
evt.RequestMore();
}
else
@ -3105,20 +2994,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
m_layers_editing.state = LayersEditing::Editing;
_perform_layer_editing_action(&evt);
}
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1)))
{
if (evt.LeftDown())
{
// A volume is selected and the mouse is inside the reset button. Reset the ModelObject's layer height profile.
m_layers_editing.reset_layer_height_profile(*this);
// Index 2 means no editing, just wait for mouse up event.
m_layers_editing.state = LayersEditing::Completed;
m_dirty = true;
}
}
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled)
{
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)
@ -3201,7 +3076,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
// we do not want to translate objects if the user just clicked on an object while pressing shift to remove it from the selection and then drag
if (m_selection.contains_volume(get_first_hover_volume_idx()))
{
#if ENABLE_6DOF_CAMERA
if (std::abs(m_camera.get_dir_forward()(2)) < EPSILON)
#else
if (m_camera.get_theta() == 90.0f)
#endif // ENABLE_6DOF_CAMERA
{
// side view -> move selected volumes orthogonally to camera view direction
Linef3 ray = mouse_ray(pos);
@ -3261,9 +3140,18 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (m_hover_volume_idxs.empty() && m_mouse.is_start_position_3D_defined())
{
const Vec3d& orig = m_mouse.drag.start_position_3D;
#if ENABLE_6DOF_CAMERA
double x = Geometry::deg2rad(pos(0) - orig(0)) * (double)TRACKBALLSIZE;
double y = Geometry::deg2rad(pos(1) - orig(1)) * (double)TRACKBALLSIZE;
if (wxGetApp().plater()->get_mouse3d_controller().is_running() || (wxGetApp().app_config->get("use_free_camera") == "1"))
m_camera.rotate_local_around_target(Vec3d(y, x, 0.0));
else
m_camera.rotate_on_sphere(x, y);
#else
float sign = m_camera.inverted_phi ? -1.0f : 1.0f;
m_camera.phi += sign * ((float)pos(0) - (float)orig(0)) * TRACKBALLSIZE;
m_camera.set_theta(m_camera.get_theta() - ((float)pos(1) - (float)orig(1)) * TRACKBALLSIZE, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA);
#endif // ENABLE_6DOF_CAMERA
m_dirty = true;
}
m_mouse.drag.start_position_3D = Vec3d((double)pos(0), (double)pos(1), 0.0);
@ -3313,9 +3201,11 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
if (!evt.ShiftDown() && m_picking_enabled)
deselect_all();
}
#if !ENABLE_6DOF_CAMERA
else if (evt.LeftUp() && m_mouse.dragging)
// Flips X mouse deltas if bed is upside down
m_camera.inverted_phi = (m_camera.get_dir_up()(2) < 0.0);
#endif // !ENABLE_6DOF_CAMERA
else if (evt.RightUp())
{
m_mouse.position = pos.cast<double>();
@ -3833,14 +3723,13 @@ static bool string_getter(const bool is_undo, int idx, const char** out_text)
return wxGetApp().plater()->undo_redo_string_getter(is_undo, idx, out_text);
}
void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x)
void GLCanvas3D::_render_undo_redo_stack(const bool is_undo, float pos_x) const
{
ImGuiWrapper* imgui = wxGetApp().imgui();
const float x = pos_x * (float)get_camera().get_zoom() + 0.5f * (float)get_canvas_size().get_width();
imgui->set_next_window_pos(x, m_undoredo_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f);
imgui->set_next_window_bg_alpha(0.5f);
std::string title = is_undo ? L("Undo History") : L("Redo History");
std::string title = is_undo ? L("Undo History") : L("Redo History");
imgui->begin(_(title), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
int hovered = m_imgui_undo_redo_hovered_pos;
@ -3887,7 +3776,7 @@ static void debug_output_thumbnail(const ThumbnailData& thumbnail_data)
}
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const
{
auto is_visible = [](const GLVolume& v) -> bool
{
@ -3921,6 +3810,9 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool
Camera camera;
camera.set_type(Camera::Ortho);
#if ENABLE_6DOF_CAMERA
camera.set_scene_box(scene_bounding_box());
#endif // ENABLE_6DOF_CAMERA
camera.zoom_to_volumes(visible_volumes, thumbnail_data.width, thumbnail_data.height);
camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height);
camera.apply_view_matrix();
@ -3971,13 +3863,17 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, bool
glsafe(::glDisable(GL_DEPTH_TEST));
if (show_bed)
#if ENABLE_6DOF_CAMERA
_render_bed(!camera.is_looking_downward(), false);
#else
_render_bed(camera.get_theta(), false);
#endif // ENABLE_6DOF_CAMERA
if (transparent_background)
glsafe(::glClearColor(1.0f, 1.0f, 1.0f, 1.0f));
}
void GLCanvas3D::_render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
void GLCanvas3D::_render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const
{
thumbnail_data.set(w, h);
if (!thumbnail_data.is_valid())
@ -4081,7 +3977,7 @@ void GLCanvas3D::_render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, un
glsafe(::glDisable(GL_MULTISAMPLE));
}
void GLCanvas3D::_render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
void GLCanvas3D::_render_thumbnail_framebuffer_ext(ThumbnailData & thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const
{
thumbnail_data.set(w, h);
if (!thumbnail_data.is_valid())
@ -4185,7 +4081,7 @@ void GLCanvas3D::_render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data
glsafe(::glDisable(GL_MULTISAMPLE));
}
void GLCanvas3D::_render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background)
void GLCanvas3D::_render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const
{
// check that thumbnail size does not exceed the default framebuffer size
const Size& cnv_size = get_canvas_size();
@ -4222,6 +4118,9 @@ bool GLCanvas3D::_init_toolbars()
if (!_init_undoredo_toolbar())
return false;
if (!_init_view_toolbar())
return false;
return true;
}
@ -4362,7 +4261,7 @@ bool GLCanvas3D::_init_main_toolbar()
item.name = "layersediting";
item.icon_filename = "layers_white.svg";
item.tooltip = _utf8(L("Height ranges"));
item.tooltip = _utf8(L("Variable layer height"));
item.sprite_id = 10;
item.left.toggable = true;
item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); };
@ -4479,6 +4378,11 @@ bool GLCanvas3D::_init_undoredo_toolbar()
return true;
}
bool GLCanvas3D::_init_view_toolbar()
{
return wxGetApp().plater()->init_view_toolbar();
}
bool GLCanvas3D::_set_current()
{
return m_context != nullptr && m_canvas->SetCurrent(*m_context);
@ -4916,8 +4820,7 @@ void GLCanvas3D::_render_main_toolbar() const
#endif // ENABLE_RETINA_GL
Size cnv_size = get_canvas_size();
float zoom = (float)m_camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)m_camera.get_inv_zoom();
float top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
float left = -0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width()) * inv_zoom;
@ -4943,8 +4846,7 @@ void GLCanvas3D::_render_undoredo_toolbar() const
#endif // ENABLE_RETINA_GL
Size cnv_size = get_canvas_size();
float zoom = (float)m_camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)m_camera.get_inv_zoom();
float top = 0.5f * (float)cnv_size.get_height() * inv_zoom;
float left = (m_main_toolbar.get_width() - 0.5f * (m_main_toolbar.get_width() + m_undoredo_toolbar.get_width())) * inv_zoom;
@ -4966,8 +4868,7 @@ void GLCanvas3D::_render_view_toolbar() const
#endif // ENABLE_RETINA_GL
Size cnv_size = get_canvas_size();
float zoom = (float)m_camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)m_camera.get_inv_zoom();
// places the toolbar on the bottom-left corner of the 3d scene
float top = (-0.5f * (float)cnv_size.get_height() + m_view_toolbar.get_height()) * inv_zoom;
@ -5373,7 +5274,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
// For coloring by a color_print(M600), return a parsed color.
bool color_by_color_print() const { return color_print_values!=nullptr; }
const size_t color_print_color_idx_by_layer_idx(const size_t layer_idx) const {
const Model::CustomGCode value(layers[layer_idx]->print_z + EPSILON, "", 0, "");
const Model::CustomGCode value{layers[layer_idx]->print_z + EPSILON, "", 0, ""};
auto it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value);
return (it - color_print_values->begin()) % number_tools();
}
@ -5384,17 +5285,17 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
auto it = std::find_if(color_print_values->begin(), color_print_values->end(),
[print_z](const Model::CustomGCode& code)
{ return fabs(code.height - print_z) < EPSILON; });
{ return fabs(code.print_z - print_z) < EPSILON; });
if (it != color_print_values->end())
{
const std::string& code = it->gcode;
// pause print or custom Gcode
if (code == PausePrintCode ||
(code != ColorChangeCode && code != ExtruderChangeCode))
(code != ColorChangeCode && code != ToolChangeCode))
return number_tools()-1; // last color item is a gray color for pause print or custom G-code
// change tool (extruder)
if (code == ExtruderChangeCode)
if (code == ToolChangeCode)
return get_color_idx_for_tool_change(it, extruder);
// change color for current extruder
if (code == ColorChangeCode) {
@ -5404,7 +5305,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
}
}
const Model::CustomGCode value(print_z + EPSILON, "", 0, "");
const Model::CustomGCode value{print_z + EPSILON, "", 0, ""};
it = std::lower_bound(color_print_values->begin(), color_print_values->end(), value);
while (it != color_print_values->begin())
{
@ -5416,7 +5317,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
return color_idx;
}
// change tool (extruder)
if (it->gcode == ExtruderChangeCode)
if (it->gcode == ToolChangeCode)
return get_color_idx_for_tool_change(it, extruder);
}
@ -5460,7 +5361,7 @@ void GLCanvas3D::_load_print_object_toolpaths(const PrintObject& print_object, c
bool is_tool_change = false;
while (it_n != color_print_values->begin()) {
--it_n;
if (it_n->gcode == ExtruderChangeCode) {
if (it_n->gcode == ToolChangeCode) {
is_tool_change = true;
if (it_n->extruder == it->extruder || (it_n->extruder == 0 && it->extruder == extruder))
return get_m600_color_idx(it);
@ -5840,7 +5741,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
return 0.0f;
}
static GCodePreviewData::Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
static Color path_color(const GCodePreviewData& data, const std::vector<float>& tool_colors, float value)
{
switch (data.extrusion.view_type)
{
@ -5858,8 +5759,8 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
return data.get_volumetric_rate_color(value);
case GCodePreviewData::Extrusion::Tool:
{
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float));
Color color;
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data() + (unsigned int)value * 4), 4 * sizeof(float));
return color;
}
case GCodePreviewData::Extrusion::ColorPrint:
@ -5867,16 +5768,16 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
int color_cnt = (int)tool_colors.size() / 4;
int val = value > color_cnt ? color_cnt - 1 : value;
GCodePreviewData::Color color;
::memcpy((void*)color.rgba, (const void*)(tool_colors.data() + val * 4), 4 * sizeof(float));
Color color;
::memcpy((void*)color.rgba.data(), (const void*)(tool_colors.data() + val * 4), 4 * sizeof(float));
return color;
}
default:
return GCodePreviewData::Color::Dummy;
return Color{};
}
return GCodePreviewData::Color::Dummy;
return Color{};
}
};
@ -5919,7 +5820,7 @@ void GLCanvas3D::_load_gcode_extrusion_paths(const GCodePreviewData& preview_dat
if (! values.empty()) {
m_gcode_preview_volume_index.first_volumes.emplace_back(GCodePreviewVolumeIndex::Extrusion, role, (unsigned int)m_volumes.volumes.size());
for (const float value : values)
roles_filters.back().emplace_back(value, m_volumes.new_toolpath_volume(Helper::path_color(preview_data, tool_colors, value).rgba, vertex_buffer_prealloc_size));
roles_filters.back().emplace_back(value, m_volumes.new_toolpath_volume(Helper::path_color(preview_data, tool_colors, value).rgba.data(), vertex_buffer_prealloc_size));
}
}
}
@ -6002,7 +5903,7 @@ inline void travel_paths_internal(
by_type.reserve(values.size());
// creates a new volume for each feedrate
for (TYPE type : values)
by_type.emplace_back(type, volumes.new_nontoolpath_volume(func_color(type).rgba, VERTEX_BUFFER_RESERVE_SIZE));
by_type.emplace_back(type, volumes.new_nontoolpath_volume(func_color(type).rgba.data(), VERTEX_BUFFER_RESERVE_SIZE));
}
// populates volumes
@ -6049,19 +5950,19 @@ void GLCanvas3D::_load_gcode_travel_paths(const GCodePreviewData& preview_data,
case GCodePreviewData::Extrusion::Feedrate:
travel_paths_internal<float>(preview_data,
[](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.feedrate; },
[&preview_data](const float feedrate) -> const GCodePreviewData::Color { return preview_data.get_feedrate_color(feedrate); },
[&preview_data](const float feedrate) -> const Color { return preview_data.get_feedrate_color(feedrate); },
m_volumes, m_initialized);
break;
case GCodePreviewData::Extrusion::Tool:
travel_paths_internal<unsigned int>(preview_data,
[](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.extruder_id; },
[&tool_colors](const unsigned int extruder_id) -> const GCodePreviewData::Color { assert((extruder_id + 1) * 4 <= tool_colors.size()); return GCodePreviewData::Color(tool_colors.data() + extruder_id * 4); },
[&tool_colors](const unsigned int extruder_id) -> const Color { assert((extruder_id + 1) * 4 <= tool_colors.size()); return Color(tool_colors.data() + extruder_id * 4); },
m_volumes, m_initialized);
break;
default:
travel_paths_internal<unsigned int>(preview_data,
[](const GCodePreviewData::Travel::Polyline &polyline) { return polyline.type; },
[&preview_data](const unsigned int type) -> const GCodePreviewData::Color& { return preview_data.travel.type_colors[type]; },
[&preview_data](const unsigned int type) -> const Color& { return preview_data.travel.type_colors[type]; },
m_volumes, m_initialized);
break;
}

View file

@ -83,7 +83,6 @@ template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
using HeightProfileSmoothEvent = Event<HeightProfileSmoothingParams>;
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_REMOVE_OBJECT, SimpleEvent);
@ -106,11 +105,9 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
class GLCanvas3D
{
@ -160,17 +157,10 @@ private:
private:
static const float THICKNESS_BAR_WIDTH;
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
static const float THICKNESS_RESET_BUTTON_HEIGHT;
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
bool m_enabled;
Shader m_shader;
unsigned int m_z_texture_id;
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
mutable GLTexture m_tooltip_texture;
mutable GLTexture m_reset_texture;
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
// Not owned by LayersEditing.
const DynamicPrintConfig *m_config;
// ModelObject for the currently selected object (Model::objects[last_object_id]).
@ -182,10 +172,8 @@ private:
std::vector<double> m_layer_height_profile;
bool m_layer_height_profile_modified;
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
mutable float m_adaptive_cusp;
mutable float m_adaptive_quality;
mutable HeightProfileSmoothingParams m_smooth_params;
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
class LayersTexture
{
@ -233,24 +221,13 @@ private:
void adjust_layer_height_profile();
void accept_changes(GLCanvas3D& canvas);
void reset_layer_height_profile(GLCanvas3D& canvas);
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp);
void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn);
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void adaptive_layer_height_profile(GLCanvas3D& canvas, float quality_factor);
void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params);
static float get_cursor_z_relative(const GLCanvas3D& canvas);
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y);
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
static Rect get_bar_rect_screen(const GLCanvas3D& canvas);
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
static Rect get_reset_rect_screen(const GLCanvas3D& canvas);
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
static Rect get_reset_rect_viewport(const GLCanvas3D& canvas);
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
float object_max_z() const { return m_object_max_z; }
@ -259,18 +236,11 @@ private:
private:
bool is_initialized() const;
void generate_layer_height_texture();
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const;
void _render_reset_texture(const Rect& reset_rect) const;
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const;
void render_profile(const Rect& bar_rect) const;
void update_slicing_parameters();
static float thickness_bar_width(const GLCanvas3D &canvas);
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
static float reset_button_height(const GLCanvas3D &canvas);
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
};
struct Mouse
@ -417,7 +387,6 @@ private:
std::unique_ptr<RetinaHelper> m_retina_helper;
#endif
bool m_in_render;
bool m_render_enabled;
LegendTexture m_legend_texture;
WarningTexture m_warning_texture;
wxTimer m_timer;
@ -435,7 +404,9 @@ private:
bool m_use_clipping_planes;
mutable SlaCap m_sla_caps[2];
std::string m_sidebar_field;
bool m_keep_dirty;
// when true renders an extra frame by not resetting m_dirty to false
// see request_extra_frame()
bool m_extra_frame_requested;
mutable GLVolumeCollection m_volumes;
Selection m_selection;
@ -476,7 +447,7 @@ private:
RenderStats m_render_stats;
#endif // ENABLE_RENDER_STATISTICS
int m_imgui_undo_redo_hovered_pos{ -1 };
mutable int m_imgui_undo_redo_hovered_pos{ -1 };
int m_selected_extruder;
public:
@ -536,11 +507,9 @@ public:
bool is_layers_editing_enabled() const;
bool is_layers_editing_allowed() const;
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
void reset_layer_height_profile();
void adaptive_layer_height_profile(float cusp);
void adaptive_layer_height_profile(float quality_factor);
void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params);
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
bool is_reload_delayed() const;
@ -555,9 +524,6 @@ public:
void enable_dynamic_background(bool enable);
void allow_multisample(bool allow);
void enable_render(bool enable) { m_render_enabled = enable; }
bool is_render_enabled() const { return m_render_enabled; }
void zoom_to_bed();
void zoom_to_volumes();
void zoom_to_selection();
@ -571,7 +537,7 @@ public:
#if ENABLE_THUMBNAIL_GENERATOR
// printable_only == false -> render also non printable volumes as grayed
// parts_only == false -> render also sla support and pad
void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
void render_thumbnail(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
#endif // ENABLE_THUMBNAIL_GENERATOR
void select_all();
@ -666,9 +632,7 @@ public:
void set_cursor(ECursorType type);
void msw_rescale();
bool is_keeping_dirty() const { return m_keep_dirty; }
void start_keeping_dirty() { m_keep_dirty = true; }
void stop_keeping_dirty() { m_keep_dirty = false; }
void request_extra_frame() { m_extra_frame_requested = true; }
int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); }
void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); }
@ -685,6 +649,7 @@ private:
bool _init_toolbars();
bool _init_main_toolbar();
bool _init_undoredo_toolbar();
bool _init_view_toolbar();
bool _set_current();
void _resize(unsigned int w, unsigned int h);
@ -723,15 +688,15 @@ private:
#endif // ENABLE_SHOW_CAMERA_TARGET
void _render_sla_slices() const;
void _render_selection_sidebar_hints() const;
void _render_undo_redo_stack(const bool is_undo, float pos_x);
void _render_undo_redo_stack(const bool is_undo, float pos_x) const;
#if ENABLE_THUMBNAIL_GENERATOR
void _render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
void _render_thumbnail_internal(ThumbnailData& thumbnail_data, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
// render thumbnail using an off-screen framebuffer
void _render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
void _render_thumbnail_framebuffer(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
// render thumbnail using an off-screen framebuffer when GLEW_EXT_framebuffer_object is supported
void _render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
void _render_thumbnail_framebuffer_ext(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
// render thumbnail using the default framebuffer
void _render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
void _render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background) const;
#endif // ENABLE_THUMBNAIL_GENERATOR
void _update_volumes_hover_state() const;

View file

@ -69,8 +69,7 @@ namespace GUI {
return;
const Camera& camera = canvas.get_camera();
float zoom = (float)camera.get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)camera.get_inv_zoom();
Size cnv_size = canvas.get_canvas_size();
float cnv_half_width = 0.5f * (float)cnv_size.get_width();

View file

@ -142,7 +142,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char*
int file_length = (int)vs.tellg();
vs.seekg(0, vs.beg);
std::string vertex_shader(file_length, '\0');
vs.read(const_cast<char*>(vertex_shader.data()), file_length);
vs.read(vertex_shader.data(), file_length);
if (!vs.good())
return false;
@ -156,7 +156,7 @@ bool GLShader::load_from_file(const char* fragment_shader_filename, const char*
file_length = (int)fs.tellg();
fs.seekg(0, fs.beg);
std::string fragment_shader(file_length, '\0');
fs.read(const_cast<char*>(fragment_shader.data()), file_length);
fs.read(fragment_shader.data(), file_length);
if (!fs.good())
return false;

View file

@ -168,12 +168,26 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
if (filenames.empty() || states.empty() || (sprite_size_px == 0))
return false;
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
// every tile needs to have a 1px border around it to avoid artifacts when linear sampling on its edges
unsigned int sprite_size_px_ex = sprite_size_px + 1;
m_width = 1 + (int)(sprite_size_px_ex * states.size());
m_height = 1 + (int)(sprite_size_px_ex * filenames.size());
#else
m_width = (int)(sprite_size_px * states.size());
m_height = (int)(sprite_size_px * filenames.size());
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
int n_pixels = m_width * m_height;
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
int sprite_n_pixels = sprite_size_px_ex * sprite_size_px_ex;
int sprite_stride = sprite_size_px_ex * 4;
#else
int sprite_n_pixels = sprite_size_px * sprite_size_px;
int sprite_bytes = sprite_n_pixels * 4;
int sprite_stride = sprite_size_px * 4;
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
int sprite_bytes = sprite_n_pixels * 4;
if (n_pixels <= 0)
{
@ -211,7 +225,12 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
float scale = (float)sprite_size_px / std::max(image->width, image->height);
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
// offset by 1 to leave the first pixel empty (both in x and y)
nsvgRasterize(rast, image, 1, 1, scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_stride);
#else
nsvgRasterize(rast, image, 0, 0, scale, sprite_data.data(), sprite_size_px, sprite_size_px, sprite_stride);
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
// makes white only copy of the sprite
::memcpy((void*)sprite_white_only_data.data(), (const void*)sprite_data.data(), sprite_bytes);
@ -231,7 +250,11 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
::memset((void*)&sprite_gray_only_data.data()[offset], 128, 3);
}
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
int sprite_offset_px = sprite_id * (int)sprite_size_px_ex * m_width;
#else
int sprite_offset_px = sprite_id * sprite_size_px * m_width;
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
int state_id = -1;
for (const std::pair<int, bool>& state : states)
{
@ -250,6 +273,23 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
// applies background, if needed
if (state.second)
{
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
float inv_255 = 1.0f / 255.0f;
// offset by 1 to leave the first pixel empty (both in x and y)
for (unsigned int r = 1; r <= sprite_size_px; ++r)
{
unsigned int offset_r = r * sprite_size_px_ex;
for (unsigned int c = 1; c <= sprite_size_px; ++c)
{
unsigned int offset = (offset_r + c) * 4;
float alpha = (float)output_data.data()[offset + 3] * inv_255;
output_data.data()[offset + 0] = (unsigned char)(output_data.data()[offset + 0] * alpha);
output_data.data()[offset + 1] = (unsigned char)(output_data.data()[offset + 1] * alpha);
output_data.data()[offset + 2] = (unsigned char)(output_data.data()[offset + 2] * alpha);
output_data.data()[offset + 3] = (unsigned char)(128 * (1.0f - alpha) + output_data.data()[offset + 3] * alpha);
}
}
#else
for (int i = 0; i < sprite_n_pixels; ++i)
{
int offset = i * 4;
@ -259,13 +299,22 @@ bool GLTexture::load_from_svg_files_as_sprites_array(const std::vector<std::stri
output_data.data()[offset + 2] = (unsigned char)(output_data.data()[offset + 2] * alpha);
output_data.data()[offset + 3] = (unsigned char)(128 * (1.0f - alpha) + output_data.data()[offset + 3] * alpha);
}
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
}
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
int state_offset_px = sprite_offset_px + state_id * sprite_size_px_ex;
for (int j = 0; j < (int)sprite_size_px_ex; ++j)
{
::memcpy((void*)&data.data()[(state_offset_px + j * m_width) * 4], (const void*)&output_data.data()[j * sprite_stride], sprite_stride);
}
#else
int state_offset_px = sprite_offset_px + state_id * sprite_size_px;
for (int j = 0; j < (int)sprite_size_px; ++j)
{
::memcpy((void*)&data.data()[(state_offset_px + j * m_width) * 4], (const void*)&output_data.data()[j * sprite_stride], sprite_stride);
}
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
}
nsvgDelete(image);

View file

@ -86,7 +86,35 @@ bool GLToolbarItem::update_enabled_state()
void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
{
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
auto uvs = [this](unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) ->GLTexture::Quad_UVs
{
assert((tex_width != 0) && (tex_height != 0));
GLTexture::Quad_UVs ret;
// tiles in the texture are spaced by 1 pixel
float icon_size_px = (float)(tex_width - 1) / (float)Num_States;
float inv_tex_width = 1.0f / (float)tex_width;
float inv_tex_height = 1.0f / (float)tex_height;
// tiles in the texture are spaced by 1 pixel
float u_offset = 1.0f * inv_tex_width;
float v_offset = 1.0f * inv_tex_height;
float du = icon_size_px * inv_tex_width;
float dv = icon_size_px * inv_tex_height;
float left = u_offset + (float)m_state * du;
float right = left + du - u_offset;
float top = v_offset + (float)m_data.sprite_id * dv;
float bottom = top + dv - v_offset;
ret.left_top = { left, top };
ret.left_bottom = { left, bottom };
ret.right_bottom = { right, bottom };
ret.right_top = { right, top };
return ret;
};
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, uvs(tex_width, tex_height, icon_size));
#else
GLTexture::render_sub_texture(tex_id, left, right, bottom, top, get_uvs(tex_width, tex_height, icon_size));
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
if (is_pressed())
{
@ -97,6 +125,7 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b
}
}
#if !ENABLE_MODIFIED_TOOLBAR_TEXTURES
GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const
{
GLTexture::Quad_UVs uvs;
@ -110,14 +139,14 @@ GLTexture::Quad_UVs GLToolbarItem::get_uvs(unsigned int tex_width, unsigned int
float right = left + scaled_icon_width;
float top = (float)m_data.sprite_id * scaled_icon_height;
float bottom = top + scaled_icon_height;
uvs.left_top = { left, top };
uvs.left_bottom = { left, bottom };
uvs.right_bottom = { right, bottom };
uvs.right_top = { right, top };
return uvs;
}
#endif // !ENABLE_MODIFIED_TOOLBAR_TEXTURES
BackgroundTexture::Metadata::Metadata()
: filename("")
@ -624,8 +653,7 @@ std::string GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLC
{
// NB: mouse_pos is already scaled appropriately
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
float factor = m_layout.scale * inv_zoom;
Size cnv_size = parent.get_canvas_size();
@ -729,8 +757,7 @@ std::string GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCan
{
// NB: mouse_pos is already scaled appropriately
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
float factor = m_layout.scale * inv_zoom;
Size cnv_size = parent.get_canvas_size();
@ -846,8 +873,7 @@ int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3
{
// NB: mouse_pos is already scaled appropriately
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
float factor = m_layout.scale * inv_zoom;
Size cnv_size = parent.get_canvas_size();
@ -920,8 +946,7 @@ int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D&
{
// NB: mouse_pos is already scaled appropriately
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
float factor = m_layout.scale * inv_zoom;
Size cnv_size = parent.get_canvas_size();
@ -1073,8 +1098,7 @@ void GLToolbar::render_horizontal(const GLCanvas3D& parent) const
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
float factor = inv_zoom * m_layout.scale;
float scaled_icons_size = m_layout.icons_size * factor;
@ -1122,8 +1146,7 @@ void GLToolbar::render_vertical(const GLCanvas3D& parent) const
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
float zoom = (float)parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)parent.get_camera().get_inv_zoom();
float factor = inv_zoom * m_layout.scale;
float scaled_icons_size = m_layout.icons_size * factor;
@ -1194,7 +1217,12 @@ bool GLToolbar::generate_icons_texture() const
states.push_back(std::make_pair(1, true));
}
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_layout.icons_size * m_layout.scale), true);
unsigned int sprite_size_px = (unsigned int)(m_layout.icons_size * m_layout.scale);
// // force even size
// if (sprite_size_px % 2 != 0)
// sprite_size_px += 1;
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false);
if (res)
m_icons_texture_dirty = false;

View file

@ -143,7 +143,9 @@ public:
void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
private:
#if !ENABLE_MODIFIED_TOOLBAR_TEXTURES
GLTexture::Quad_UVs get_uvs(unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const;
#endif // !ENABLE_MODIFIED_TOOLBAR_TEXTURES
void set_visible(bool visible) { m_data.visible = visible; }
friend class GLToolbar;
@ -293,6 +295,7 @@ public:
bool is_any_item_pressed() const;
unsigned int get_items_count() const { return (unsigned int)m_items.size(); }
int get_item_id(const std::string& name) const;
void force_left_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Left, item_id, parent, false); }

View file

@ -46,6 +46,7 @@
#include "SysInfoDialog.hpp"
#include "KBShortcutsDialog.hpp"
#include "UpdateDialogs.hpp"
#include "RemovableDriveManager.hpp"
#ifdef __WXMSW__
#include <Shlobj.h>
@ -261,6 +262,8 @@ bool GUI_App::on_init_inner()
m_printhost_job_queue.reset(new PrintHostJobQueue(mainframe->printhost_queue_dlg()));
RemovableDriveManager::get_instance().init();
Bind(wxEVT_IDLE, [this](wxIdleEvent& event)
{
if (! plater_)
@ -271,6 +274,10 @@ bool GUI_App::on_init_inner()
this->obj_manipul()->update_if_dirty();
#if !__APPLE__
RemovableDriveManager::get_instance().update(wxGetLocalTime(), true);
#endif
// Preset updating & Configwizard are done after the above initializations,
// and after MainFrame is created & shown.
// The extra CallAfter() is needed because of Mac, where this is the only way
@ -283,7 +290,7 @@ bool GUI_App::on_init_inner()
PresetUpdater::UpdateResult updater_result;
try {
updater_result = preset_updater->config_update();
updater_result = preset_updater->config_update(app_config->orig_version());
if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
mainframe->Close();
} else if (updater_result == PresetUpdater::R_INCOMPAT_CONFIGURED) {
@ -298,6 +305,7 @@ bool GUI_App::on_init_inner()
preset_updater->slic3r_update_notify();
preset_updater->sync(preset_bundle);
});
}
});
@ -333,17 +341,12 @@ unsigned GUI_App::get_colour_approx_luma(const wxColour &colour)
}
bool GUI_App::dark_mode()
{
const unsigned luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
return luma < 128;
}
bool GUI_App::dark_mode_menus()
{
#if __APPLE__
return mac_dark_mode();
#else
return dark_mode();
const unsigned luma = get_colour_approx_luma(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
return luma < 128;
#endif
}
@ -466,6 +469,9 @@ void GUI_App::recreate_GUI()
dlg.Update(30, _(L("Recreating")) + dots);
topwindow->Destroy();
// For this moment ConfigWizard is deleted, invalidate it
m_wizard = nullptr;
}
dlg.Update(80, _(L("Loading of current presets")) + dots);

View file

@ -109,7 +109,6 @@ public:
static unsigned get_colour_approx_luma(const wxColour &colour);
static bool dark_mode();
static bool dark_mode_menus();
void init_label_colours();
void update_label_colours_from_appconfig();
void init_fonts();

View file

@ -59,6 +59,11 @@ static PrinterTechnology printer_technology()
return wxGetApp().preset_bundle->printers.get_selected_preset().printer_technology();
}
static const Selection& scene_selection()
{
return wxGetApp().plater()->canvas3D()->get_selection();
}
// Config from current edited printer preset
static DynamicPrintConfig& printer_config()
{
@ -807,7 +812,7 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
if (!item) {
if (col == nullptr) {
if (wxOSX)
if (wxOSX && !multiple_selection())
UnselectAll();
else if (!evt_context_menu)
// Case, when last item was deleted and under GTK was called wxEVT_DATAVIEW_SELECTION_CHANGED,
@ -822,8 +827,13 @@ void ObjectList::list_manipulation(bool evt_context_menu/* = false*/)
}
if (wxOSX && item && col) {
wxDataViewItemArray sels;
GetSelections(sels);
UnselectAll();
Select(item);
if (sels.Count() > 1)
SetSelections(sels);
else
Select(item);
}
const wxString title = col->GetTitle();
@ -1182,7 +1192,13 @@ void ObjectList::get_settings_choice(const wxString& category_name)
{
wxArrayString names;
wxArrayInt selections;
wxDataViewItem item = GetSelection();
/* If we try to add settings for object/part from 3Dscene,
* for the second try there is selected ItemSettings in ObjectList.
* So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes
*/
const wxDataViewItem selected_item = GetSelection();
wxDataViewItem item = m_objects_model->GetItemType(selected_item) & itSettings ? m_objects_model->GetParent(selected_item) : selected_item;
const ItemType item_type = m_objects_model->GetItemType(item);
@ -1307,9 +1323,18 @@ void ObjectList::get_settings_choice(const wxString& category_name)
void ObjectList::get_freq_settings_choice(const wxString& bundle_name)
{
std::vector<std::string> options = get_options_for_bundle(bundle_name);
wxDataViewItem item = GetSelection();
const Selection& selection = scene_selection();
const wxDataViewItem sel_item = // when all instances in object are selected
GetSelectedItemsCount() > 1 && selection.is_single_full_object() ?
m_objects_model->GetItemById(selection.get_object_idx()) :
GetSelection();
ItemType item_type = m_objects_model->GetItemType(item);
/* If we try to add settings for object/part from 3Dscene,
* for the second try there is selected ItemSettings in ObjectList.
* So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes
*/
wxDataViewItem item = m_objects_model->GetItemType(sel_item) & itSettings ? m_objects_model->GetParent(sel_item) : sel_item;
const ItemType item_type = m_objects_model->GetItemType(item);
/* Because of we couldn't edited layer_height for ItVolume from settings list,
* correct options according to the selected item type :
@ -1395,17 +1420,23 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu)
const ConfigOptionMode mode = wxGetApp().get_mode();
wxWindow* parent = wxGetApp().plater();
if (mode == comAdvanced) {
append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].first), "",
[this](wxCommandEvent&) { load_subobject(ModelVolumeType::MODEL_PART); }, ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].second);
[this](wxCommandEvent&) { load_subobject(ModelVolumeType::MODEL_PART); },
ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::MODEL_PART)].second, nullptr,
[this]() { return is_instance_or_object_selected(); }, parent);
}
if (mode == comSimple) {
append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].first), "",
[this](wxCommandEvent&) { load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_ENFORCER); },
ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].second);
ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_ENFORCER)].second, nullptr,
[this]() { return is_instance_or_object_selected(); }, parent);
append_menu_item(menu, wxID_ANY, _(ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)].first), "",
[this](wxCommandEvent&) { load_generic_subobject(L("Box"), ModelVolumeType::SUPPORT_BLOCKER); },
ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)].second);
ADD_VOLUME_MENU_ITEMS[int(ModelVolumeType::SUPPORT_BLOCKER)].second, nullptr,
[this]() { return is_instance_or_object_selected(); }, parent);
return;
}
@ -1415,7 +1446,8 @@ void ObjectList::append_menu_items_add_volume(wxMenu* menu)
auto& item = ADD_VOLUME_MENU_ITEMS[type];
wxMenu* sub_menu = append_submenu_add_generic(menu, ModelVolumeType(type));
append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second);
append_submenu(menu, sub_menu, wxID_ANY, _(item.first), "", item.second,
[this]() { return is_instance_or_object_selected(); }, parent);
}
}
@ -1426,10 +1458,17 @@ wxMenuItem* ObjectList::append_menu_item_split(wxMenu* menu)
[this]() { return is_splittable(); }, wxGetApp().plater());
}
wxMenuItem* ObjectList::append_menu_item_layers_editing(wxMenu* menu)
bool ObjectList::is_instance_or_object_selected()
{
const Selection& selection = scene_selection();
return selection.is_single_full_instance() || selection.is_single_full_object();
}
wxMenuItem* ObjectList::append_menu_item_layers_editing(wxMenu* menu, wxWindow* parent)
{
return append_menu_item(menu, wxID_ANY, _(L("Height range Modifier")), "",
[this](wxCommandEvent&) { layers_editing(); }, "edit_layers_all", menu);
[this](wxCommandEvent&) { layers_editing(); }, "edit_layers_all", menu,
[this]() { return is_instance_or_object_selected(); }, parent);
}
wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
@ -1470,6 +1509,12 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
#endif
menu->DestroySeparators(); // delete old separators
// If there are selected more then one instance but not all of them
// don't add settings menu items
const Selection& selection = scene_selection();
if (selection.is_multiple_full_instance() && !selection.is_single_full_object())
return nullptr;
const auto sel_vol = get_selected_model_volume();
if (sel_vol && sel_vol->type() >= ModelVolumeType::SUPPORT_ENFORCER)
return nullptr;
@ -1485,7 +1530,8 @@ wxMenuItem* ObjectList::append_menu_item_settings(wxMenu* menu_)
menu->SetFirstSeparator();
// Add frequently settings
const bool is_object_settings = m_objects_model->GetItemType(GetSelection()) == itObject;
const ItemType item_type = m_objects_model->GetItemType(GetSelection());
const bool is_object_settings = item_type & itObject || item_type & itInstance || selection.is_single_full_object();
create_freq_settings_popupmenu(menu, is_object_settings);
if (mode == comAdvanced)
@ -1511,15 +1557,39 @@ wxMenuItem* ObjectList::append_menu_item_change_type(wxMenu* menu)
wxMenuItem* ObjectList::append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent)
{
return append_menu_item(menu, wxID_ANY, _(L("Set as a Separated Object")), "",
[this](wxCommandEvent&) { split_instances(); }, "", menu, [](){return wxGetApp().plater()->can_set_instance_to_object(); }, parent);
wxMenuItem* menu_item = append_menu_item(menu, wxID_ANY, _(L("Set as a Separated Object")), "",
[this](wxCommandEvent&) { split_instances(); }, "", menu);
/* New behavior logic:
* 1. Split Object to several separated object, if ALL instances are selected
* 2. Separate selected instances from the initial object to the separated object,
* if some (not all) instances are selected
*/
parent->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt)
{
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
evt.SetText(selection.is_single_full_object() ?
_(L("Set as a Separated Objects")) : _(L("Set as a Separated Object")));
evt.Enable(wxGetApp().plater()->can_set_instance_to_object());
}, menu_item->GetId());
return menu_item;
}
wxMenuItem* ObjectList::append_menu_item_printable(wxMenu* menu, wxWindow* /*parent*/)
{
return append_menu_check_item(menu, wxID_ANY, _(L("Printable")), "", [](wxCommandEvent&) {
wxGetApp().plater()->canvas3D()->get_selection().toggle_instance_printable_state();
}, menu);
return append_menu_check_item(menu, wxID_ANY, _(L("Printable")), "", [this](wxCommandEvent&) {
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
wxDataViewItem item;
if (GetSelectedItemsCount() > 1 && selection.is_single_full_object())
item = m_objects_model->GetItemById(selection.get_object_idx());
else
item = GetSelection();
if (item)
toggle_printable_state(item);
}, menu);
}
void ObjectList::append_menu_items_osx(wxMenu* menu)
@ -1552,8 +1622,14 @@ void ObjectList::append_menu_item_export_stl(wxMenu* menu) const
void ObjectList::append_menu_item_reload_from_disk(wxMenu* menu) const
{
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater());
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu);
#else
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu,
[]() { return wxGetApp().plater()->can_reload_from_disk(); }, wxGetApp().plater());
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
}
void ObjectList::append_menu_item_change_extruder(wxMenu* menu) const
@ -1615,7 +1691,7 @@ void ObjectList::create_object_popupmenu(wxMenu *menu)
menu->AppendSeparator();
// Layers Editing for object
append_menu_item_layers_editing(menu);
append_menu_item_layers_editing(menu, wxGetApp().plater());
menu->AppendSeparator();
// rest of a object_menu will be added later in:
@ -1659,17 +1735,6 @@ void ObjectList::create_part_popupmenu(wxMenu *menu)
void ObjectList::create_instance_popupmenu(wxMenu*menu)
{
m_menu_item_split_instances = append_menu_item_instance_to_object(menu, wxGetApp().plater());
/* New behavior logic:
* 1. Split Object to several separated object, if ALL instances are selected
* 2. Separate selected instances from the initial object to the separated object,
* if some (not all) instances are selected
*/
wxGetApp().plater()->Bind(wxEVT_UPDATE_UI, [](wxUpdateUIEvent& evt) {
// evt.Enable(can_split_instances()); }, m_menu_item_split_instances->GetId());
evt.SetText(wxGetApp().plater()->canvas3D()->get_selection().is_single_full_object() ?
_(L("Set as a Separated Objects")) : _(L("Set as a Separated Object")));
}, m_menu_item_split_instances->GetId());
}
void ObjectList::create_default_popupmenu(wxMenu*menu)
@ -1683,13 +1748,22 @@ wxMenu* ObjectList::create_settings_popupmenu(wxMenu *parent_menu)
wxMenu *menu = new wxMenu;
settings_menu_hierarchy settings_menu;
const bool is_part = m_objects_model->GetParent(GetSelection()) != wxDataViewItem(nullptr);
/* If we try to add settings for object/part from 3Dscene,
* for the second try there is selected ItemSettings in ObjectList.
* So, check if selected item isn't SettingsItem. And get a SettingsItem's parent item, if yes
*/
const wxDataViewItem selected_item = GetSelection();
wxDataViewItem item = m_objects_model->GetItemType(selected_item) & itSettings ? m_objects_model->GetParent(selected_item) : selected_item;
const bool is_part = !(m_objects_model->GetItemType(item) == itObject || scene_selection().is_single_full_object());
get_options_menu(settings_menu, is_part);
for (auto cat : settings_menu) {
append_menu_item(menu, wxID_ANY, _(cat.first), "",
[menu, this](wxCommandEvent& event) { get_settings_choice(menu->GetLabel(event.GetId())); },
CATEGORY_ICON.find(cat.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(cat.first), parent_menu);
CATEGORY_ICON.find(cat.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(cat.first), parent_menu,
[this]() { return true; }, wxGetApp().plater());
}
return menu;
@ -1709,7 +1783,8 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu, const bool is_obje
append_menu_item(menu, wxID_ANY, _(it.first), "",
[menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); },
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu);
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu,
[this]() { return true; }, wxGetApp().plater());
}
#if 0
// Add "Quick" settings bundles
@ -1722,7 +1797,8 @@ void ObjectList::create_freq_settings_popupmenu(wxMenu *menu, const bool is_obje
append_menu_item(menu, wxID_ANY, wxString::Format(_(L("Quick Add Settings (%s)")), _(it.first)), "",
[menu, this](wxCommandEvent& event) { get_freq_settings_choice(menu->GetLabel(event.GetId())); },
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu);
CATEGORY_ICON.find(it.first) == CATEGORY_ICON.end() ? wxNullBitmap : CATEGORY_ICON.at(it.first), menu,
[this]() { return true; }, wxGetApp().plater());
}
#endif
}
@ -1852,7 +1928,7 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode
if (obj_idx < 0)
return;
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
const Selection& selection = scene_selection();
assert(obj_idx == selection.get_object_idx());
/** Any changes of the Object's composition is duplicated for all Object's Instances
@ -2177,9 +2253,13 @@ void ObjectList::split()
void ObjectList::layers_editing()
{
const auto item = GetSelection();
const int obj_idx = get_selected_obj_idx();
if (!item || obj_idx < 0)
const Selection& selection = scene_selection();
const int obj_idx = selection.get_object_idx();
wxDataViewItem item = obj_idx >= 0 && GetSelectedItemsCount() > 1 && selection.is_single_full_object() ?
m_objects_model->GetItemById(obj_idx) :
GetSelection();
if (!item)
return;
const wxDataViewItem obj_item = m_objects_model->GetTopParent(item);
@ -2292,7 +2372,7 @@ bool ObjectList::selected_instances_of_same_object()
bool ObjectList::can_split_instances()
{
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
const Selection& selection = scene_selection();
return selection.is_multiple_full_instance() || selection.is_single_full_instance();
}
@ -2320,7 +2400,7 @@ void ObjectList::part_selection_changed()
{
og_name = _(L("Group manipulation"));
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
const Selection& selection = scene_selection();
// don't show manipulation panel for case of all Object's parts selection
update_and_show_manipulations = !selection.is_single_full_instance();
}
@ -2855,6 +2935,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, coordf_t la
layer_height <= get_max_layer_height(extruder_idx))
{
config->set_key_value("layer_height", new ConfigOptionFloat(layer_height));
changed_object(obj_idx);
return true;
}
@ -2876,6 +2957,7 @@ bool ObjectList::edit_layer_range(const t_layer_height_range& range, const t_lay
ranges.erase(range);
ranges[new_range] = config;
changed_object(obj_idx);
wxDataViewItem root_item = m_objects_model->GetLayerRootItem(m_objects_model->GetItemById(obj_idx));
// To avoid update selection after deleting of a selected item (under GTK)
@ -2928,7 +3010,7 @@ int ObjectList::get_selected_layers_range_idx() const
void ObjectList::update_selections()
{
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
const Selection& selection = scene_selection();
wxDataViewItemArray sels;
if ( ( m_selection_mode & (smSettings|smLayer|smLayerRoot) ) == 0)
@ -2958,7 +3040,8 @@ void ObjectList::update_selections()
else if (selection.is_single_full_object() || selection.is_multiple_full_object())
{
const Selection::ObjectIdxsToInstanceIdxsMap& objects_content = selection.get_content();
if (m_selection_mode & (smSettings | smLayer | smLayerRoot))
// it's impossible to select Settings, Layer or LayerRoot for several objects
if (!selection.is_multiple_full_object() && (m_selection_mode & (smSettings | smLayer | smLayerRoot)))
{
auto obj_idx = objects_content.begin()->first;
wxDataViewItem obj_item = m_objects_model->GetItemById(obj_idx);
@ -3635,7 +3718,7 @@ void ObjectList::instances_to_separated_objects(const int obj_idx)
void ObjectList::split_instances()
{
const Selection& selection = wxGetApp().plater()->canvas3D()->get_selection();
const Selection& selection = scene_selection();
const int obj_idx = selection.get_object_idx();
if (obj_idx == -1)
return;
@ -3802,8 +3885,8 @@ void ObjectList::show_multi_selection_menu()
GetSelections(sels);
for (const wxDataViewItem& item : sels)
if (!(m_objects_model->GetItemType(item) & (itVolume | itObject)))
// show this menu only for Object(s)/Volume(s) selection
if (!(m_objects_model->GetItemType(item) & (itVolume | itObject | itInstance)))
// show this menu only for Objects(Instances mixed with Objects)/Volumes selection
return;
wxMenu* menu = new wxMenu();
@ -3813,7 +3896,17 @@ void ObjectList::show_multi_selection_menu()
_(L("Select extruder number for selected objects and/or parts")),
[this](wxCommandEvent&) { extruder_selection(); }, "", menu);
PopupMenu(menu);
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu);
#else
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { wxGetApp().plater()->reload_from_disk(); }, "", menu, []() {
return wxGetApp().plater()->can_reload_from_disk();
}, wxGetApp().plater());
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
wxGetApp().plater()->PopupMenu(menu);
}
void ObjectList::extruder_selection()
@ -3891,8 +3984,15 @@ void ObjectList::update_after_undo_redo()
Plater::SuppressSnapshots suppress(wxGetApp().plater());
// Unselect all objects before deleting them, so that no change of selection is emitted during deletion.
unselect_objects();//this->UnselectAll();
/* To avoid execution of selection_changed()
* from wxEVT_DATAVIEW_SELECTION_CHANGED emitted from DeleteAll(),
* wrap this two functions into m_prevent_list_events *
* */
m_prevent_list_events = true;
this->UnselectAll();
m_objects_model->DeleteAll();
m_prevent_list_events = false;
size_t obj_idx = 0;
std::vector<size_t> obj_idxs;

View file

@ -226,11 +226,12 @@ public:
void get_settings_choice(const wxString& category_name);
void get_freq_settings_choice(const wxString& bundle_name);
void show_settings(const wxDataViewItem settings_item);
bool is_instance_or_object_selected();
wxMenu* append_submenu_add_generic(wxMenu* menu, const ModelVolumeType type);
void append_menu_items_add_volume(wxMenu* menu);
wxMenuItem* append_menu_item_split(wxMenu* menu);
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu);
wxMenuItem* append_menu_item_layers_editing(wxMenu* menu, wxWindow* parent);
wxMenuItem* append_menu_item_settings(wxMenu* menu);
wxMenuItem* append_menu_item_change_type(wxMenu* menu);
wxMenuItem* append_menu_item_instance_to_object(wxMenu* menu, wxWindow* parent);
@ -365,6 +366,8 @@ public:
void update_printable_state(int obj_idx, int instance_idx);
void toggle_printable_state(wxDataViewItem item);
void show_multi_selection_menu();
private:
#ifdef __WXOSX__
// void OnChar(wxKeyEvent& event);
@ -383,8 +386,6 @@ private:
void OnEditingStarted(wxDataViewEvent &event);
#endif /* __WXMSW__ */
void OnEditingDone(wxDataViewEvent &event);
void show_multi_selection_menu();
void extruder_selection();
void set_extruder_for_selected_items(const int extruder) const ;

View file

@ -341,11 +341,6 @@ void Preview::set_number_extruders(unsigned int number_extruders)
}
}
void Preview::set_canvas_as_dirty()
{
m_canvas->set_as_dirty();
}
void Preview::set_enabled(bool enabled)
{
m_enabled = enabled;
@ -538,7 +533,9 @@ void Preview::on_combochecklist_features(wxCommandEvent& evt)
void Preview::on_checkbox_travel(wxCommandEvent& evt)
{
m_gcode_preview_data->travel.is_visible = m_checkbox_travel->IsChecked();
refresh_print();
m_gcode_preview_data->ranges.feedrate.set_mode(GCodePreviewData::FeedrateKind::TRAVEL, m_gcode_preview_data->travel.is_visible);
// Rather than refresh, reload print so that speed color ranges get recomputed (affected by travel visibility)
reload_print();
}
void Preview::on_checkbox_retractions(wxCommandEvent& evt)
@ -569,7 +566,7 @@ void Preview::update_view_type(bool slice_completed)
{
const DynamicPrintConfig& config = wxGetApp().preset_bundle->project_config;
const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_height.empty() /*&&
const wxString& choice = !wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes.empty() /*&&
(wxGetApp().extruders_edited_cnt()==1 || !slice_completed) */?
_(L("Color Print")) :
config.option<ConfigOptionFloats>("wiping_volumes_matrix")->values.size() > 1 ?
@ -600,7 +597,7 @@ void Preview::create_double_slider()
Bind(wxCUSTOMEVT_TICKSCHANGED, [this](wxEvent&) {
Model& model = wxGetApp().plater()->model();
model.custom_gcode_per_height = m_slider->GetTicksValues();
model.custom_gcode_per_print_z = m_slider->GetTicksValues();
m_schedule_background_process();
update_view_type(false);
@ -646,7 +643,7 @@ void Preview::check_slider_values(std::vector<Model::CustomGCode>& ticks_from_mo
ticks_from_model.erase(std::remove_if(ticks_from_model.begin(), ticks_from_model.end(),
[layers_z](Model::CustomGCode val)
{
auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.height - DoubleSlider::epsilon());
auto it = std::lower_bound(layers_z.begin(), layers_z.end(), val.print_z - DoubleSlider::epsilon());
return it == layers_z.end();
}),
ticks_from_model.end());
@ -669,8 +666,11 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee
bool snap_to_min = force_sliders_full_range || m_slider->is_lower_at_min();
bool snap_to_max = force_sliders_full_range || m_slider->is_higher_at_max();
std::vector<Model::CustomGCode> &ticks_from_model = wxGetApp().plater()->model().custom_gcode_per_height;
check_slider_values(ticks_from_model, layers_z);
// Detect and set manipulation mode for double slider
update_double_slider_mode();
Model::CustomGCodeInfo &ticks_info_from_model = wxGetApp().plater()->model().custom_gcode_per_print_z;
check_slider_values(ticks_info_from_model.gcodes, layers_z);
m_slider->SetSliderValues(layers_z);
assert(m_slider->GetMinValue() == 0);
@ -692,12 +692,61 @@ void Preview::update_double_slider(const std::vector<double>& layers_z, bool kee
}
m_slider->SetSelectionSpan(idx_low, idx_high);
m_slider->SetTicksValues(ticks_from_model);
m_slider->SetTicksValues(ticks_info_from_model);
bool color_print_enable = (wxGetApp().plater()->printer_technology() == ptFFF);
m_slider->EnableTickManipulation(wxGetApp().plater()->printer_technology() == ptFFF);
}
m_slider->EnableTickManipulation(color_print_enable);
m_slider->SetManipulationState(wxGetApp().extruders_edited_cnt());
void Preview::update_double_slider_mode()
{
// true -> single-extruder printer profile OR
// multi-extruder printer profile , but whole model is printed by only one extruder
// false -> multi-extruder printer profile , and model is printed by several extruders
bool one_extruder_printed_model = true;
// extruder used for whole model for multi-extruder printer profile
int only_extruder = -1;
if (wxGetApp().extruders_edited_cnt() > 1)
{
const ModelObjectPtrs& objects = wxGetApp().plater()->model().objects;
// check if whole model uses just only one extruder
if (!objects.empty())
{
const int extruder = objects[0]->config.has("extruder") ?
objects[0]->config.option("extruder")->getInt() : 0;
auto is_one_extruder_printed_model = [objects, extruder]()
{
for (ModelObject* object : objects)
{
if (object->config.has("extruder") &&
object->config.option("extruder")->getInt() != extruder)
return false;
if (object->volumes.size() > 1)
for (ModelVolume* volume : object->volumes)
if (volume->config.has("extruder") &&
volume->config.option("extruder")->getInt() != extruder)
return false;
for (const auto& range : object->layer_config_ranges)
if (range.second.has("extruder") &&
range.second.option("extruder")->getInt() != extruder)
return false;
}
return true;
};
if (is_one_extruder_printed_model())
only_extruder = extruder;
else
one_extruder_printed_model = false;
}
}
m_slider->SetModeAndOnlyExtruder(one_extruder_printed_model, only_extruder);
}
void Preview::reset_double_slider()
@ -789,7 +838,7 @@ void Preview::load_print_as_fff(bool keep_z_range)
colors.push_back("#808080"); // gray color for pause print or custom G-code
if (!gcode_preview_data_valid)
color_print_values = wxGetApp().plater()->model().custom_gcode_per_height;
color_print_values = wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes;
}
else if (gcode_preview_data_valid || (m_gcode_preview_data->extrusion.view_type == GCodePreviewData::Extrusion::Tool) )
{

View file

@ -116,7 +116,6 @@ public:
void set_as_dirty();
void set_number_extruders(unsigned int number_extruders);
void set_canvas_as_dirty();
void set_enabled(bool enabled);
void bed_shape_changed();
void select_view(const std::string& direction);
@ -158,8 +157,9 @@ private:
void create_double_slider();
void check_slider_values(std::vector<Model::CustomGCode> &ticks_from_model,
const std::vector<double> &layers_z);
void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false);
void reset_double_slider();
void update_double_slider(const std::vector<double>& layers_z, bool keep_z_range = false);
void update_double_slider_mode();
// update DoubleSlider after keyDown in canvas
void update_double_slider_from_canvas(wxKeyEvent& event);

View file

@ -136,21 +136,39 @@ void GLGizmoCut::on_render_for_picking() const
void GLGizmoCut::on_render_input_window(float x, float y, float bottom_limit)
{
const float approx_height = m_imgui->scaled(11.0f);
y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
static float last_y = 0.0f;
static float last_h = 0.0f;
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->begin(_(L("Cut")), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
// adjust window position to avoid overlap the view toolbar
float win_h = ImGui::GetWindowHeight();
y = std::min(y, bottom_limit - win_h);
ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
if ((last_h != win_h) || (last_y != y))
{
// ask canvas for another frame to render the window in the correct position
m_parent.request_extra_frame();
if (last_h != win_h)
last_h = win_h;
if (last_y != y)
last_y = y;
}
ImGui::PushItemWidth(m_imgui->scaled(5.0f));
ImGui::InputDouble("Z", &m_cut_z, 0.0f, 0.0f, "%.2f");
ImGui::AlignTextToFramePadding();
m_imgui->text("Z");
ImGui::SameLine();
ImGui::PushItemWidth(m_imgui->get_style_scaling() * 150.0f);
ImGui::InputDouble("", &m_cut_z, 0.0f, 0.0f, "%.2f");
ImGui::Separator();
m_imgui->checkbox(_(L("Keep upper part")), m_keep_upper);
m_imgui->checkbox(_(L("Keep lower part")), m_keep_lower);
m_imgui->checkbox(_(L("Rotate lower part upwards")), m_rotate_lower);
ImGui::Separator();
m_imgui->disabled_begin(!m_keep_upper && !m_keep_lower);
const bool cut_clicked = m_imgui->button(_(L("Perform cut")));
m_imgui->disabled_end();

View file

@ -48,6 +48,11 @@ std::string GLGizmoMove3D::on_get_name() const
return (_(L("Move")) + " [M]").ToUTF8().data();
}
bool GLGizmoMove3D::on_is_activable() const
{
return !m_parent.get_selection().is_empty();
}
void GLGizmoMove3D::on_start_dragging()
{
if (m_hover_id != -1)
@ -166,25 +171,6 @@ void GLGizmoMove3D::on_render_for_picking() const
render_grabber_extension(Z, box, true);
}
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoMove3D::on_render_input_window(float x, float y, float bottom_limit)
{
const Selection& selection = m_parent.get_selection();
bool show_position = selection.is_single_full_instance();
const Vec3d& position = selection.get_bounding_box().center();
Vec3d displacement = show_position ? position : m_displacement;
wxString label = show_position ? _(L("Position (mm)")) : _(L("Displacement (mm)"));
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", displacement, 100.0f, "%.2f");
m_imgui->end();
}
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
double GLGizmoMove3D::calc_projection(const UpdateData& data) const
{
double projection = 0.0;

View file

@ -33,14 +33,12 @@ public:
protected:
virtual bool on_init();
virtual std::string on_get_name() const;
virtual bool on_is_activable() const;
virtual void on_start_dragging();
virtual void on_stop_dragging();
virtual void on_update(const UpdateData& data);
virtual void on_render() const;
virtual void on_render_for_picking() const;
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit);
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
private:
double calc_projection(const UpdateData& data) const;

View file

@ -449,6 +449,11 @@ std::string GLGizmoRotate3D::on_get_name() const
return (_(L("Rotate")) + " [R]").ToUTF8().data();
}
bool GLGizmoRotate3D::on_is_activable() const
{
return !m_parent.get_selection().is_empty();
}
void GLGizmoRotate3D::on_start_dragging()
{
if ((0 <= m_hover_id) && (m_hover_id < 3))
@ -475,21 +480,5 @@ void GLGizmoRotate3D::on_render() const
m_gizmos[Z].render();
}
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoRotate3D::on_render_input_window(float x, float y, float bottom_limit)
{
Vec3d rotation(Geometry::rad2deg(m_gizmos[0].get_angle()), Geometry::rad2deg(m_gizmos[1].get_angle()), Geometry::rad2deg(m_gizmos[2].get_angle()));
wxString label = _(L("Rotation (deg)"));
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", rotation, 100.0f, "%.2f");
m_imgui->end();
}
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
} // namespace GUI
} // namespace Slic3r

View file

@ -104,6 +104,7 @@ protected:
if (id < 3)
m_gizmos[id].disable_grabber(0);
}
virtual bool on_is_activable() const;
virtual void on_start_dragging();
virtual void on_stop_dragging();
virtual void on_update(const UpdateData& data)
@ -121,13 +122,8 @@ protected:
g.render_for_picking();
}
}
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit);
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
};
} // namespace GUI
} // namespace Slic3r

View file

@ -49,7 +49,8 @@ std::string GLGizmoScale3D::on_get_name() const
bool GLGizmoScale3D::on_is_activable() const
{
return !m_parent.get_selection().is_wipe_tower();
const Selection& selection = m_parent.get_selection();
return !selection.is_empty() && !selection.is_wipe_tower();
}
void GLGizmoScale3D::on_start_dragging()
@ -284,21 +285,6 @@ void GLGizmoScale3D::on_render_for_picking() const
render_grabbers_for_picking(m_parent.get_selection().get_bounding_box());
}
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoScale3D::on_render_input_window(float x, float y, float bottom_limit)
{
const Selection& selection = m_parent.get_selection();
bool single_instance = selection.is_single_full_instance();
wxString label = _(L("Scale (%)"));
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(label, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse);
m_imgui->input_vec3("", m_scale * 100.f, 100.0f, "%.2f");
m_imgui->end();
}
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
void GLGizmoScale3D::render_grabbers_connection(unsigned int id_1, unsigned int id_2) const
{
unsigned int grabbers_count = (unsigned int)m_grabbers.size();

View file

@ -50,9 +50,6 @@ protected:
virtual void on_update(const UpdateData& data);
virtual void on_render() const;
virtual void on_render_for_picking() const;
#if !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
virtual void on_render_input_window(float x, float y, float bottom_limit);
#endif // !DISABLE_MOVE_ROTATE_SCALE_GIZMOS_IMGUI
private:
void render_grabbers_connection(unsigned int id_1, unsigned int id_2) const;

View file

@ -686,6 +686,9 @@ void GLGizmoSlaSupports::make_line_segments() const
void GLGizmoSlaSupports::on_render_input_window(float x, float y, float bottom_limit)
{
static float last_y = 0.0f;
static float last_h = 0.0f;
if (!m_model_object)
return;
@ -697,12 +700,22 @@ RENDER_AGAIN:
//ImGui::SetNextWindowPos(ImVec2(x, y - std::max(0.f, y+window_size.y-bottom_limit) ));
//ImGui::SetNextWindowSize(ImVec2(window_size));
const float approx_height = m_imgui->scaled(18.0f);
y = std::min(y, bottom_limit - approx_height);
m_imgui->set_next_window_pos(x, y, ImGuiCond_Always);
m_imgui->set_next_window_bg_alpha(0.5f);
m_imgui->begin(on_get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse);
// adjust window position to avoid overlap the view toolbar
float win_h = ImGui::GetWindowHeight();
y = std::min(y, bottom_limit - win_h);
ImGui::SetWindowPos(ImVec2(x, y), ImGuiCond_Always);
if ((last_h != win_h) || (last_y != y))
{
// ask canvas for another frame to render the window in the correct position
m_parent.request_extra_frame();
if (last_h != win_h)
last_h = win_h;
if (last_y != y)
last_y = y;
}
// First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that:
const float settings_sliders_left = std::max(m_imgui->calc_text_size(m_desc.at("minimal_distance")).x, m_imgui->calc_text_size(m_desc.at("points_density")).x) + m_imgui->scaled(1.f);
@ -725,6 +738,7 @@ RENDER_AGAIN:
float diameter_upper_cap = static_cast<ConfigOptionFloat*>(wxGetApp().preset_bundle->sla_prints.get_edited_preset().config.option("support_pillar_diameter"))->value;
if (m_new_point_head_diameter > diameter_upper_cap)
m_new_point_head_diameter = diameter_upper_cap;
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("head_diameter"));
ImGui::SameLine(diameter_slider_left);
ImGui::PushItemWidth(window_width - diameter_slider_left);
@ -785,6 +799,7 @@ RENDER_AGAIN:
}
}
else { // not in editing mode:
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("minimal_distance"));
ImGui::SameLine(settings_sliders_left);
ImGui::PushItemWidth(window_width - settings_sliders_left);
@ -798,6 +813,7 @@ RENDER_AGAIN:
bool slider_edited = ImGui::IsItemEdited(); // someone is dragging the slider
bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("points_density"));
ImGui::SameLine(settings_sliders_left);
@ -828,7 +844,7 @@ RENDER_AGAIN:
if (generate)
auto_generate();
m_imgui->text("");
ImGui::Separator();
if (m_imgui->button(m_desc.at("manual_editing")))
switch_to_editing_mode();
@ -845,9 +861,12 @@ RENDER_AGAIN:
// Following is rendered in both editing and non-editing mode:
m_imgui->text("");
ImGui::Separator();
if (m_clipping_plane_distance == 0.f)
{
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view"));
}
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){

View file

@ -20,10 +20,6 @@ GLGizmosManager::GLGizmosManager(GLCanvas3D& parent)
, m_enabled(false)
, m_icons_texture_dirty(true)
, m_current(Undefined)
, m_overlay_icons_size(Default_Icons_Size)
, m_overlay_scale(1.0f)
, m_overlay_border(5.0f)
, m_overlay_gap_y(5.0f)
, m_tooltip("")
, m_serializing(false)
{
@ -53,19 +49,18 @@ size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const
return Undefined;
float cnv_h = (float)m_parent.get_canvas_size().get_height();
float height = get_total_overlay_height();
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float top_y = 0.5f * (cnv_h - height) + scaled_border;
float height = get_scaled_total_height();
float icons_size = m_layout.scaled_icons_size();
float border = m_layout.scaled_border();
float stride_y = m_layout.scaled_stride_y();
float top_y = 0.5f * (cnv_h - height) + border;
// is mouse horizontally in the area?
if ((scaled_border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= scaled_border + scaled_icons_size))) {
if ((border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= border + icons_size))) {
// which icon is it on?
size_t from_top = (size_t)((float)mouse_pos(1) - top_y)/scaled_stride_y;
size_t from_top = (size_t)((float)mouse_pos(1) - top_y) / stride_y;
// is it really on the icon or already past the border?
if ((float)mouse_pos(1) <= top_y + from_top*scaled_stride_y + scaled_icons_size) {
if ((float)mouse_pos(1) <= top_y + from_top * stride_y + icons_size) {
std::vector<size_t> selectable = get_selectable_idxs();
if (from_top < selectable.size())
return selectable[from_top];
@ -110,18 +105,18 @@ bool GLGizmosManager::init()
void GLGizmosManager::set_overlay_icon_size(float size)
{
if (m_overlay_icons_size != size)
if (m_layout.icons_size != size)
{
m_overlay_icons_size = size;
m_layout.icons_size = size;
m_icons_texture_dirty = true;
}
}
void GLGizmosManager::set_overlay_scale(float scale)
{
if (m_overlay_scale != scale)
if (m_layout.scale != scale)
{
m_overlay_scale = scale;
m_layout.scale = scale;
m_icons_texture_dirty = true;
}
}
@ -560,7 +555,7 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt)
update_data();
wxGetApp().obj_manipul()->set_dirty();
// Let the platter know that the dragging finished, so a delayed refresh
// Let the plater know that the dragging finished, so a delayed refresh
// of the scene with the background processing data should be performed.
m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_FINISHED));
// updates camera target constraints
@ -843,97 +838,111 @@ void GLGizmosManager::render_background(float left, float top, float right, floa
void GLGizmosManager::do_render_overlay() const
{
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
std::vector<size_t> selectable_idxs = get_selectable_idxs();
if (selectable_idxs.empty())
return;
#else
if (m_gizmos.empty())
return;
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
float cnv_w = (float)m_parent.get_canvas_size().get_width();
float cnv_h = (float)m_parent.get_canvas_size().get_height();
float zoom = (float)m_parent.get_camera().get_zoom();
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
float inv_zoom = (float)m_parent.get_camera().get_inv_zoom();
float height = get_total_overlay_height();
float width = get_total_overlay_width();
float scaled_border = m_overlay_border * m_overlay_scale * inv_zoom;
float height = get_scaled_total_height();
float width = get_scaled_total_width();
float zoomed_border = m_layout.scaled_border() * inv_zoom;
float top_x = (-0.5f * cnv_w) * inv_zoom;
float top_y = (0.5f * height) * inv_zoom;
float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom;
float zoomed_top_y = (0.5f * height) * inv_zoom;
float left = top_x;
float top = top_y;
float right = left + width * inv_zoom;
float bottom = top - height * inv_zoom;
float zoomed_left = zoomed_top_x;
float zoomed_top = zoomed_top_y;
float zoomed_right = zoomed_left + width * inv_zoom;
float zoomed_bottom = zoomed_top - height * inv_zoom;
render_background(left, top, right, bottom, scaled_border);
render_background(zoomed_left, zoomed_top, zoomed_right, zoomed_bottom, zoomed_border);
top_x += scaled_border;
top_y -= scaled_border;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale * inv_zoom;
zoomed_top_x += zoomed_border;
zoomed_top_y -= zoomed_border;
float icons_size = m_layout.scaled_icons_size();
float zoomed_icons_size = icons_size * inv_zoom;
float zoomed_stride_y = m_layout.scaled_stride_y() * inv_zoom;
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale * inv_zoom;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
unsigned int icons_texture_id = m_icons_texture.get_id();
int tex_width = m_icons_texture.get_width();
int tex_height = m_icons_texture.get_height();
float inv_tex_width = (tex_width != 0) ? 1.0f / tex_width : 0.0f;
float inv_tex_height = (tex_height != 0) ? 1.0f / tex_height : 0.0f;
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1))
return;
float du = (float)(tex_width - 1) / (4.0f * (float)tex_width); // 4 is the number of possible states if the icons
float dv = (float)(tex_height - 1) / (float)(m_gizmos.size() * tex_height);
// tiles in the texture are spaced by 1 pixel
float u_offset = 1.0f / (float)tex_width;
float v_offset = 1.0f / (float)tex_height;
#else
if ((icons_texture_id == 0) || (tex_width <= 0) || (tex_height <= 0))
return;
float inv_tex_width = (tex_width != 0) ? 1.0f / tex_width : 0.0f;
float inv_tex_height = (tex_height != 0) ? 1.0f / tex_height : 0.0f;
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
for (size_t idx : selectable_idxs)
#else
for (size_t idx : get_selectable_idxs())
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
{
GLGizmoBase* gizmo = m_gizmos[idx].get();
unsigned int sprite_id = gizmo->get_sprite_id();
int icon_idx = m_current == idx ? 2 : (m_hover == idx ? 1 : 0);
int icon_idx = (m_current == idx) ? 2 : ((m_hover == idx) ? 1 : (gizmo->is_activable()? 0 : 3));
#if ENABLE_MODIFIED_TOOLBAR_TEXTURES
float v_top = v_offset + sprite_id * dv;
float u_left = u_offset + icon_idx * du;
float v_bottom = v_top + dv - v_offset;
float u_right = u_left + du - u_offset;
#else
float u_icon_size = icons_size * inv_tex_width;
float v_icon_size = icons_size * inv_tex_height;
float u_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_width;
float v_icon_size = m_overlay_icons_size * m_overlay_scale * inv_tex_height;
float v_top = sprite_id * v_icon_size;
float u_left = icon_idx * u_icon_size;
float v_bottom = v_top + v_icon_size;
float u_right = u_left + u_icon_size;
#endif // ENABLE_MODIFIED_TOOLBAR_TEXTURES
GLTexture::render_sub_texture(icons_texture_id, top_x, top_x + scaled_icons_size, top_y - scaled_icons_size, top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } });
if (idx == m_current) {
float toolbar_top = cnv_h - m_parent.get_view_toolbar_height();
gizmo->render_input_window(width, 0.5f * cnv_h - top_y * zoom, toolbar_top);
gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top);
}
top_y -= scaled_stride_y;
zoomed_top_y -= zoomed_stride_y;
}
}
float GLGizmosManager::get_total_overlay_height() const
float GLGizmosManager::get_scaled_total_height() const
{
float scaled_icons_size = m_overlay_icons_size * m_overlay_scale;
float scaled_border = m_overlay_border * m_overlay_scale;
float scaled_gap_y = m_overlay_gap_y * m_overlay_scale;
float scaled_stride_y = scaled_icons_size + scaled_gap_y;
float height = 2.0f * scaled_border;
/*for (size_t idx=0; idx<m_gizmos.size(); ++idx)
{
if ((m_gizmos[idx] == nullptr) || !m_gizmos[idx]->is_selectable())
continue;
height += scaled_stride_y;
}*/
height += get_selectable_idxs().size() * scaled_stride_y;
return height - scaled_gap_y;
return m_layout.scale * (2.0f * m_layout.border + (float)get_selectable_idxs().size() * m_layout.stride_y() - m_layout.gap_y);
}
float GLGizmosManager::get_total_overlay_width() const
float GLGizmosManager::get_scaled_total_width() const
{
return (2.0f * m_overlay_border + m_overlay_icons_size) * m_overlay_scale;
return 2.0f * m_layout.scaled_border() + m_layout.scaled_icons_size();
}
GLGizmoBase* GLGizmosManager::get_current() const
{
if (m_current==Undefined || m_gizmos.empty())
return nullptr;
else
return m_gizmos[m_current].get();
return ((m_current == Undefined) || m_gizmos.empty()) ? nullptr : m_gizmos[m_current].get();
}
bool GLGizmosManager::generate_icons_texture() const
@ -951,11 +960,17 @@ bool GLGizmosManager::generate_icons_texture() const
}
std::vector<std::pair<int, bool>> states;
states.push_back(std::make_pair(1, false));
states.push_back(std::make_pair(0, false));
states.push_back(std::make_pair(0, true));
states.push_back(std::make_pair(1, false)); // Activable
states.push_back(std::make_pair(0, false)); // Hovered
states.push_back(std::make_pair(0, true)); // Selected
states.push_back(std::make_pair(2, false)); // Disabled
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, (unsigned int)(m_overlay_icons_size * m_overlay_scale), true);
unsigned int sprite_size_px = (unsigned int)m_layout.scaled_icons_size();
// // force even size
// if (sprite_size_px % 2 != 0)
// sprite_size_px += 1;
bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false);
if (res)
m_icons_texture_dirty = false;

View file

@ -64,12 +64,28 @@ public:
};
private:
struct Layout
{
float scale{ 1.0f };
float icons_size{ Default_Icons_Size };
float border{ 5.0f };
float gap_y{ 5.0f };
float stride_y() const { return icons_size + gap_y;}
float scaled_icons_size() const { return scale * icons_size; }
float scaled_border() const { return scale * border; }
float scaled_gap_y() const { return scale * gap_y; }
float scaled_stride_y() const { return scale * stride_y(); }
};
GLCanvas3D& m_parent;
bool m_enabled;
std::vector<std::unique_ptr<GLGizmoBase>> m_gizmos;
mutable GLTexture m_icons_texture;
mutable bool m_icons_texture_dirty;
BackgroundTexture m_background_texture;
Layout m_layout;
EType m_current;
EType m_hover;
@ -79,11 +95,6 @@ private:
void activate_gizmo(EType type);
float m_overlay_icons_size;
float m_overlay_scale;
float m_overlay_border;
float m_overlay_gap_y;
struct MouseCapture
{
bool left;
@ -202,8 +213,8 @@ private:
void render_background(float left, float top, float right, float bottom, float border) const;
void do_render_overlay() const;
float get_total_overlay_height() const;
float get_total_overlay_width() const;
float get_scaled_total_height() const;
float get_scaled_total_width() const;
GLGizmoBase* get_current() const;

View file

@ -96,7 +96,7 @@ void ImGuiWrapper::set_language(const std::string &language)
ranges = ranges_turkish;
} else if (lang == "vi") {
ranges = ranges_vietnamese;
} else if (lang == "jp") {
} else if (lang == "ja") {
ranges = ImGui::GetIO().Fonts->GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs
m_font_cjk = true;
} else if (lang == "ko") {
@ -254,6 +254,16 @@ bool ImGuiWrapper::begin(const wxString &name, int flags)
return begin(into_u8(name), flags);
}
bool ImGuiWrapper::begin(const std::string& name, bool* close, int flags)
{
return ImGui::Begin(name.c_str(), close, (ImGuiWindowFlags)flags);
}
bool ImGuiWrapper::begin(const wxString& name, bool* close, int flags)
{
return begin(into_u8(name), close, flags);
}
void ImGuiWrapper::end()
{
ImGui::End();
@ -514,13 +524,18 @@ void ImGuiWrapper::init_style()
(hex_color & 0xff) / 255.0f);
};
static const unsigned COL_GREY_DARK = 0x444444ff;
static const unsigned COL_WINDOW_BACKGROND = 0x222222cc;
static const unsigned COL_GREY_DARK = 0x555555ff;
static const unsigned COL_GREY_LIGHT = 0x666666ff;
static const unsigned COL_ORANGE_DARK = 0xc16737ff;
static const unsigned COL_ORANGE_LIGHT = 0xff7d38ff;
// Generics
// Window
style.WindowRounding = 4.0f;
set_color(ImGuiCol_WindowBg, COL_WINDOW_BACKGROND);
set_color(ImGuiCol_TitleBgActive, COL_ORANGE_DARK);
// Generics
set_color(ImGuiCol_FrameBg, COL_GREY_DARK);
set_color(ImGuiCol_FrameBgHovered, COL_GREY_LIGHT);
set_color(ImGuiCol_FrameBgActive, COL_GREY_LIGHT);

View file

@ -56,6 +56,8 @@ public:
bool begin(const std::string &name, int flags = 0);
bool begin(const wxString &name, int flags = 0);
bool begin(const std::string& name, bool* close, int flags = 0);
bool begin(const wxString& name, bool* close, int flags = 0);
void end();
bool button(const wxString &label);

View file

@ -124,7 +124,7 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_S
_3DScene::remove_all_canvases();
// Slic3r::GUI::deregister_on_request_update_callback();
// set to null tabs and a platter
// set to null tabs and a plater
// to avoid any manipulations with them from App->wxEVT_IDLE after of the mainframe closing
wxGetApp().tabs_list.clear();
wxGetApp().plater_ = nullptr;
@ -379,16 +379,6 @@ void MainFrame::on_dpi_changed(const wxRect &suggested_rect)
this->Maximize(is_maximized);
}
static std::string menu_icon(const std::string& icon_name)
{
#ifdef __WXMSW__
const std::string folder = "white\\";
#else
const std::string folder = "white/";
#endif
return wxGetApp().dark_mode_menus() ? folder+icon_name : icon_name;
}
void MainFrame::init_menubar()
{
#ifdef __APPLE__
@ -402,7 +392,7 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { if (m_plater) m_plater->new_project(); }, "", nullptr,
[this](){return m_plater != nullptr && can_start_new_project(); }, this);
append_menu_item(fileMenu, wxID_ANY, _(L("&Open Project")) + dots + "\tCtrl+O", _(L("Open a project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, menu_icon("open"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->load_project(); }, "open", nullptr,
[this](){return m_plater != nullptr; }, this);
wxMenu* recent_projects_menu = new wxMenu();
@ -440,60 +430,65 @@ void MainFrame::init_menubar()
Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { evt.Enable(m_recent_projects.GetCount() > 0); }, recent_projects_submenu->GetId());
append_menu_item(fileMenu, wxID_ANY, _(L("&Save Project")) + "\tCtrl+S", _(L("Save current project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, menu_icon("save"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(into_path(m_plater->get_project_filename(".3mf"))); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
#ifdef __APPLE__
append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Shift+S", _(L("Save current project file as")),
#else
append_menu_item(fileMenu, wxID_ANY, _(L("Save Project &as")) + dots + "\tCtrl+Alt+S", _(L("Save current project file as")),
#endif // __APPLE__
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, menu_icon("save"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->export_3mf(); }, "save", nullptr,
[this](){return m_plater != nullptr && can_save(); }, this);
fileMenu->AppendSeparator();
wxMenu* import_menu = new wxMenu();
append_menu_item(import_menu, wxID_ANY, _(L("Import STL/OBJ/AM&F/3MF")) + dots + "\tCtrl+I", _(L("Load a model")),
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, menu_icon("import_plater"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater", nullptr,
[this](){return m_plater != nullptr; }, this);
import_menu->AppendSeparator();
append_menu_item(import_menu, wxID_ANY, _(L("Import &Config")) + dots + "\tCtrl+L", _(L("Load exported configuration file")),
[this](wxCommandEvent&) { load_config_file(); }, menu_icon("import_config"));
[this](wxCommandEvent&) { load_config_file(); }, "import_config", nullptr,
[this]() {return true; }, this);
append_menu_item(import_menu, wxID_ANY, _(L("Import Config from &project")) + dots +"\tCtrl+Alt+L", _(L("Load configuration from project file")),
[this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, menu_icon("import_config"));
[this](wxCommandEvent&) { if (m_plater) m_plater->extract_config_from_project(); }, "import_config", nullptr,
[this]() {return true; }, this);
import_menu->AppendSeparator();
append_menu_item(import_menu, wxID_ANY, _(L("Import Config &Bundle")) + dots, _(L("Load presets from a bundle")),
[this](wxCommandEvent&) { load_configbundle(); }, menu_icon("import_config_bundle"));
[this](wxCommandEvent&) { load_configbundle(); }, "import_config_bundle", nullptr,
[this]() {return true; }, this);
append_submenu(fileMenu, import_menu, wxID_ANY, _(L("&Import")), "");
wxMenu* export_menu = new wxMenu();
wxMenuItem* item_export_gcode = append_menu_item(export_menu, wxID_ANY, _(L("Export &G-code")) + dots +"\tCtrl+G", _(L("Export current plate as G-code")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, menu_icon("export_gcode"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(); }, "export_gcode", nullptr,
[this](){return can_export_gcode(); }, this);
m_changeable_menu_items.push_back(item_export_gcode);
wxMenuItem* item_send_gcode = append_menu_item(export_menu, wxID_ANY, _(L("S&end G-code")) + dots +"\tCtrl+Shift+G", _(L("Send to print current plate as G-code")),
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, menu_icon("export_gcode"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->send_gcode(); }, "export_gcode", nullptr,
[this](){return can_send_gcode(); }, this);
m_changeable_menu_items.push_back(item_send_gcode);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, menu_icon("export_plater"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, "export_plater", nullptr,
[this](){return can_export_model(); }, this);
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL &including supports")) + dots, _(L("Export current plate as STL including supports")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, menu_icon("export_plater"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, "export_plater", nullptr,
[this](){return can_export_supports(); }, this);
append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, menu_icon("export_plater"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, "export_plater", nullptr,
[this](){return can_export_model(); }, this);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export &toolpaths as OBJ")) + dots, _(L("Export toolpaths as OBJ")),
[this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, menu_icon("export_plater"), nullptr,
[this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, "export_plater", nullptr,
[this]() {return can_export_toolpaths(); }, this);
export_menu->AppendSeparator();
append_menu_item(export_menu, wxID_ANY, _(L("Export &Config")) +dots +"\tCtrl+E", _(L("Export current configuration to file")),
[this](wxCommandEvent&) { export_config(); }, menu_icon("export_config"));
[this](wxCommandEvent&) { export_config(); }, "export_config", nullptr,
[this]() {return true; }, this);
append_menu_item(export_menu, wxID_ANY, _(L("Export Config &Bundle")) + dots, _(L("Export all presets to file")),
[this](wxCommandEvent&) { export_configbundle(); }, menu_icon("export_config_bundle"));
[this](wxCommandEvent&) { export_configbundle(); }, "export_config_bundle", nullptr,
[this]() {return true; }, this);
append_submenu(fileMenu, export_menu, wxID_ANY, _(L("&Export")), "");
fileMenu->AppendSeparator();
@ -521,11 +516,12 @@ void MainFrame::init_menubar()
fileMenu->AppendSeparator();
#endif
m_menu_item_reslice_now = append_menu_item(fileMenu, wxID_ANY, _(L("(Re)Slice No&w")) + "\tCtrl+R", _(L("Start new slicing process")),
[this](wxCommandEvent&) { reslice_now(); }, menu_icon("re_slice"), nullptr,
[this](wxCommandEvent&) { reslice_now(); }, "re_slice", nullptr,
[this](){return m_plater != nullptr && can_reslice(); }, this);
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_ANY, _(L("&Repair STL file")) + dots, _(L("Automatically repair an STL file")),
[this](wxCommandEvent&) { repair_stl(); }, menu_icon("wrench"));
[this](wxCommandEvent&) { repair_stl(); }, "wrench", nullptr,
[this]() {return true; }, this);
fileMenu->AppendSeparator();
append_menu_item(fileMenu, wxID_EXIT, _(L("&Quit")), wxString::Format(_(L("Quit %s")), SLIC3R_APP_NAME),
[this](wxCommandEvent&) { Close(false); });
@ -561,10 +557,10 @@ void MainFrame::init_menubar()
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Delete selected")) + sep + hotkey_delete,
_(L("Deletes the current selection")),[this](wxCommandEvent&) { m_plater->remove_selected(); },
menu_icon("remove_menu"), nullptr, [this](){return can_delete(); }, this);
"remove_menu", nullptr, [this](){return can_delete(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("Delete &all")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + hotkey_delete,
_(L("Deletes all objects")), [this](wxCommandEvent&) { m_plater->reset_with_confirm(); },
menu_icon("delete_all_menu"), nullptr, [this](){return can_delete_all(); }, this);
"delete_all_menu", nullptr, [this](){return can_delete_all(); }, this);
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Undo")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "Z",
@ -577,10 +573,10 @@ void MainFrame::init_menubar()
editMenu->AppendSeparator();
append_menu_item(editMenu, wxID_ANY, _(L("&Copy")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "C",
_(L("Copy selection to clipboard")), [this](wxCommandEvent&) { m_plater->copy_selection_to_clipboard(); },
menu_icon("copy_menu"), nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
"copy_menu", nullptr, [this](){return m_plater->can_copy_to_clipboard(); }, this);
append_menu_item(editMenu, wxID_ANY, _(L("&Paste")) + sep + GUI::shortkey_ctrl_prefix() + sep_space + "V",
_(L("Paste clipboard")), [this](wxCommandEvent&) { m_plater->paste_from_clipboard(); },
menu_icon("paste_menu"), nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
"paste_menu", nullptr, [this](){return m_plater->can_paste_from_clipboard(); }, this);
}
// Window menu
@ -589,26 +585,30 @@ void MainFrame::init_menubar()
size_t tab_offset = 0;
if (m_plater) {
append_menu_item(windowMenu, wxID_HIGHEST + 1, _(L("&Plater Tab")) + "\tCtrl+1", _(L("Show the plater")),
[this](wxCommandEvent&) { select_tab(0); }, menu_icon("plater"));
[this](wxCommandEvent&) { select_tab(0); }, "plater", nullptr,
[this]() {return true; }, this);
tab_offset += 1;
}
if (tab_offset > 0) {
windowMenu->AppendSeparator();
}
append_menu_item(windowMenu, wxID_HIGHEST + 2, _(L("P&rint Settings Tab")) + "\tCtrl+2", _(L("Show the print settings")),
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, menu_icon("cog"));
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 0); }, "cog", nullptr,
[this]() {return true; }, this);
wxMenuItem* item_material_tab = append_menu_item(windowMenu, wxID_HIGHEST + 3, _(L("&Filament Settings Tab")) + "\tCtrl+3", _(L("Show the filament settings")),
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, menu_icon("spool"));
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 1); }, "spool", nullptr,
[this]() {return true; }, this);
m_changeable_menu_items.push_back(item_material_tab);
append_menu_item(windowMenu, wxID_HIGHEST + 4, _(L("Print&er Settings Tab")) + "\tCtrl+4", _(L("Show the printer settings")),
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, menu_icon("printer"));
[this, tab_offset](wxCommandEvent&) { select_tab(tab_offset + 2); }, "printer", nullptr,
[this]() {return true; }, this);
if (m_plater) {
windowMenu->AppendSeparator();
append_menu_item(windowMenu, wxID_HIGHEST + 5, _(L("3&D")) + "\tCtrl+5", _(L("Show the 3D editing view")),
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, menu_icon("editor_menu"), nullptr,
[this](wxCommandEvent&) { m_plater->select_view_3D("3D"); }, "editor_menu", nullptr,
[this](){return can_change_view(); }, this);
append_menu_item(windowMenu, wxID_HIGHEST + 6, _(L("Pre&view")) + "\tCtrl+6", _(L("Show the 3D slices preview")),
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, menu_icon("preview_menu"), nullptr,
[this](wxCommandEvent&) { m_plater->select_view_3D("Preview"); }, "preview_menu", nullptr,
[this](){return can_change_view(); }, this);
}
@ -627,7 +627,8 @@ void MainFrame::init_menubar()
windowMenu->AppendSeparator();
append_menu_item(windowMenu, wxID_ANY, _(L("Print &Host Upload Queue")) + "\tCtrl+J", _(L("Display the Print Host Upload Queue window")),
[this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, menu_icon("upload_queue"));
[this](wxCommandEvent&) { m_printhost_queue_dlg->Show(); }, "upload_queue", nullptr,
[this]() {return true; }, this);
}
// View menu
@ -685,7 +686,7 @@ void MainFrame::init_menubar()
[this](wxCommandEvent&) { wxGetApp().keyboard_shortcuts(); });
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG
helpMenu->AppendSeparator();
append_menu_item(helpMenu, wxID_ANY, _(L("DEBUG gcode thumbnails")), _(L("DEBUG ONLY - read the selected gcode file and generates png for the contained thumbnails")),
append_menu_item(helpMenu, wxID_ANY, "DEBUG gcode thumbnails", "DEBUG ONLY - read the selected gcode file and generates png for the contained thumbnails",
[this](wxCommandEvent&) { wxGetApp().gcode_thumbnails_debug(); });
#endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG
}
@ -726,7 +727,7 @@ void MainFrame::update_menubar()
m_changeable_menu_items[miSend] ->SetItemLabel((is_fff ? _(L("S&end G-code")) : _(L("S&end to print"))) + dots + "\tCtrl+Shift+G");
m_changeable_menu_items[miMaterialTab] ->SetItemLabel((is_fff ? _(L("&Filament Settings Tab")) : _(L("Mate&rial Settings Tab"))) + "\tCtrl+3");
m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, menu_icon(is_fff ? "spool": "resin")));
m_changeable_menu_items[miMaterialTab] ->SetBitmap(create_scaled_bitmap(this, is_fff ? "spool": "resin"));
}
// To perform the "Quck Slice", "Quick Slice and Save As", "Repeat last Quick Slice" and "Slice to SVG".
@ -1005,7 +1006,7 @@ void MainFrame::load_configbundle(wxString file/* = wxEmptyString, const bool re
}
// Load a provied DynamicConfig into the Print / Filament / Printer tabs, thus modifying the active preset.
// Also update the platter with the new presets.
// Also update the plater with the new presets.
void MainFrame::load_config(const DynamicPrintConfig& config)
{
PrinterTechnology printer_technology = wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology();

View file

@ -125,7 +125,7 @@ public:
void load_config(const DynamicPrintConfig& config);
void select_tab(size_t tab) const;
void select_view(const std::string& direction);
// Propagate changed configuration from the Tab to the Platter and save changes to the AppConfig
// Propagate changed configuration from the Tab to the Plater and save changes to the AppConfig
void on_config_changed(DynamicPrintConfig* cfg) const ;
void add_to_recent_projects(const wxString& filename);

View file

@ -129,10 +129,9 @@ MeshRaycaster::MeshRaycaster(const TriangleMesh& mesh)
{
}
MeshRaycaster::~MeshRaycaster()
{
delete m_AABB_wrapper;
}
// Define the default destructor here. This is needed for the PIMPL with
// unique_ptr to work, the AABBWrapper is complete here.
MeshRaycaster::~MeshRaycaster() = default;
Vec3f MeshRaycaster::AABBWrapper::get_hit_pos(const igl::Hit& hit) const
{

View file

@ -17,7 +17,7 @@ namespace GUI {
struct Camera;
// lm_FIXME: Following class might possibly be replaced by Eigen::Hyperplane
class ClippingPlane
{
double m_data[4];
@ -67,13 +67,23 @@ public:
};
// MeshClipper class cuts a mesh and is able to return a triangulated cut.
class MeshClipper {
public:
// Inform MeshClipper about which plane we want to use to cut the mesh
// This is supposed to be in world coordinates.
void set_plane(const ClippingPlane& plane);
// Which mesh to cut. MeshClipper remembers const * to it, caller
// must make sure that it stays valid.
void set_mesh(const TriangleMesh& mesh);
// Inform the MeshClipper about the transformation that transforms the mesh
// into world coordinates.
void set_transformation(const Geometry::Transformation& trafo);
// Return the triangulated cut. The points are returned directly
// in world coordinates.
const std::vector<Vec3f>& get_triangles();
private:
@ -90,26 +100,45 @@ private:
// MeshRaycaster class answers queries such as where on the mesh someone clicked,
// whether certain points are visible or obscured by the mesh etc.
class MeshRaycaster {
public:
// The class saves a const* to the mesh, called is responsible
// for making sure it does not get invalid.
MeshRaycaster(const TriangleMesh& mesh);
~MeshRaycaster();
void set_transformation(const Geometry::Transformation& trafo);
void set_camera(const Camera& camera);
bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane = nullptr) const;
// Given a mouse position, this returns true in case it is on the mesh.
bool unproject_on_mesh(
const Vec2d& mouse_pos,
const Transform3d& trafo, // how to get the mesh into world coords
const Camera& camera, // current camera position
Vec3f& position, // where to save the positibon of the hit (mesh coords)
Vec3f& normal, // normal of the triangle that was hit
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active)
) const;
std::vector<unsigned> get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera,
const std::vector<Vec3f>& points, const ClippingPlane* clipping_plane = nullptr) const;
// Given a vector of points in woorld coordinates, this returns vector
// of indices of points that are visible (i.e. not cut by clipping plane
// or obscured by part of the mesh.
std::vector<unsigned> get_unobscured_idxs(
const Geometry::Transformation& trafo, // how to get the mesh into world coords
const Camera& camera, // current camera position
const std::vector<Vec3f>& points, // points in world coords
const ClippingPlane* clipping_plane = nullptr // clipping plane (if active)
) const;
// Given a point in world coords, the method returns closest point on the mesh.
// The output is in mesh coords.
// normal* can be used to also get normal of the respective triangle.
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
private:
// PIMPL wrapper around igl::AABB so I don't have to include the header-only IGL here
class AABBWrapper;
AABBWrapper* m_AABB_wrapper;
std::unique_ptr<AABBWrapper> m_AABB_wrapper;
const TriangleMesh* m_mesh = nullptr;
};

View file

@ -5,6 +5,7 @@
#include "GUI_App.hpp"
#include "PresetBundle.hpp"
#include "AppConfig.hpp"
#include "GLCanvas3D.hpp"
#include <wx/glcanvas.h>
@ -14,6 +15,9 @@
#include <bitset>
//unofficial linux lib
//#include <spnav.h>
// WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules
static const std::vector<int> _3DCONNEXION_VENDORS =
@ -54,13 +58,19 @@ const double Mouse3DController::State::DefaultTranslationScale = 2.5;
const double Mouse3DController::State::MaxTranslationDeadzone = 0.2;
const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone;
const float Mouse3DController::State::DefaultRotationScale = 1.0f;
const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DController::State::MaxTranslationDeadzone;
const float Mouse3DController::State::MaxRotationDeadzone = 0.2f;
const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone;
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
const double Mouse3DController::State::DefaultZoomScale = 0.1;
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
Mouse3DController::State::State()
: m_buttons_enabled(false)
, m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone)
, m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone)
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
, m_zoom_params(DefaultZoomScale, 0.0)
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
, m_mouse_wheel_counter(0)
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
, m_translation_queue_max_size(0)
@ -109,7 +119,7 @@ void Mouse3DController::State::append_button(unsigned int id)
bool Mouse3DController::State::process_mouse_wheel()
{
if (m_mouse_wheel_counter == 0)
if (m_mouse_wheel_counter.load() == 0)
return false;
else if (!m_rotation.queue.empty())
{
@ -117,7 +127,7 @@ bool Mouse3DController::State::process_mouse_wheel()
return true;
}
m_mouse_wheel_counter = 0;
m_mouse_wheel_counter.store(0);
return true;
}
@ -146,19 +156,31 @@ bool Mouse3DController::State::apply(Camera& camera)
if (has_translation())
{
const Vec3d& translation = m_translation.queue.front();
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
double zoom_factor = camera.min_zoom() / camera.get_zoom();
camera.set_target(camera.get_target() + zoom_factor * m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(2) * camera.get_dir_up()));
if (translation(1) != 0.0)
camera.update_zoom(m_zoom_params.scale * translation(1) / std::abs(translation(1)));
#else
camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up()));
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
m_translation.queue.pop();
ret = true;
}
if (has_rotation())
{
#if ENABLE_6DOF_CAMERA
Vec3d rotation = (m_rotation_params.scale * m_rotation.queue.front()).cast<double>();
camera.rotate_local_around_target(Vec3d(Geometry::deg2rad(rotation(0)), Geometry::deg2rad(-rotation(2)), Geometry::deg2rad(-rotation(1))));
#else
const Vec3f& rotation = m_rotation.queue.front();
float theta = m_rotation_params.scale * rotation(0);
float phi = m_rotation_params.scale * rotation(2);
float sign = camera.inverted_phi ? -1.0f : 1.0f;
camera.phi += sign * phi;
camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA);
#endif // ENABLE_6DOF_CAMERA
m_rotation.queue.pop();
ret = true;
}
@ -184,7 +206,12 @@ Mouse3DController::Mouse3DController()
, m_device(nullptr)
, m_device_str("")
, m_running(false)
, m_settings_dialog(false)
, m_show_settings_dialog(false)
, m_mac_mouse_connected(false)
, m_settings_dialog_closed_by_user(false)
#if __APPLE__
,m_handler_mac(new Mouse3DHandlerMac(this))
#endif //__APPLE__
{
m_last_time = std::chrono::high_resolution_clock::now();
}
@ -223,14 +250,13 @@ bool Mouse3DController::apply(Camera& camera)
if (!m_initialized)
return false;
std::lock_guard<std::mutex> lock(m_mutex);
// check if the user unplugged the device
if (!m_running && is_device_connected())
if (!is_running() && is_device_connected())
{
disconnect_device();
// hides the settings dialog if the user re-plug the device
m_settings_dialog = false;
// hides the settings dialog if the user un-plug the device
m_show_settings_dialog = false;
m_settings_dialog_closed_by_user = false;
}
// check if the user plugged the device
@ -240,100 +266,148 @@ bool Mouse3DController::apply(Camera& camera)
return is_device_connected() ? m_state.apply(camera) : false;
}
void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const
void Mouse3DController::render_settings_dialog(GLCanvas3D& canvas) const
{
if (!m_running || !m_settings_dialog)
if (!is_running() || !m_show_settings_dialog)
return;
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f);
imgui.set_next_window_bg_alpha(0.5f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator);
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Device:")));
ImGui::PopStyleColor();
ImGui::SameLine();
imgui.text(m_device_str);
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Speed:")));
ImGui::PopStyleColor();
float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale;
if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.5f, 2.0f, "%.1f"))
m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale);
float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale;
if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.5f, 2.0f, "%.1f"))
m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale);
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Deadzone:")));
ImGui::PopStyleColor();
float translation_deadzone = (float)m_state.get_translation_deadzone();
if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
m_state.set_translation_deadzone((double)translation_deadzone);
float rotation_deadzone = m_state.get_rotation_deadzone();
if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f"))
m_state.set_rotation_deadzone(rotation_deadzone);
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
ImGui::Separator();
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text("DEBUG:");
imgui.text("Vectors:");
ImGui::PopStyleColor();
Vec3f translation = m_state.get_translation().cast<float>();
Vec3f rotation = m_state.get_rotation();
ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text("Queue size:");
ImGui::PopStyleColor();
int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() };
int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() };
int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() };
ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly);
ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly);
ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly);
int queue_size = (int)m_state.get_queues_max_size();
if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly))
// when the user clicks on [X] or [Close] button we need to trigger
// an extra frame to let the dialog disappear
if (m_settings_dialog_closed_by_user)
{
if (queue_size > 0)
m_state.set_queues_max_size(queue_size);
m_show_settings_dialog = false;
m_settings_dialog_closed_by_user = false;
canvas.request_extra_frame();
return;
}
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text("Camera:");
ImGui::PopStyleColor();
Vec3f target = wxGetApp().plater()->get_camera().get_target().cast<float>();
ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
Size cnv_size = canvas.get_canvas_size();
ImGuiWrapper& imgui = *wxGetApp().imgui();
imgui.set_next_window_pos(0.5f * (float)cnv_size.get_width(), 0.5f * (float)cnv_size.get_height(), ImGuiCond_Always, 0.5f, 0.5f);
static ImVec2 last_win_size(0.0f, 0.0f);
bool shown = true;
if (imgui.begin(_(L("3Dconnexion settings")), &shown, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse))
{
if (shown)
{
ImVec2 win_size = ImGui::GetWindowSize();
if ((last_win_size.x != win_size.x) || (last_win_size.y != win_size.y))
{
// when the user clicks on [X] button, the next time the dialog is shown
// has a dummy size, so we trigger an extra frame to let it have the correct size
last_win_size = win_size;
canvas.request_extra_frame();
}
const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator);
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Device:")));
ImGui::PopStyleColor();
ImGui::SameLine();
imgui.text(m_device_str);
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Speed:")));
ImGui::PopStyleColor();
float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale;
if (imgui.slider_float(_(L("Translation")) + "##1", &translation_scale, 0.1f, 10.0f, "%.1f"))
m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale);
float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale;
if (imgui.slider_float(_(L("Rotation")) + "##1", &rotation_scale, 0.1f, 10.0f, "%.1f"))
m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale);
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
float zoom_scale = m_state.get_zoom_scale() / State::DefaultZoomScale;
if (imgui.slider_float(_(L("Zoom")), &zoom_scale, 0.1f, 10.0f, "%.1f"))
m_state.set_zoom_scale(State::DefaultZoomScale * zoom_scale);
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text(_(L("Deadzone:")));
ImGui::PopStyleColor();
float translation_deadzone = (float)m_state.get_translation_deadzone();
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
if (imgui.slider_float(_(L("Translation")) + "/" + _(L("Zoom")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
#else
if (imgui.slider_float(_(L("Translation")) + "##2", &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
m_state.set_translation_deadzone((double)translation_deadzone);
float rotation_deadzone = m_state.get_rotation_deadzone();
if (imgui.slider_float(_(L("Rotation")) + "##2", &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f"))
m_state.set_rotation_deadzone(rotation_deadzone);
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
ImGui::Separator();
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text("DEBUG:");
imgui.text("Vectors:");
ImGui::PopStyleColor();
Vec3f translation = m_state.get_translation().cast<float>();
Vec3f rotation = m_state.get_rotation();
ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text("Queue size:");
ImGui::PopStyleColor();
int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() };
int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() };
int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() };
ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly);
ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly);
ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly);
int queue_size = (int)m_state.get_queues_max_size();
if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly))
{
if (queue_size > 0)
m_state.set_queues_max_size(queue_size);
}
ImGui::Separator();
ImGui::PushStyleColor(ImGuiCol_Text, color);
imgui.text("Camera:");
ImGui::PopStyleColor();
Vec3f target = wxGetApp().plater()->get_camera().get_target().cast<float>();
ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
imgui.end();
ImGui::Separator();
if (imgui.button(_(L("Close"))))
{
// the user clicked on the [Close] button
m_settings_dialog_closed_by_user = true;
canvas.set_as_dirty();
}
}
else
{
// the user clicked on the [X] button
m_settings_dialog_closed_by_user = true;
canvas.set_as_dirty();
}
}
ImGui::PopStyleVar();
imgui.end();
}
bool Mouse3DController::connect_device()
{
static const long long DETECTION_TIME_MS = 2000; // seconds
#ifdef __APPLE__
return false;
#endif//__APPLE__
static const long long DETECTION_TIME_MS = 2000; // two seconds
if (is_device_connected())
return false;
@ -341,7 +415,7 @@ bool Mouse3DController::connect_device()
// check time since last detection took place
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_last_time).count() < DETECTION_TIME_MS)
return false;
m_last_time = std::chrono::high_resolution_clock::now();
// Enumerates devices
@ -464,7 +538,7 @@ bool Mouse3DController::connect_device()
{
if (device.second.size() == 1)
{
#ifdef __linux__
#if defined(__linux__)
hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr);
if (test_device != nullptr)
{
@ -472,7 +546,7 @@ bool Mouse3DController::connect_device()
#else
if (device.second.front().has_valid_usage())
{
#endif // __linux__
#endif // __linux__
vendor_id = device.first.first;
product_id = device.first.second;
break;
@ -489,6 +563,7 @@ bool Mouse3DController::connect_device()
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\"";
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
#ifdef __linux__
hid_device* test_device = hid_open_path(data.path.c_str());
if (test_device != nullptr)
@ -503,7 +578,7 @@ bool Mouse3DController::connect_device()
hid_close(test_device);
break;
}
#else
#else // !__linux__
if (data.has_valid_usage())
{
path = data.path;
@ -551,38 +626,49 @@ bool Mouse3DController::connect_device()
if (m_device != nullptr)
{
std::vector<wchar_t> manufacturer(1024, 0);
hid_get_manufacturer_string(m_device, manufacturer.data(), 1024);
m_device_str = boost::nowide::narrow(manufacturer.data());
wchar_t buffer[1024];
hid_get_manufacturer_string(m_device, buffer, 1024);
m_device_str = boost::nowide::narrow(buffer);
// #3479 seems to show that sometimes an extra whitespace is added, so we remove it
boost::algorithm::trim(m_device_str);
std::vector<wchar_t> product(1024, 0);
hid_get_product_string(m_device, product.data(), 1024);
m_device_str += "/" + boost::nowide::narrow(product.data());
BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str;
hid_get_product_string(m_device, buffer, 1024);
m_device_str += "/" + boost::nowide::narrow(buffer);
// #3479 seems to show that sometimes an extra whitespace is added, so we remove it
boost::algorithm::trim(m_device_str);
BOOST_LOG_TRIVIAL(info) << "Connected 3DConnexion device:";
BOOST_LOG_TRIVIAL(info) << "Manufacturer/product: " << m_device_str;
BOOST_LOG_TRIVIAL(info) << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")";
BOOST_LOG_TRIVIAL(info) << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")";
if (!path.empty())
BOOST_LOG_TRIVIAL(info) << "Path................: '" << path << "'";
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout << std::endl << "Connected device:" << std::endl;
std::cout << "Manufacturer/product: " << m_device_str << std::endl;
std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl;
std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl;
std::cout << "Path................: '" << path << "'" << std::endl;
std::cout << "Opened device." << std::endl;
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
// get device parameters from the config, if present
double translation_speed = 1.0;
float rotation_speed = 1.0;
double translation_speed = 4.0;
float rotation_speed = 4.0;
double translation_deadzone = State::DefaultTranslationDeadzone;
float rotation_deadzone = State::DefaultRotationDeadzone;
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
double zoom_speed = 2.0;
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed);
wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone);
wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed);
wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone);
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
wxGetApp().app_config->get_mouse_device_zoom_speed(m_device_str, zoom_speed);
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
// clamp to valid values
m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation_speed)));
m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone)));
m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed)));
m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone)));
m_state.set_translation_scale(State::DefaultTranslationScale * std::clamp(translation_speed, 0.1, 10.0));
m_state.set_translation_deadzone(std::clamp(translation_deadzone, 0.0, State::MaxTranslationDeadzone));
m_state.set_rotation_scale(State::DefaultRotationScale * std::clamp(rotation_speed, 0.1f, 10.0f));
m_state.set_rotation_deadzone(std::clamp(rotation_deadzone, 0.0f, State::MaxRotationDeadzone));
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
m_state.set_zoom_scale(State::DefaultZoomScale * std::clamp(zoom_speed, 0.1, 10.0));
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
}
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
else
@ -608,8 +694,13 @@ void Mouse3DController::disconnect_device()
m_thread.join();
// Store current device parameters into the config
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(),
m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone(), m_state.get_zoom_scale() / State::DefaultZoomScale);
#else
wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(),
m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone());
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
wxGetApp().app_config->save();
// Close the 3Dconnexion device
@ -637,10 +728,9 @@ void Mouse3DController::run()
collect_input();
}
}
void Mouse3DController::collect_input()
{
DataPacket packet = { 0 };
DataPacketRaw packet = { 0 };
int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100);
if (res < 0)
{
@ -648,12 +738,47 @@ void Mouse3DController::collect_input()
stop();
return;
}
handle_input(packet, res);
}
void Mouse3DController::handle_input_axis(const DataPacketAxis& packet)
{
if (!wxGetApp().IsActive())
return;
bool appended = false;
//translation
double deadzone = m_state.get_translation_deadzone();
Vec3d translation(std::abs(packet[0]) > deadzone ? -packet[0] : 0.0,
std::abs(packet[1]) > deadzone ? packet[1] : 0.0,
std::abs(packet[2]) > deadzone ? packet[2] : 0.0);
if (!translation.isApprox(Vec3d::Zero()))
{
m_state.append_translation(translation);
appended = true;
}
//rotation
deadzone = m_state.get_rotation_deadzone();
Vec3f rotation(std::abs(packet[3]) > deadzone ? -(float)packet[3] : 0.0,
std::abs(packet[4]) > deadzone ? (float)packet[4] : 0.0,
std::abs(packet[5]) > deadzone ? -(float)packet[5] : 0.0);
if (!rotation.isApprox(Vec3f::Zero()))
{
m_state.append_rotation(rotation);
appended = true;
}
if (appended)
{
wxGetApp().plater()->set_current_canvas_as_dirty();
// ask for an idle event to update 3D scene
wxWakeUpIdle();
}
}
void Mouse3DController::handle_input(const DataPacketRaw& packet, const int packet_lenght)
{
if (!wxGetApp().IsActive())
return;
std::lock_guard<std::mutex> lock(m_mutex);
int res = packet_lenght;
bool updated = false;
if (res == 7)
@ -669,11 +794,14 @@ void Mouse3DController::collect_input()
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (updated)
{
wxGetApp().plater()->set_current_canvas_as_dirty();
// ask for an idle event to update 3D scene
wxWakeUpIdle();
}
}
bool Mouse3DController::handle_packet(const DataPacket& packet)
bool Mouse3DController::handle_packet(const DataPacketRaw& packet)
{
switch (packet[0])
{
@ -717,7 +845,7 @@ bool Mouse3DController::handle_packet(const DataPacket& packet)
return false;
}
bool Mouse3DController::handle_wireless_packet(const DataPacket& packet)
bool Mouse3DController::handle_wireless_packet(const DataPacketRaw& packet)
{
switch (packet[0])
{
@ -764,7 +892,7 @@ double convert_input(unsigned char first, unsigned char second, double deadzone)
return (std::abs(ret) > deadzone) ? ret : 0.0;
}
bool Mouse3DController::handle_packet_translation(const DataPacket& packet)
bool Mouse3DController::handle_packet_translation(const DataPacketRaw& packet)
{
double deadzone = m_state.get_translation_deadzone();
Vec3d translation(-convert_input(packet[1], packet[2], deadzone),
@ -780,12 +908,18 @@ bool Mouse3DController::handle_packet_translation(const DataPacket& packet)
return false;
}
bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte)
bool Mouse3DController::handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte)
{
double deadzone = (double)m_state.get_rotation_deadzone();
#if ENABLE_6DOF_CAMERA
Vec3f rotation((float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone),
(float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone),
(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone));
#else
Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone),
(float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone),
-(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone));
#endif // ENABLE_6DOF_CAMERA
if (!rotation.isApprox(Vec3f::Zero()))
{
@ -796,7 +930,7 @@ bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigne
return false;
}
bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size)
bool Mouse3DController::handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size)
{
unsigned int data = 0;
for (unsigned int i = 1; i < packet_size; ++i)

View file

@ -14,10 +14,17 @@
#include <vector>
#include <chrono>
namespace Slic3r {
namespace GUI {
#if __APPLE__
class Mouse3DHandlerMac;
#endif//__APPLE__
struct Camera;
class GLCanvas3D;
class Mouse3DController
{
@ -30,6 +37,9 @@ class Mouse3DController
static const float DefaultRotationScale;
static const float MaxRotationDeadzone;
static const float DefaultRotationDeadzone;
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
static const double DefaultZoomScale;
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
private:
template <typename Number>
@ -61,6 +71,9 @@ class Mouse3DController
CustomParameters<double> m_translation_params;
CustomParameters<float> m_rotation_params;
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
CustomParameters<double> m_zoom_params;
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
// When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected.
// We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere
@ -69,7 +82,7 @@ class Mouse3DController
// Mouse3DController::collect_input() through the call to the append_rotation() method
// GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method
// GLCanvas3D::on_idle() through the call to the apply() method
unsigned int m_mouse_wheel_counter;
std::atomic<unsigned int> m_mouse_wheel_counter;
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
size_t m_translation_queue_max_size;
@ -96,6 +109,11 @@ class Mouse3DController
float get_rotation_scale() const { return m_rotation_params.scale; }
void set_rotation_scale(float scale) { m_rotation_params.scale = scale; }
#if ENABLE_3DCONNEXION_Y_AS_ZOOM
double get_zoom_scale() const { return m_zoom_params.scale; }
void set_zoom_scale(double scale) { m_zoom_params.scale = scale; }
#endif // ENABLE_3DCONNEXION_Y_AS_ZOOM
double get_translation_deadzone() const { return m_translation_params.deadzone; }
void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; }
@ -125,12 +143,14 @@ class Mouse3DController
bool m_initialized;
mutable State m_state;
std::mutex m_mutex;
std::thread m_thread;
hid_device* m_device;
std::string m_device_str;
bool m_running;
bool m_settings_dialog;
bool m_mac_mouse_connected;
mutable bool m_show_settings_dialog;
// set to true when ther user closes the dialog by clicking on [X] or [Close] buttons
mutable bool m_settings_dialog_closed_by_user;
std::chrono::time_point<std::chrono::high_resolution_clock> m_last_time;
public:
@ -139,37 +159,56 @@ public:
void init();
void shutdown();
bool is_device_connected() const { return m_device != nullptr; }
bool is_running() const { return m_running; }
bool is_device_connected() const { return m_device != nullptr || m_mac_mouse_connected; }
bool is_running() const { return m_running || m_mac_mouse_connected; }
bool process_mouse_wheel() { std::lock_guard<std::mutex> lock(m_mutex); return m_state.process_mouse_wheel(); }
void set_mac_mouse_connected(bool b){m_mac_mouse_connected = b;};
bool process_mouse_wheel() { return m_state.process_mouse_wheel(); }
bool apply(Camera& camera);
bool is_settings_dialog_shown() const { return m_settings_dialog; }
void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); }
void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const;
bool is_settings_dialog_shown() const { return m_show_settings_dialog; }
void show_settings_dialog(bool show) { m_show_settings_dialog = show && is_running(); }
void render_settings_dialog(GLCanvas3D& canvas) const;
typedef std::array<double, 6> DataPacketAxis;
void handle_input_axis(const DataPacketAxis& packet);
private:
bool connect_device();
void disconnect_device();
void start();
void stop() { m_running = false; }
typedef std::array<unsigned char, 13> DataPacketRaw;
// secondary thread methods
void run();
void collect_input();
void handle_input(const DataPacketRaw& packet, const int packet_lenght);
bool handle_packet(const DataPacketRaw& packet);
bool handle_wireless_packet(const DataPacketRaw& packet);
bool handle_packet_translation(const DataPacketRaw& packet);
bool handle_packet_rotation(const DataPacketRaw& packet, unsigned int first_byte);
bool handle_packet_button(const DataPacketRaw& packet, unsigned int packet_size);
typedef std::array<unsigned char, 13> DataPacket;
bool handle_packet(const DataPacket& packet);
bool handle_wireless_packet(const DataPacket& packet);
bool handle_packet_translation(const DataPacket& packet);
bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte);
bool handle_packet_button(const DataPacket& packet, unsigned int packet_size);
#if __APPLE__
Mouse3DHandlerMac* m_handler_mac;
#endif//__APPLE__
};
#if __APPLE__
class Mouse3DHandlerMac{
public:
Mouse3DHandlerMac(Mouse3DController* controller);
~Mouse3DHandlerMac();
bool available();
};
#endif//__APPLE__
} // namespace GUI
} // namespace Slic3r
#endif // slic3r_Mouse3DController_hpp_

View file

@ -0,0 +1,255 @@
#include "Mouse3DController.hpp"
#include <stdint.h>
#include <dlfcn.h>
#include <array>
#include <boost/log/trivial.hpp>
#include <cstdio>
static Slic3r::GUI::Mouse3DController* mouse_3d_controller = NULL;
static uint16_t clientID = 0;
static bool driver_loaded = false;
static bool has_new_driver = false; // drivers >= 10.2.2 are "new", and can process events on a separate thread
// replicate just enough of the 3Dx API for our uses, not everything the driver provides
#define kConnexionClientModeTakeOver 1
#define kConnexionMaskAxis 0x3f00
#define kConnexionMaskAll 0x3fff
#define kConnexionMaskAllButtons 0xffffffff
#define kConnexionCmdHandleButtons 2
#define kConnexionCmdHandleAxis 3
#define kConnexionCmdAppSpecific 10
#define kConnexionMsgDeviceState '3dSR'
#define kConnexionCtlGetDeviceID '3did'
#pragma pack(push, 2)
struct ConnexionDeviceState {
uint16_t version;
uint16_t client;
uint16_t command;
int16_t param;
int32_t value;
uint64_t time;
uint8_t report[8];
uint16_t buttons8; // obsolete! (pre-10.x drivers)
int16_t axis[6]; // tx, ty, tz, rx, ry, rz
uint16_t address;
uint32_t buttons;
};
#pragma pack(pop)
// callback functions:
typedef void (*AddedHandler)(uint32_t);
typedef void (*RemovedHandler)(uint32_t);
typedef void (*MessageHandler)(uint32_t, uint32_t msg_type, void *msg_arg);
// driver functions:
typedef int16_t (*SetConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler, bool);
typedef int16_t (*InstallConnexionHandlers_ptr)(MessageHandler, AddedHandler, RemovedHandler);
typedef void (*CleanupConnexionHandlers_ptr)();
typedef uint16_t (*RegisterConnexionClient_ptr)(uint32_t signature,
const char *name,
uint16_t mode,
uint32_t mask);
typedef void (*SetConnexionClientButtonMask_ptr)(uint16_t clientID, uint32_t buttonMask);
typedef void (*UnregisterConnexionClient_ptr)(uint16_t clientID);
typedef int16_t (*ConnexionClientControl_ptr)(uint16_t clientID,
uint32_t message,
int32_t param,
int32_t *result);
#define DECLARE_FUNC(name) name##_ptr name = NULL
DECLARE_FUNC(SetConnexionHandlers);
DECLARE_FUNC(InstallConnexionHandlers);
DECLARE_FUNC(CleanupConnexionHandlers);
DECLARE_FUNC(RegisterConnexionClient);
DECLARE_FUNC(SetConnexionClientButtonMask);
DECLARE_FUNC(UnregisterConnexionClient);
DECLARE_FUNC(ConnexionClientControl);
static void *load_func(void *module, const char *func_name)
{
void *func = dlsym(module, func_name);
//#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
if (func) {
BOOST_LOG_TRIVIAL(info) << func_name <<" loaded";
}
else {
//printf("<!> %s\n", dlerror());
BOOST_LOG_TRIVIAL(error) <<"loading 3dx drivers dlsym error: "<< dlerror();
}
//#endif
return func;
}
#define LOAD_FUNC(name) name = (name##_ptr)load_func(module, #name)
static void *module; // handle to the whole driver
static bool load_driver_functions()
{
if (driver_loaded) {
return true;
}
module = dlopen("/Library/Frameworks/3DconnexionClient.framework/3DconnexionClient",
RTLD_LAZY | RTLD_LOCAL);
if (module) {
BOOST_LOG_TRIVIAL(info) << "loading 3dx drivers";
LOAD_FUNC(SetConnexionHandlers);
if (SetConnexionHandlers != NULL) {
driver_loaded = true;
has_new_driver = true;
}
else {
BOOST_LOG_TRIVIAL(info) << "installing 3dx drivers";
LOAD_FUNC(InstallConnexionHandlers);
driver_loaded = (InstallConnexionHandlers != NULL);
}
if (driver_loaded) {
LOAD_FUNC(CleanupConnexionHandlers);
LOAD_FUNC(RegisterConnexionClient);
LOAD_FUNC(SetConnexionClientButtonMask);
LOAD_FUNC(UnregisterConnexionClient);
LOAD_FUNC(ConnexionClientControl);
}
}
else {
BOOST_LOG_TRIVIAL(error) << "3dx drivers module loading error: "<< dlerror() ;
#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("<!> %s\n", dlerror());
#endif
}
#if DENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("loaded: %s\n", driver_loaded ? "YES" : "NO");
printf("new: %s\n", has_new_driver ? "YES" : "NO");
#endif
BOOST_LOG_TRIVIAL(info) << "3dx drivers loaded: "<< driver_loaded ? "YES" : "NO" ;
return driver_loaded;
}
static void unload_driver()
{
dlclose(module);
}
static void DeviceAdded(uint32_t unused)
{
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
std::cout<<"3D device added"<<std::endl;
#endif
BOOST_LOG_TRIVIAL(info)<<"3dx device added";
// determine exactly which device is plugged in
int32_t result;
ConnexionClientControl(clientID, kConnexionCtlGetDeviceID, 0, &result);
int16_t vendorID = result >> 16;
int16_t productID = result & 0xffff;
//TODO: verify device
mouse_3d_controller->set_mac_mouse_connected(true);
}
static void DeviceRemoved(uint32_t unused)
{
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
printf("3d device removed\n");
#endif
BOOST_LOG_TRIVIAL(info) << "3dx device removed\n";
mouse_3d_controller->set_mac_mouse_connected(true);
}
static void DeviceEvent(uint32_t unused, uint32_t msg_type, void *msg_arg)
{
if (msg_type == kConnexionMsgDeviceState) {
ConnexionDeviceState *s = (ConnexionDeviceState *)msg_arg;
if (s->client == clientID) {
switch (s->command) {
case kConnexionCmdHandleAxis: {
/*
The axis field is an array of 6 signed 16-bit integers corresponding to the 6 device axes. Data is ordered as Tx, Tz, Ty, Rx, Rz, Ry. The values reported are scaled by the driver according to the speed slider settings on the 3Dconnexion preference panel. At maximum speed, the range is - 1024 to 1024. Typical range that you should optimize your application for should be -500 to 500.
*/
//Actually we are getting values way over 1024. Max is probably 2048 now.
std::array<double, 6> packet;
for (int i = 0; i < 6; i++) {
packet[i] = (double)s->axis[i]/350.0;//wanted to divide by 500 but 350 is used at raw input so i used same value.
}
mouse_3d_controller->handle_input_axis(packet);
break;
}
case kConnexionCmdHandleButtons:
break;
case kConnexionCmdAppSpecific:
break;
default:
break;
}
}
}
}
namespace Slic3r {
namespace GUI {
Mouse3DHandlerMac::Mouse3DHandlerMac(Mouse3DController* controller)
{
BOOST_LOG_TRIVIAL(info) << "3dx mac handler starts";
if (load_driver_functions()) {
mouse_3d_controller = controller;
uint16_t error;
if (has_new_driver) {
error = SetConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved, false);
}
else {
error = InstallConnexionHandlers(DeviceEvent, DeviceAdded, DeviceRemoved);
}
if (error) {
return;
}
// Registration is done either by 4letter constant (CFBundleSignature - obsolete
//and we dont have that) or Executable name in pascal string(first byte is string lenght).
//If no packets are recieved the name might be different - check cmake. If debugging try commenting
// set_target_properties(PrusaSlicer PROPERTIES OUTPUT_NAME "prusa-slicer")
clientID = RegisterConnexionClient(
0, "\013PrusaSlicer", kConnexionClientModeTakeOver, kConnexionMaskAxis);
BOOST_LOG_TRIVIAL(info) << "3dx mac handler registered";
}
}
Mouse3DHandlerMac::~Mouse3DHandlerMac()
{
if (driver_loaded) {
UnregisterConnexionClient(clientID);
CleanupConnexionHandlers();
unload_driver();
}
mouse_3d_controller = nullptr;
}
bool Mouse3DHandlerMac::available()
{
return driver_loaded;
}
}}//namespace Slic3r::GUI

View file

@ -26,6 +26,9 @@
#include <wx/colordlg.h>
#include <wx/numdlg.h>
#include <wx/debug.h>
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
#include <wx/busyinfo.h>
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
#include "libslic3r/libslic3r.h"
#include "libslic3r/Format/STL.hpp"
@ -77,6 +80,7 @@
#include "../Utils/FixModelByWin10.hpp"
#include "../Utils/UndoRedo.hpp"
#include "../Utils/Thread.hpp"
#include "RemovableDriveManager.hpp"
#include <wx/glcanvas.h> // Needs to be last because reasons :-/
#include "WipeTowerDialog.hpp"
@ -322,7 +326,7 @@ wxBitmapComboBox(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(15 *
cfg_new.set_key_value("extruder_colour", colors);
wxGetApp().get_tab(Preset::TYPE_PRINTER)->load_config(cfg_new);
wxGetApp().preset_bundle->update_platter_filament_ui(extruder_idx, this);
wxGetApp().preset_bundle->update_plater_filament_ui(extruder_idx, this);
wxGetApp().plater()->on_config_change(cfg_new);
}
});
@ -698,7 +702,9 @@ struct Sidebar::priv
wxButton *btn_export_gcode;
wxButton *btn_reslice;
wxButton *btn_send_gcode;
ScalableButton *btn_send_gcode;
ScalableButton *btn_remove_device;
ScalableButton* btn_export_gcode_removable; //exports to removable drives (appears only if removable drive is connected)
priv(Plater *plater) : plater(plater) {}
~priv();
@ -847,22 +853,50 @@ Sidebar::Sidebar(Plater *parent)
// Buttons underneath the scrolled area
auto init_btn = [this](wxButton **btn, wxString label) {
// rescalable bitmap buttons "Send to printer" and "Remove device"
auto init_scalable_btn = [this](ScalableButton** btn, const std::string& icon_name, wxString tooltip = wxEmptyString)
{
#ifdef __APPLE__
int bmp_px_cnt = 16;
#else
int bmp_px_cnt = 32;
#endif //__APPLE__
ScalableBitmap bmp = ScalableBitmap(this, icon_name, bmp_px_cnt);
*btn = new ScalableButton(this, wxID_ANY, bmp, "", wxBU_EXACTFIT);
(*btn)->SetToolTip(tooltip);
(*btn)->Hide();
};
init_scalable_btn(&p->btn_send_gcode , "export_gcode", _(L("Send to printer")));
init_scalable_btn(&p->btn_remove_device, "cross" , _(L("Remove device")));
init_scalable_btn(&p->btn_export_gcode_removable, "export_to_sd", _(L("Export to SD card/ USB thumb drive")));
// regular buttons "Slice now" and "Export G-code"
const int scaled_height = p->btn_remove_device->GetBitmapHeight() + 4;
auto init_btn = [this](wxButton **btn, wxString label, const int button_height) {
*btn = new wxButton(this, wxID_ANY, label, wxDefaultPosition,
wxDefaultSize, wxBU_EXACTFIT);
wxSize(-1, button_height), wxBU_EXACTFIT);
(*btn)->SetFont(wxGetApp().bold_font());
};
init_btn(&p->btn_send_gcode, _(L("Send to printer")));
p->btn_send_gcode->Hide();
init_btn(&p->btn_export_gcode, _(L("Export G-code")) + dots);
init_btn(&p->btn_reslice, _(L("Slice now")));
init_btn(&p->btn_export_gcode, _(L("Export G-code")) + dots , scaled_height);
init_btn(&p->btn_reslice , _(L("Slice now")) , scaled_height);
enable_buttons(false);
auto *btns_sizer = new wxBoxSizer(wxVERTICAL);
auto* complect_btns_sizer = new wxBoxSizer(wxHORIZONTAL);
complect_btns_sizer->Add(p->btn_export_gcode, 1, wxEXPAND);
complect_btns_sizer->Add(p->btn_send_gcode);
complect_btns_sizer->Add(p->btn_export_gcode_removable);
complect_btns_sizer->Add(p->btn_remove_device);
btns_sizer->Add(p->btn_reslice, 0, wxEXPAND | wxTOP, margin_5);
btns_sizer->Add(p->btn_send_gcode, 0, wxEXPAND | wxTOP, margin_5);
btns_sizer->Add(p->btn_export_gcode, 0, wxEXPAND | wxTOP, margin_5);
btns_sizer->Add(complect_btns_sizer, 0, wxEXPAND | wxTOP, margin_5);
auto *sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(p->scrolled, 1, wxEXPAND);
@ -870,7 +904,7 @@ Sidebar::Sidebar(Plater *parent)
SetSizer(sizer);
// Events
p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(); });
p->btn_export_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(false); });
p->btn_reslice->Bind(wxEVT_BUTTON, [this](wxCommandEvent&)
{
const bool export_gcode_after_slicing = wxGetKeyState(WXK_SHIFT);
@ -881,6 +915,8 @@ Sidebar::Sidebar(Plater *parent)
p->plater->select_view_3D("Preview");
});
p->btn_send_gcode->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->send_gcode(); });
p->btn_remove_device->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->eject_drive(); });
p->btn_export_gcode_removable->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { p->plater->export_gcode(true); });
}
Sidebar::~Sidebar() {}
@ -921,18 +957,18 @@ void Sidebar::update_all_preset_comboboxes()
// Update the print choosers to only contain the compatible presets, update the dirty flags.
if (print_tech == ptFFF)
preset_bundle.prints.update_platter_ui(p->combo_print);
preset_bundle.prints.update_plater_ui(p->combo_print);
else {
preset_bundle.sla_prints.update_platter_ui(p->combo_sla_print);
preset_bundle.sla_materials.update_platter_ui(p->combo_sla_material);
preset_bundle.sla_prints.update_plater_ui(p->combo_sla_print);
preset_bundle.sla_materials.update_plater_ui(p->combo_sla_material);
}
// Update the printer choosers, update the dirty flags.
preset_bundle.printers.update_platter_ui(p->combo_printer);
preset_bundle.printers.update_plater_ui(p->combo_printer);
// Update the filament choosers to only contain the compatible presets, update the color preview,
// update the dirty flags.
if (print_tech == ptFFF) {
for (size_t i = 0; i < p->combos_filament.size(); ++i)
preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]);
preset_bundle.update_plater_filament_ui(i, p->combos_filament[i]);
}
}
@ -955,22 +991,22 @@ void Sidebar::update_presets(Preset::Type preset_type)
}
for (size_t i = 0; i < filament_cnt; i++) {
preset_bundle.update_platter_filament_ui(i, p->combos_filament[i]);
preset_bundle.update_plater_filament_ui(i, p->combos_filament[i]);
}
break;
}
case Preset::TYPE_PRINT:
preset_bundle.prints.update_platter_ui(p->combo_print);
preset_bundle.prints.update_plater_ui(p->combo_print);
break;
case Preset::TYPE_SLA_PRINT:
preset_bundle.sla_prints.update_platter_ui(p->combo_sla_print);
preset_bundle.sla_prints.update_plater_ui(p->combo_sla_print);
break;
case Preset::TYPE_SLA_MATERIAL:
preset_bundle.sla_materials.update_platter_ui(p->combo_sla_material);
preset_bundle.sla_materials.update_plater_ui(p->combo_sla_material);
break;
case Preset::TYPE_PRINTER:
@ -1026,6 +1062,13 @@ void Sidebar::msw_rescale()
p->object_info->msw_rescale();
p->btn_send_gcode->msw_rescale();
p->btn_remove_device->msw_rescale();
p->btn_export_gcode_removable->msw_rescale();
const int scaled_height = p->btn_remove_device->GetBitmap().GetHeight() + 4;
p->btn_export_gcode->SetMinSize(wxSize(-1, scaled_height));
p->btn_reslice ->SetMinSize(wxSize(-1, scaled_height));
p->scrolled->Layout();
}
@ -1158,9 +1201,9 @@ void Sidebar::update_sliced_info_sizer()
{
double material_cost = cfg->option("bottle_cost")->getFloat() /
cfg->option("bottle_volume")->getFloat();
str_total_cost = wxString::Format("%.2f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000);
str_total_cost = wxString::Format("%.3f", material_cost*(ps.objects_used_material + ps.support_used_material) / 1000);
}
p->sliced_info->SetTextAndShow(siCost, str_total_cost);
p->sliced_info->SetTextAndShow(siCost, str_total_cost, "Cost");
wxString t_est = std::isnan(ps.estimated_print_time) ? "N/A" : get_time_dhms(float(ps.estimated_print_time));
p->sliced_info->SetTextAndShow(siEstimatedTime, t_est, _(L("Estimated printing time")) + " :");
@ -1254,11 +1297,15 @@ void Sidebar::enable_buttons(bool enable)
p->btn_reslice->Enable(enable);
p->btn_export_gcode->Enable(enable);
p->btn_send_gcode->Enable(enable);
p->btn_remove_device->Enable(enable);
p->btn_export_gcode_removable->Enable(enable);
}
bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
bool Sidebar::show_reslice(bool show) const { return p->btn_reslice->Show(show); }
bool Sidebar::show_export(bool show) const { return p->btn_export_gcode->Show(show); }
bool Sidebar::show_send(bool show) const { return p->btn_send_gcode->Show(show); }
bool Sidebar::show_disconnect(bool show) const { return p->btn_remove_device->Show(show); }
bool Sidebar::show_export_removable(bool show)const { return p->btn_export_gcode_removable->Show(show); }
bool Sidebar::is_multifilament()
{
@ -1819,6 +1866,13 @@ struct Plater::priv
bool is_preview_loaded() const { return preview->is_loaded(); }
bool is_view3D_shown() const { return current_panel == view3D; }
void set_current_canvas_as_dirty();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
GLCanvas3D* get_current_canvas3D();
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool init_view_toolbar();
void reset_all_gizmos();
void update_ui_from_settings();
ProgressStatusBar* statusbar();
@ -1944,7 +1998,9 @@ struct Plater::priv
bool can_fix_through_netfabb() const;
bool can_set_instance_to_object() const;
bool can_mirror() const;
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool can_reload_from_disk() const;
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
#if ENABLE_THUMBNAIL_GENERATOR
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool show_bed, bool transparent_background);
@ -1964,7 +2020,6 @@ private:
bool complit_init_object_menu();
bool complit_init_sla_object_menu();
bool complit_init_part_menu();
void init_view_toolbar();
bool can_split() const;
bool layers_height_allowed() const;
@ -2033,7 +2088,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
background_process.set_finished_event(EVT_PROCESS_COMPLETED);
// Default printer technology for default config.
background_process.select_technology(this->printer_technology);
// Register progress callback from the Print class to the Platter.
// Register progress callback from the Print class to the Plater.
auto statuscb = [this](const Slic3r::PrintBase::SlicingStatus &status) {
wxQueueEvent(this->q, new Slic3r::SlicingStatusEvent(EVT_SLICING_UPDATE, 0, status));
@ -2098,11 +2153,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
// 3DScene/Toolbar:
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
@ -2116,7 +2169,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_OBJECTS, &priv::on_action_split_objects, this);
view3D_canvas->Bind(EVT_GLTOOLBAR_SPLIT_VOLUMES, &priv::on_action_split_volumes, this);
view3D_canvas->Bind(EVT_GLTOOLBAR_LAYERSEDITING, &priv::on_action_layersediting, this);
view3D_canvas->Bind(EVT_GLCANVAS_INIT, [this](SimpleEvent&) { init_view_toolbar(); });
view3D_canvas->Bind(EVT_GLCANVAS_UPDATE_BED_SHAPE, [this](SimpleEvent&)
{
set_bed_shape(config->option<ConfigOptionPoints>("bed_shape")->values,
@ -2156,6 +2208,9 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
// Initialize the Undo / Redo stack with a first snapshot.
this->take_snapshot(_(L("New Project")));
//void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
RemovableDriveManager::get_instance().set_drive_count_changed_callback(std::bind(&Plater::priv::show_action_buttons, this, std::placeholders::_1));
}
Plater::priv::~priv()
@ -2312,7 +2367,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (object->volumes.size() > 1)
{
Slic3r::GUI::show_info(nullptr,
_(L("You can't load SLA project if there is at least one multi-part object on the bed")) + "\n\n" +
_(L("You cannot load SLA project with a multi-part object on the bed")) + "\n\n" +
_(L("Please check your object list before preset changing.")),
_(L("Attention!")));
return obj_idxs;
@ -2326,7 +2381,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
config += std::move(config_loaded);
}
this->model.custom_gcode_per_height = model.custom_gcode_per_height;
this->model.custom_gcode_per_print_z = model.custom_gcode_per_print_z;
}
if (load_config)
@ -2745,7 +2800,7 @@ void Plater::priv::reset()
// The hiding of the slicing results, if shown, is not taken care by the background process, so we do it here
this->sidebar->show_sliced_info_sizer(false);
model.custom_gcode_per_height.clear();
model.custom_gcode_per_print_z.gcodes.clear();
}
void Plater::priv::mirror(Axis axis)
@ -2976,7 +3031,6 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool
this->update_print_volume_state();
// Apply new config to the possibly running background task.
bool was_running = this->background_process.running();
this->background_process.set_force_update_print_regions(force_validation);
Print::ApplyStatus invalidated = this->background_process.apply(this->q->model(), wxGetApp().preset_bundle->full_config());
// Just redraw the 3D canvas without reloading the scene to consume the update of the layer height profile.
@ -3146,6 +3200,7 @@ void Plater::priv::update_fff_scene()
this->preview->reload_print();
// In case this was MM print, wipe tower bounding box on 3D tab might need redrawing with exact depth:
view3D->reload_scene(true);
}
void Plater::priv::update_sla_scene()
@ -3195,19 +3250,91 @@ void Plater::priv::reload_from_disk()
// collects paths of files to load
std::vector<fs::path> input_paths;
std::vector<fs::path> missing_input_paths;
for (const SelectedVolume& v : selected_volumes)
{
const ModelVolume* volume = model.objects[v.object_idx]->volumes[v.volume_idx];
if (!volume->source.input_file.empty() && boost::filesystem::exists(volume->source.input_file))
input_paths.push_back(volume->source.input_file);
const ModelObject* object = model.objects[v.object_idx];
const ModelVolume* volume = object->volumes[v.volume_idx];
if (!volume->source.input_file.empty())
{
if (fs::exists(volume->source.input_file))
input_paths.push_back(volume->source.input_file);
else
missing_input_paths.push_back(volume->source.input_file);
}
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
else if (!volume->name.empty())
missing_input_paths.push_back(volume->name);
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
}
std::sort(missing_input_paths.begin(), missing_input_paths.end());
missing_input_paths.erase(std::unique(missing_input_paths.begin(), missing_input_paths.end()), missing_input_paths.end());
while (!missing_input_paths.empty())
{
// ask user to select the missing file
fs::path search = missing_input_paths.back();
wxString title = _(L("Please select the file to reload"));
#if defined(__APPLE__)
title += " (" + from_u8(search.filename().string()) + ")";
#endif // __APPLE__
title += ":";
wxFileDialog dialog(q, title, "", from_u8(search.filename().string()), file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_FILE_MUST_EXIST);
if (dialog.ShowModal() != wxID_OK)
return;
std::string sel_filename_path = dialog.GetPath().ToUTF8().data();
std::string sel_filename = fs::path(sel_filename_path).filename().string();
if (boost::algorithm::iequals(search.filename().string(), sel_filename))
{
input_paths.push_back(sel_filename_path);
missing_input_paths.pop_back();
fs::path sel_path = fs::path(sel_filename_path).remove_filename().string();
std::vector<fs::path>::iterator it = missing_input_paths.begin();
while (it != missing_input_paths.end())
{
// try to use the path of the selected file with all remaining missing files
fs::path repathed_filename = sel_path;
repathed_filename /= it->filename();
if (fs::exists(repathed_filename))
{
input_paths.push_back(repathed_filename.string());
it = missing_input_paths.erase(it);
}
else
++it;
}
}
else
{
wxString message = _(L("It is not allowed to change the file to reload")) + " (" + from_u8(search.filename().string()) + ").\n" + _(L("Do you want to retry")) + " ?";
wxMessageDialog dlg(q, message, wxMessageBoxCaptionStr, wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION);
if (dlg.ShowModal() != wxID_YES)
return;
}
}
std::sort(input_paths.begin(), input_paths.end());
input_paths.erase(std::unique(input_paths.begin(), input_paths.end()), input_paths.end());
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
std::vector<wxString> fail_list;
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
// load one file at a time
for (size_t i = 0; i < input_paths.size(); ++i)
{
const auto& path = input_paths[i].string();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
wxBusyCursor wait;
wxBusyInfo info(_(L("Reload from: ")) + from_u8(path), q->get_current_canvas3D()->get_wxglcanvas());
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
Model new_model;
try
{
@ -3221,42 +3348,107 @@ void Plater::priv::reload_from_disk()
catch (std::exception&)
{
// error while loading
view3D->get_canvas3d()->enable_render(true);
return;
}
// update the selected volumes whose source is the current file
for (const SelectedVolume& old_v : selected_volumes)
for (const SelectedVolume& sel_v : selected_volumes)
{
ModelObject* old_model_object = model.objects[old_v.object_idx];
ModelVolume* old_volume = old_model_object->volumes[old_v.volume_idx];
ModelObject* old_model_object = model.objects[sel_v.object_idx];
ModelVolume* old_volume = old_model_object->volumes[sel_v.volume_idx];
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool has_source = !old_volume->source.input_file.empty() && boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(), fs::path(path).filename().string());
bool has_name = !old_volume->name.empty() && boost::algorithm::iequals(old_volume->name, fs::path(path).filename().string());
if (has_source || has_name)
#else
int new_volume_idx = old_volume->source.volume_idx;
int new_object_idx = old_volume->source.object_idx;
if (old_volume->source.input_file == path)
if (boost::algorithm::iequals(fs::path(old_volume->source.input_file).filename().string(),
fs::path(path).filename().string()))
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
{
if (new_object_idx < (int)new_model.objects.size())
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
int new_volume_idx = -1;
int new_object_idx = -1;
if (has_source)
{
ModelObject* new_model_object = new_model.objects[new_object_idx];
if (new_volume_idx < (int)new_model_object->volumes.size())
// take idxs from source
new_volume_idx = old_volume->source.volume_idx;
new_object_idx = old_volume->source.object_idx;
}
else
{
// take idxs from the 1st matching volume
for (size_t o = 0; o < new_model.objects.size(); ++o)
{
old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
ModelVolume* new_volume = old_model_object->volumes.back();
new_volume->set_new_unique_id();
new_volume->config.apply(old_volume->config);
new_volume->set_type(old_volume->type());
new_volume->set_material_id(old_volume->material_id());
new_volume->set_transformation(old_volume->get_transformation());
new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
std::swap(old_model_object->volumes[old_v.volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
ModelObject* obj = new_model.objects[o];
bool found = false;
for (size_t v = 0; v < obj->volumes.size(); ++v)
{
if (obj->volumes[v]->name == old_volume->name)
{
new_volume_idx = (int)v;
new_object_idx = (int)o;
found = true;
break;
}
}
if (found)
break;
}
}
if ((new_object_idx < 0) && ((int)new_model.objects.size() <= new_object_idx))
{
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
continue;
}
#else
assert(new_object_idx < (int)new_model.objects.size());
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
ModelObject* new_model_object = new_model.objects[new_object_idx];
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
if ((new_volume_idx < 0) && ((int)new_model.objects.size() <= new_volume_idx))
{
fail_list.push_back(from_u8(has_source ? old_volume->source.input_file : old_volume->name));
continue;
}
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
if (new_volume_idx < (int)new_model_object->volumes.size())
{
old_model_object->add_volume(*new_model_object->volumes[new_volume_idx]);
ModelVolume* new_volume = old_model_object->volumes.back();
new_volume->set_new_unique_id();
new_volume->config.apply(old_volume->config);
new_volume->set_type(old_volume->type());
new_volume->set_material_id(old_volume->material_id());
new_volume->set_transformation(old_volume->get_transformation() * old_volume->source.transform);
new_volume->translate(new_volume->get_transformation().get_matrix(true) * (new_volume->source.mesh_offset - old_volume->source.mesh_offset));
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
new_volume->source.input_file = path;
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
std::swap(old_model_object->volumes[sel_v.volume_idx], old_model_object->volumes.back());
old_model_object->delete_volume(old_model_object->volumes.size() - 1);
old_model_object->ensure_on_bed();
}
}
}
}
model.adjust_min_z();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
if (!fail_list.empty())
{
wxString message = _(L("Unable to reload:")) + "\n";
for (const wxString& s : fail_list)
{
message += s + "\n";
}
wxMessageDialog dlg(q, message, _(L("Error during reload")), wxOK | wxOK_DEFAULT | wxICON_WARNING);
dlg.ShowModal();
}
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
// update 3D scene
update();
@ -3350,7 +3542,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
// keeps current gcode preview, if any
preview->reload_print(true);
preview->set_canvas_as_dirty();
preview->set_as_dirty();
view_toolbar.select_item("Preview");
}
@ -3380,8 +3572,8 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
// TODO: ?
if (preset_type == Preset::TYPE_FILAMENT && sidebar->is_multifilament()) {
// Only update the platter UI for the 2nd and other filaments.
wxGetApp().preset_bundle->update_platter_filament_ui(idx, combo);
// Only update the plater UI for the 2nd and other filaments.
wxGetApp().preset_bundle->update_plater_filament_ui(idx, combo);
}
else {
wxWindowUpdateLocker noUpdates(sidebar->presets_panel());
@ -3491,6 +3683,7 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
break;
default: break;
}
if (canceled) {
if (wxGetApp().get_mode() == comSimple)
@ -3499,6 +3692,12 @@ void Plater::priv::on_process_completed(wxCommandEvent &evt)
}
else if (wxGetApp().get_mode() == comSimple)
show_action_buttons(false);
if(!canceled && RemovableDriveManager::get_instance().get_is_writing())
{
RemovableDriveManager::get_instance().set_is_writing(false);
show_action_buttons(false);
}
}
void Plater::priv::on_layer_editing_toggled(bool enable)
@ -3542,17 +3741,32 @@ void Plater::priv::on_right_click(RBtnEvent& evt)
wxMenu* menu = nullptr;
if (obj_idx == -1)
menu = &default_menu;
if (obj_idx == -1) // no one or several object are selected
{
if (evt.data.second) // right button was clicked on empty space
menu = &default_menu;
else
{
sidebar->obj_list()->show_multi_selection_menu();
return;
}
}
else
{
// If in 3DScene is(are) selected volume(s), but right button was clicked on empty space
if (evt.data.second)
return;
menu = printer_technology == ptSLA ? &sla_object_menu :
get_selection().is_single_full_instance() ? // show "Object menu" for each FullInstance instead of FullObject
&object_menu : &part_menu;
if (printer_technology == ptSLA)
menu = &sla_object_menu;
else
{
// show "Object menu" for each one or several FullInstance instead of FullObject
const bool is_some_full_instances = get_selection().is_single_full_instance() ||
get_selection().is_single_full_object() ||
get_selection().is_multiple_full_instance();
menu = is_some_full_instances ? &object_menu : &part_menu;
}
sidebar->obj_list()->append_menu_item_settings(menu);
@ -3712,8 +3926,13 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
append_menu_item(menu, wxID_ANY, _(L("Delete")) + "\tDel", _(L("Remove the selected object")),
[this](wxCommandEvent&) { q->remove_selected(); }, "delete", nullptr, [this]() { return can_delete(); }, q);
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu);
#else
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected volumes from disk")),
[this](wxCommandEvent&) { q->reload_from_disk(); }, "", menu, [this]() { return can_reload_from_disk(); }, q);
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
sidebar->obj_list()->append_menu_item_export_stl(menu);
}
@ -3741,18 +3960,27 @@ bool Plater::priv::init_common_menu(wxMenu* menu, const bool is_part/* = false*/
wxMenuItem* menu_item_printable = sidebar->obj_list()->append_menu_item_printable(menu, q);
menu->AppendSeparator();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")),
[this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr);
#else
append_menu_item(menu, wxID_ANY, _(L("Reload from disk")), _(L("Reload the selected object from disk")),
[this](wxCommandEvent&) { reload_from_disk(); }, "", nullptr, [this]() { return can_reload_from_disk(); }, q);
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
append_menu_item(menu, wxID_ANY, _(L("Export as STL")) + dots, _(L("Export the selected object as STL file")),
[this](wxCommandEvent&) { q->export_stl(false, true); });
[this](wxCommandEvent&) { q->export_stl(false, true); }, "", nullptr,
[this]() {
const Selection& selection = get_selection();
return selection.is_single_full_instance() || selection.is_single_full_object();
}, q);
menu->AppendSeparator();
q->Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) {
const Selection& selection = get_selection();
int instance_idx = selection.get_instance_idx();
evt.Enable(instance_idx != -1);
evt.Enable(selection.is_single_full_instance() || selection.is_single_full_object());
if (instance_idx != -1)
{
evt.Check(model.objects[selection.get_object_idx()]->instances[instance_idx]->printable);
@ -3798,7 +4026,7 @@ bool Plater::priv::complit_init_object_menu()
object_menu.AppendSeparator();
// Layers Editing for object
sidebar->obj_list()->append_menu_item_layers_editing(&object_menu);
sidebar->obj_list()->append_menu_item_layers_editing(&object_menu, q);
object_menu.AppendSeparator();
// "Add (volumes)" popupmenu will be added later in append_menu_items_add_volume()
@ -3833,8 +4061,27 @@ bool Plater::priv::complit_init_part_menu()
return true;
}
void Plater::priv::init_view_toolbar()
void Plater::priv::set_current_canvas_as_dirty()
{
if (current_panel == view3D)
view3D->set_as_dirty();
else if (current_panel == preview)
preview->set_as_dirty();
}
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
GLCanvas3D* Plater::priv::get_current_canvas3D()
{
return (current_panel == view3D) ? view3D->get_canvas3d() : ((current_panel == preview) ? preview->get_canvas3d() : nullptr);
}
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool Plater::priv::init_view_toolbar()
{
if (view_toolbar.get_items_count() > 0)
// already initialized
return true;
BackgroundTexture::Metadata background_data;
background_data.filename = "toolbar_background.png";
background_data.left = 16;
@ -3843,7 +4090,7 @@ void Plater::priv::init_view_toolbar()
background_data.bottom = 16;
if (!view_toolbar.init(background_data))
return;
return false;
view_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left);
view_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Bottom);
@ -3858,7 +4105,7 @@ void Plater::priv::init_view_toolbar()
item.sprite_id = 0;
item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_3D)); };
if (!view_toolbar.add_item(item))
return;
return false;
item.name = "Preview";
item.icon_filename = "preview.svg";
@ -3866,10 +4113,12 @@ void Plater::priv::init_view_toolbar()
item.sprite_id = 1;
item.left.action_callback = [this]() { if (this->q != nullptr) wxPostEvent(this->q, SimpleEvent(EVT_GLVIEWTOOLBAR_PREVIEW)); };
if (!view_toolbar.add_item(item))
return;
return false;
view_toolbar.select_item("3D");
view_toolbar.set_enabled(true);
return true;
}
bool Plater::priv::can_set_instance_to_object() const
@ -3897,6 +4146,7 @@ bool Plater::priv::can_mirror() const
return get_selection().is_from_single_instance();
}
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool Plater::priv::can_reload_from_disk() const
{
// struct to hold selected ModelVolumes by their indices
@ -3920,7 +4170,11 @@ bool Plater::priv::can_reload_from_disk() const
const GLVolume* v = selection.get_volume(idx);
int v_idx = v->volume_idx();
if (v_idx >= 0)
selected_volumes.push_back({ v->object_idx(), v_idx });
{
int o_idx = v->object_idx();
if ((0 <= o_idx) && (o_idx < (int)model.objects.size()))
selected_volumes.push_back({ o_idx, v_idx });
}
}
std::sort(selected_volumes.begin(), selected_volumes.end());
selected_volumes.erase(std::unique(selected_volumes.begin(), selected_volumes.end()), selected_volumes.end());
@ -3930,7 +4184,7 @@ bool Plater::priv::can_reload_from_disk() const
for (const SelectedVolume& v : selected_volumes)
{
const ModelVolume* volume = model.objects[v.object_idx]->volumes[v.volume_idx];
if (!volume->source.input_file.empty() && boost::filesystem::exists(volume->source.input_file))
if (!volume->source.input_file.empty())
paths.push_back(volume->source.input_file);
}
std::sort(paths.begin(), paths.end());
@ -3938,6 +4192,7 @@ bool Plater::priv::can_reload_from_disk() const
return !paths.empty();
}
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
void Plater::priv::set_bed_shape(const Pointfs& shape, const std::string& custom_texture, const std::string& custom_model)
{
@ -4015,23 +4270,30 @@ void Plater::priv::update_object_menu()
void Plater::priv::show_action_buttons(const bool is_ready_to_slice) const
{
RemovableDriveManager::get_instance().set_plater_ready_to_slice(is_ready_to_slice);
wxWindowUpdateLocker noUpdater(sidebar);
const auto prin_host_opt = config->option<ConfigOptionString>("print_host");
const bool send_gcode_shown = prin_host_opt != nullptr && !prin_host_opt->value.empty();
bool disconnect_shown = !RemovableDriveManager::get_instance().is_last_drive_removed();
bool export_removable_shown = RemovableDriveManager::get_instance().get_drives_count() > 0;
// when a background processing is ON, export_btn and/or send_btn are showing
if (wxGetApp().app_config->get("background_processing") == "1")
{
if (sidebar->show_reslice(false) |
sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown))
if (sidebar->show_reslice(false) |
sidebar->show_export(true) |
sidebar->show_send(send_gcode_shown) |
sidebar->show_export_removable(export_removable_shown) |
sidebar->show_disconnect(disconnect_shown))
sidebar->Layout();
}
else
{
if (sidebar->show_reslice(is_ready_to_slice) |
sidebar->show_export(!is_ready_to_slice) |
sidebar->show_send(send_gcode_shown && !is_ready_to_slice))
sidebar->show_send(send_gcode_shown && !is_ready_to_slice) |
sidebar->show_export_removable(export_removable_shown && !is_ready_to_slice) |
sidebar->show_disconnect(disconnect_shown && !is_ready_to_slice))
sidebar->Layout();
}
}
@ -4272,7 +4534,7 @@ void Sidebar::set_btn_label(const ActionButtonType btn_type, const wxString& lab
{
case ActionButtonType::abReslice: p->btn_reslice->SetLabelText(label); break;
case ActionButtonType::abExport: p->btn_export_gcode->SetLabelText(label); break;
case ActionButtonType::abSendGCode: p->btn_send_gcode->SetLabelText(label); break;
case ActionButtonType::abSendGCode: /*p->btn_send_gcode->SetLabelText(label);*/ break;
}
}
@ -4531,9 +4793,16 @@ void Plater::cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_uppe
remove(obj_idx);
p->load_model_objects(new_objects);
Selection& selection = p->get_selection();
size_t last_id = p->model.objects.size() - 1;
for (size_t i = 0; i < new_objects.size(); ++i)
{
selection.add_object((unsigned int)(last_id - i), i == 0);
}
}
void Plater::export_gcode()
void Plater::export_gcode(bool prefer_removable)
{
if (p->model.objects.empty())
return;
@ -4555,7 +4824,21 @@ void Plater::export_gcode()
}
default_output_file = fs::path(Slic3r::fold_utf8_to_ascii(default_output_file.string()));
auto start_dir = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string());
bool removable_drives_connected = GUI::RemovableDriveManager::get_instance().update();
if(prefer_removable)
{
if(removable_drives_connected)
{
auto start_dir_removable = wxGetApp().app_config->get_last_output_dir(default_output_file.parent_path().string(), true);
if (RemovableDriveManager::get_instance().is_path_on_removable_drive(start_dir_removable))
{
start_dir = start_dir_removable;
}else
{
start_dir = RemovableDriveManager::get_instance().get_drive_path();
}
}
}
wxFileDialog dlg(this, (printer_technology() == ptFFF) ? _(L("Save G-code file as:")) : _(L("Save SL1 file as:")),
start_dir,
from_path(default_output_file.filename()),
@ -4566,11 +4849,26 @@ void Plater::export_gcode()
fs::path output_path;
if (dlg.ShowModal() == wxID_OK) {
fs::path path = into_path(dlg.GetPath());
wxGetApp().app_config->update_last_output_dir(path.parent_path().string());
wxGetApp().app_config->update_last_output_dir(path.parent_path().string(), RemovableDriveManager::get_instance().is_path_on_removable_drive(path.parent_path().string()));
output_path = std::move(path);
}
if (! output_path.empty())
{
std::string path = output_path.string();
p->export_gcode(std::move(output_path), PrintHostJob());
RemovableDriveManager::get_instance().update(0, false);
RemovableDriveManager::get_instance().set_last_save_path(path);
RemovableDriveManager::get_instance().verify_last_save_path();
if(!RemovableDriveManager::get_instance().is_last_drive_removed())
{
RemovableDriveManager::get_instance().set_is_writing(true);
RemovableDriveManager::get_instance().erase_callbacks();
RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this));
}
}
}
void Plater::export_stl(bool extended, bool selection_only)
@ -4682,7 +4980,12 @@ void Plater::export_amf()
wxBusyCursor wait;
bool export_config = true;
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
#else
if (Slic3r::store_amf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) {
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// Success
p->statusbar()->set_status_text(wxString::Format(_(L("AMF file exported to %s")), path));
} else {
@ -4711,6 +5014,16 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
DynamicPrintConfig cfg = wxGetApp().preset_bundle->full_config_secure();
const std::string path_u8 = into_u8(path);
wxBusyCursor wait;
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
bool full_pathnames = wxGetApp().app_config->get("export_sources_full_pathnames") == "1";
#if ENABLE_THUMBNAIL_GENERATOR
ThumbnailData thumbnail_data;
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true);
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames, &thumbnail_data)) {
#else
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr, full_pathnames)) {
#endif // ENABLE_THUMBNAIL_GENERATOR
#else
#if ENABLE_THUMBNAIL_GENERATOR
ThumbnailData thumbnail_data;
p->generate_thumbnail(thumbnail_data, THUMBNAIL_SIZE_3MF.first, THUMBNAIL_SIZE_3MF.second, false, true, true, true);
@ -4718,6 +5031,7 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
#else
if (Slic3r::store_3mf(path_u8.c_str(), &p->model, export_config ? &cfg : nullptr)) {
#endif // ENABLE_THUMBNAIL_GENERATOR
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// Success
p->statusbar()->set_status_text(wxString::Format(_(L("3MF file exported to %s")), path));
p->set_project_filename(path);
@ -4850,6 +5164,27 @@ void Plater::send_gcode()
}
}
void Plater::eject_drive()
{
RemovableDriveManager::get_instance().update(0, true);
RemovableDriveManager::get_instance().erase_callbacks();
RemovableDriveManager::get_instance().add_remove_callback(std::bind(&Plater::drive_ejected_callback, this));
RemovableDriveManager::get_instance().eject_drive(RemovableDriveManager::get_instance().get_last_save_path());
}
void Plater::drive_ejected_callback()
{
if (RemovableDriveManager::get_instance().get_did_eject())
{
RemovableDriveManager::get_instance().set_did_eject(false);
wxString message = "Unmounting successful. The device " + RemovableDriveManager::get_instance().get_ejected_name() + "(" + RemovableDriveManager::get_instance().get_ejected_path() + ")" + " can now be safely removed from the computer.";
wxMessageBox(message);
}
p->show_action_buttons(false);
}
void Plater::take_snapshot(const std::string &snapshot_name) { p->take_snapshot(snapshot_name); }
void Plater::take_snapshot(const wxString &snapshot_name) { p->take_snapshot(snapshot_name); }
void Plater::suppress_snapshots() { p->suppress_snapshots(); }
@ -4919,7 +5254,7 @@ void Plater::on_extruders_change(size_t num_extruders)
choices.push_back(choice);
// initialize selection
wxGetApp().preset_bundle->update_platter_filament_ui(i, choice);
wxGetApp().preset_bundle->update_plater_filament_ui(i, choice);
++i;
}
@ -5060,6 +5395,7 @@ const DynamicPrintConfig* Plater::get_plater_config() const
return p->config;
}
// Get vector of extruder colors considering filament color, if extruder color is undefined.
std::vector<std::string> Plater::get_extruder_colors_from_plater_config() const
{
const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->printers.get_edited_preset().config;
@ -5079,13 +5415,17 @@ std::vector<std::string> Plater::get_extruder_colors_from_plater_config() const
return extruder_colors;
}
/* Get vector of colors used for rendering of a Preview scene in "Color print" mode
* It consists of extruder colors and colors, saved in model.custom_gcode_per_print_z
*/
std::vector<std::string> Plater::get_colors_for_color_print() const
{
std::vector<std::string> colors = get_extruder_colors_from_plater_config();
colors.reserve(colors.size() + p->model.custom_gcode_per_print_z.gcodes.size());
for (const Model::CustomGCode& code : p->model.custom_gcode_per_height)
for (const Model::CustomGCode& code : p->model.custom_gcode_per_print_z.gcodes)
if (code.gcode == ColorChangeCode)
colors.push_back(code.color);
colors.emplace_back(code.color);
return colors;
}
@ -5120,11 +5460,23 @@ GLCanvas3D* Plater::canvas3D()
return p->view3D->get_canvas3d();
}
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
GLCanvas3D* Plater::get_current_canvas3D()
{
return p->get_current_canvas3D();
}
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
BoundingBoxf Plater::bed_shape_bb() const
{
return p->bed_shape_bb();
}
void Plater::set_current_canvas_as_dirty()
{
p->set_current_canvas_as_dirty();
}
PrinterTechnology Plater::printer_technology() const
{
return p->printer_technology;
@ -5242,6 +5594,11 @@ void Plater::msw_rescale()
GetParent()->Layout();
}
bool Plater::init_view_toolbar()
{
return p->init_view_toolbar();
}
const Camera& Plater::get_camera() const
{
return p->camera;
@ -5302,7 +5659,9 @@ bool Plater::can_copy_to_clipboard() const
bool Plater::can_undo() const { return p->undo_redo_stack().has_undo_snapshot(); }
bool Plater::can_redo() const { return p->undo_redo_stack().has_redo_snapshot(); }
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool Plater::can_reload_from_disk() const { return p->can_reload_from_disk(); }
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
const UndoRedo::Stack& Plater::undo_redo_stack_main() const { return p->undo_redo_stack_main(); }
void Plater::enter_gizmos_stack() { p->enter_gizmos_stack(); }
void Plater::leave_gizmos_stack() { p->leave_gizmos_stack(); }

View file

@ -119,6 +119,8 @@ public:
bool show_reslice(bool show) const;
bool show_export(bool show) const;
bool show_send(bool show) const;
bool show_disconnect(bool show)const;
bool show_export_removable(bool show) const;
bool is_multifilament();
void update_mode();
@ -185,7 +187,7 @@ public:
void cut(size_t obj_idx, size_t instance_idx, coordf_t z, bool keep_upper = true, bool keep_lower = true, bool rotate_lower = false);
void export_gcode();
void export_gcode(bool prefer_removable = true);
void export_stl(bool extended = false, bool selection_only = false);
void export_amf();
void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
@ -201,6 +203,8 @@ public:
void suppress_background_process(const bool stop_background_process) ;
void fix_through_netfabb(const int obj_idx, const int vol_idx = -1);
void send_gcode();
void eject_drive();
void drive_ejected_callback();
void take_snapshot(const std::string &snapshot_name);
void take_snapshot(const wxString &snapshot_name);
@ -235,8 +239,13 @@ public:
int get_selected_object_idx();
bool is_single_full_object_selection() const;
GLCanvas3D* canvas3D();
#if ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
GLCanvas3D* get_current_canvas3D();
#endif // ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
BoundingBoxf bed_shape_bb() const;
void set_current_canvas_as_dirty();
PrinterTechnology printer_technology() const;
void set_printer_technology(PrinterTechnology printer_technology);
@ -257,10 +266,14 @@ public:
bool can_copy_to_clipboard() const;
bool can_undo() const;
bool can_redo() const;
#if !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
bool can_reload_from_disk() const;
#endif // !ENABLE_BACKWARD_COMPATIBLE_RELOAD_FROM_DISK
void msw_rescale();
bool init_view_toolbar();
const Camera& get_camera() const;
const Mouse3DController& get_mouse3d_controller() const;
Mouse3DController& get_mouse3d_controller();

View file

@ -19,16 +19,11 @@ PreferencesDialog::PreferencesDialog(wxWindow* parent) :
void PreferencesDialog::build()
{
auto app_config = get_app_config();
m_optgroup = std::make_shared<ConfigOptionsGroup>(this, _(L("General")));
m_optgroup->label_width = 40;
m_optgroup->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
m_optgroup_general = std::make_shared<ConfigOptionsGroup>(this, _(L("General")));
m_optgroup_general->label_width = 40;
m_optgroup_general->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
if (opt_key == "use_custom_toolbar_size") {
m_icon_size_sizer->ShowItems(boost::any_cast<bool>(value));
this->layout();
}
};
};
// TODO
// $optgroup->append_single_option_line(Slic3r::GUI::OptionsGroup::Option->new(
@ -47,7 +42,7 @@ void PreferencesDialog::build()
"instead of the one containing the input files.");
def.set_default_value(new ConfigOptionBool{ app_config->has("remember_output_path") ? app_config->get("remember_output_path") == "1" : true });
Option option(def, "remember_output_path");
m_optgroup->append_single_option_line(option);
m_optgroup_general->append_single_option_line(option);
def.label = L("Auto-center parts");
def.type = coBool;
@ -55,7 +50,7 @@ void PreferencesDialog::build()
"around the print bed center.");
def.set_default_value(new ConfigOptionBool{ app_config->get("autocenter") == "1" });
option = Option (def,"autocenter");
m_optgroup->append_single_option_line(option);
m_optgroup_general->append_single_option_line(option);
def.label = L("Background processing");
def.type = coBool;
@ -63,7 +58,7 @@ void PreferencesDialog::build()
"as they\'re loaded in order to save time when exporting G-code.");
def.set_default_value(new ConfigOptionBool{ app_config->get("background_processing") == "1" });
option = Option (def,"background_processing");
m_optgroup->append_single_option_line(option);
m_optgroup_general->append_single_option_line(option);
// Please keep in sync with ConfigWizard
def.label = L("Check for application updates");
@ -71,7 +66,17 @@ void PreferencesDialog::build()
def.tooltip = L("If enabled, PrusaSlicer will check for the new versions of itself online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done.");
def.set_default_value(new ConfigOptionBool(app_config->get("version_check") == "1"));
option = Option (def, "version_check");
m_optgroup->append_single_option_line(option);
m_optgroup_general->append_single_option_line(option);
#if ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// Please keep in sync with ConfigWizard
def.label = L("Export sources full pathnames to 3mf and amf");
def.type = coBool;
def.tooltip = L("If enabled, allows the Reload from disk command to automatically find and load the files when invoked.");
def.set_default_value(new ConfigOptionBool(app_config->get("export_sources_full_pathnames") == "1"));
option = Option(def, "export_sources_full_pathnames");
m_optgroup_general->append_single_option_line(option);
#endif // ENABLE_CONFIGURABLE_PATHS_EXPORT_TO_3MF_AND_AMF
// Please keep in sync with ConfigWizard
def.label = L("Update built-in Presets automatically");
@ -79,7 +84,7 @@ void PreferencesDialog::build()
def.tooltip = L("If enabled, Slic3r downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup.");
def.set_default_value(new ConfigOptionBool(app_config->get("preset_update") == "1"));
option = Option (def, "preset_update");
m_optgroup->append_single_option_line(option);
m_optgroup_general->append_single_option_line(option);
def.label = L("Suppress \" - default - \" presets");
def.type = coBool;
@ -87,7 +92,7 @@ void PreferencesDialog::build()
"selections once there are any other valid presets available.");
def.set_default_value(new ConfigOptionBool{ app_config->get("no_defaults") == "1" });
option = Option (def,"no_defaults");
m_optgroup->append_single_option_line(option);
m_optgroup_general->append_single_option_line(option);
def.label = L("Show incompatible print and filament presets");
def.type = coBool;
@ -95,7 +100,7 @@ void PreferencesDialog::build()
"even if they are marked as incompatible with the active printer");
def.set_default_value(new ConfigOptionBool{ app_config->get("show_incompatible_presets") == "1" });
option = Option (def,"show_incompatible_presets");
m_optgroup->append_single_option_line(option);
m_optgroup_general->append_single_option_line(option);
#if __APPLE__
def.label = L("Use Retina resolution for the 3D scene");
@ -104,28 +109,55 @@ void PreferencesDialog::build()
"If you are experiencing 3D performance problems, disabling this option may help.");
def.set_default_value(new ConfigOptionBool{ app_config->get("use_retina_opengl") == "1" });
option = Option (def, "use_retina_opengl");
m_optgroup->append_single_option_line(option);
m_optgroup_general->append_single_option_line(option);
#endif
def.label = L("Use perspective camera");
def.type = coBool;
def.tooltip = L("If enabled, use perspective camera. If not enabled, use orthographic camera.");
def.set_default_value(new ConfigOptionBool{ app_config->get("use_perspective_camera") == "1" });
option = Option(def, "use_perspective_camera");
m_optgroup->append_single_option_line(option);
m_optgroup_camera = std::make_shared<ConfigOptionsGroup>(this, _(L("Camera")));
m_optgroup_camera->label_width = 40;
m_optgroup_camera->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
};
def.label = L("Use perspective camera");
def.type = coBool;
def.tooltip = L("If enabled, use perspective camera. If not enabled, use orthographic camera.");
def.set_default_value(new ConfigOptionBool{ app_config->get("use_perspective_camera") == "1" });
option = Option(def, "use_perspective_camera");
m_optgroup_camera->append_single_option_line(option);
#if ENABLE_6DOF_CAMERA
def.label = L("Use free camera");
def.type = coBool;
def.tooltip = L("If enabled, use free camera. If not enabled, use constrained camera.");
def.set_default_value(new ConfigOptionBool(app_config->get("use_free_camera") == "1"));
option = Option(def, "use_free_camera");
m_optgroup_camera->append_single_option_line(option);
#endif // ENABLE_6DOF_CAMERA
m_optgroup_gui = std::make_shared<ConfigOptionsGroup>(this, _(L("GUI")));
m_optgroup_gui->label_width = 40;
m_optgroup_gui->m_on_change = [this](t_config_option_key opt_key, boost::any value) {
m_values[opt_key] = boost::any_cast<bool>(value) ? "1" : "0";
if (opt_key == "use_custom_toolbar_size") {
m_icon_size_sizer->ShowItems(boost::any_cast<bool>(value));
this->layout();
}
};
def.label = L("Use custom size for toolbar icons");
def.type = coBool;
def.tooltip = L("If enabled, you can change size of toolbar icons manually.");
def.set_default_value(new ConfigOptionBool{ app_config->get("use_custom_toolbar_size") == "1" });
option = Option (def,"use_custom_toolbar_size");
m_optgroup->append_single_option_line(option);
option = Option(def, "use_custom_toolbar_size");
m_optgroup_gui->append_single_option_line(option);
create_icon_size_slider();
m_icon_size_sizer->ShowItems(app_config->get("use_custom_toolbar_size") == "1");
create_icon_size_slider();
m_icon_size_sizer->ShowItems(app_config->get("use_custom_toolbar_size") == "1");
auto sizer = new wxBoxSizer(wxVERTICAL);
sizer->Add(m_optgroup->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
sizer->Add(m_optgroup_general->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
sizer->Add(m_optgroup_camera->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
sizer->Add(m_optgroup_gui->sizer, 0, wxEXPAND | wxBOTTOM | wxLEFT | wxRIGHT, 10);
SetFont(wxGetApp().normal_font());
@ -157,7 +189,9 @@ void PreferencesDialog::accept()
void PreferencesDialog::on_dpi_changed(const wxRect &suggested_rect)
{
m_optgroup->msw_rescale();
m_optgroup_general->msw_rescale();
m_optgroup_camera->msw_rescale();
m_optgroup_gui->msw_rescale();
msw_buttons_rescale(this, em_unit(), { wxID_OK, wxID_CANCEL });
@ -182,7 +216,7 @@ void PreferencesDialog::create_icon_size_slider()
m_icon_size_sizer = new wxBoxSizer(wxHORIZONTAL);
wxWindow* parent = m_optgroup->ctrl_parent();
wxWindow* parent = m_optgroup_gui->ctrl_parent();
if (isOSX)
// For correct rendering of the slider and value label under OSX
@ -230,8 +264,9 @@ void PreferencesDialog::create_icon_size_slider()
win->SetBackgroundStyle(wxBG_STYLE_PAINT);
}
m_optgroup->sizer->Add(m_icon_size_sizer, 0, wxEXPAND | wxALL, em);
m_optgroup_gui->sizer->Add(m_icon_size_sizer, 0, wxEXPAND | wxALL, em);
}
} // GUI
} // Slic3r

View file

@ -15,8 +15,10 @@ class ConfigOptionsGroup;
class PreferencesDialog : public DPIDialog
{
std::map<std::string, std::string> m_values;
std::shared_ptr<ConfigOptionsGroup> m_optgroup;
wxSizer* m_icon_size_sizer;
std::shared_ptr<ConfigOptionsGroup> m_optgroup_general;
std::shared_ptr<ConfigOptionsGroup> m_optgroup_camera;
std::shared_ptr<ConfigOptionsGroup> m_optgroup_gui;
wxSizer* m_icon_size_sizer;
bool isOSX {false};
public:
PreferencesDialog(wxWindow* parent);

View file

@ -184,6 +184,16 @@ VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem
} else {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed variants field: `%2%`") % id % variants_field;
}
auto default_materials_field = section.second.get<std::string>("default_materials", "");
if (default_materials_field.empty())
default_materials_field = section.second.get<std::string>("default_filaments", "");
if (Slic3r::unescape_strings_cstyle(default_materials_field, model.default_materials)) {
Slic3r::sort_remove_duplicates(model.default_materials);
} else {
BOOST_LOG_TRIVIAL(error) << boost::format("Vendor bundle: `%1%`: Malformed default_materials field: `%2%`") % id % default_materials_field;
}
model.bed_model = section.second.get<std::string>("bed_model", "");
model.bed_texture = section.second.get<std::string>("bed_texture", "");
if (! model.id.empty() && ! model.variants.empty())
res.models.push_back(std::move(model));
}
@ -303,60 +313,58 @@ std::string Preset::label() const
return this->name + (this->is_dirty ? g_suffix_modified : "");
}
bool Preset::is_compatible_with_print(const Preset &active_print) const
bool is_compatible_with_print(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_print)
{
auto &condition = this->compatible_prints_condition();
auto *compatible_prints = dynamic_cast<const ConfigOptionStrings*>(this->config.option("compatible_prints"));
if (preset.vendor != nullptr && preset.vendor != active_print.vendor)
// The current profile has a vendor assigned and it is different from the active print's vendor.
return false;
auto &condition = preset.preset.compatible_prints_condition();
auto *compatible_prints = dynamic_cast<const ConfigOptionStrings*>(preset.preset.config.option("compatible_prints"));
bool has_compatible_prints = compatible_prints != nullptr && ! compatible_prints->values.empty();
if (! has_compatible_prints && ! condition.empty()) {
try {
return PlaceholderParser::evaluate_boolean_expression(condition, active_print.config);
return PlaceholderParser::evaluate_boolean_expression(condition, active_print.preset.config);
} catch (const std::runtime_error &err) {
//FIXME in case of an error, return "compatible with everything".
printf("Preset::is_compatible_with_print - parsing error of compatible_prints_condition %s:\n%s\n", active_print.name.c_str(), err.what());
printf("Preset::is_compatible_with_print - parsing error of compatible_prints_condition %s:\n%s\n", active_print.preset.name.c_str(), err.what());
return true;
}
}
return this->is_default || active_print.name.empty() || ! has_compatible_prints ||
std::find(compatible_prints->values.begin(), compatible_prints->values.end(), active_print.name) !=
return preset.preset.is_default || active_print.preset.name.empty() || ! has_compatible_prints ||
std::find(compatible_prints->values.begin(), compatible_prints->values.end(), active_print.preset.name) !=
compatible_prints->values.end();
}
bool Preset::is_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config) const
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config)
{
auto &condition = this->compatible_printers_condition();
auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(this->config.option("compatible_printers"));
if (preset.vendor != nullptr && preset.vendor != active_printer.vendor)
// The current profile has a vendor assigned and it is different from the active print's vendor.
return false;
auto &condition = preset.preset.compatible_printers_condition();
auto *compatible_printers = dynamic_cast<const ConfigOptionStrings*>(preset.preset.config.option("compatible_printers"));
bool has_compatible_printers = compatible_printers != nullptr && ! compatible_printers->values.empty();
if (! has_compatible_printers && ! condition.empty()) {
try {
return PlaceholderParser::evaluate_boolean_expression(condition, active_printer.config, extra_config);
return PlaceholderParser::evaluate_boolean_expression(condition, active_printer.preset.config, extra_config);
} catch (const std::runtime_error &err) {
//FIXME in case of an error, return "compatible with everything".
printf("Preset::is_compatible_with_printer - parsing error of compatible_printers_condition %s:\n%s\n", active_printer.name.c_str(), err.what());
printf("Preset::is_compatible_with_printer - parsing error of compatible_printers_condition %s:\n%s\n", active_printer.preset.name.c_str(), err.what());
return true;
}
}
return this->is_default || active_printer.name.empty() || ! has_compatible_printers ||
std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.name) !=
return preset.preset.is_default || active_printer.preset.name.empty() || ! has_compatible_printers ||
std::find(compatible_printers->values.begin(), compatible_printers->values.end(), active_printer.preset.name) !=
compatible_printers->values.end();
}
bool Preset::is_compatible_with_printer(const Preset &active_printer) const
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer)
{
DynamicPrintConfig config;
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name));
const ConfigOption *opt = active_printer.config.option("nozzle_diameter");
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.preset.name));
const ConfigOption *opt = active_printer.preset.config.option("nozzle_diameter");
if (opt)
config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
return this->is_compatible_with_printer(active_printer, &config);
}
bool Preset::update_compatible(const Preset &active_printer, const DynamicPrintConfig *extra_config, const Preset *active_print)
{
this->is_compatible = is_compatible_with_printer(active_printer, extra_config);
if (active_print != nullptr)
this->is_compatible &= is_compatible_with_print(*active_print);
return this->is_compatible;
return is_compatible_with_printer(preset, active_printer, &config);
}
void Preset::set_visible_from_appconfig(const AppConfig &app_config)
@ -592,6 +600,7 @@ void PresetCollection::reset(bool delete_files)
m_presets.erase(m_presets.begin() + m_num_default_presets, m_presets.end());
this->select_preset(0);
}
m_map_system_profile_renamed.clear();
}
void PresetCollection::add_default_preset(const std::vector<std::string> &keys, const Slic3r::StaticPrintConfig &defaults, const std::string &preset_name)
@ -705,6 +714,11 @@ Preset& PresetCollection::load_external_preset(
// Is there a preset already loaded with the name stored inside the config?
std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
bool found = it != m_presets.end() && it->name == original_name;
if (! found) {
// Try to match the original_name against the "renamed_from" profile names of loaded system profiles.
it = this->find_preset_renamed(original_name);
found = it != m_presets.end();
}
if (found && profile_print_params_same(it->config, cfg)) {
// The preset exists and it matches the values stored inside config.
if (select)
@ -874,24 +888,27 @@ const Preset* PresetCollection::get_selected_preset_parent() const
if (this->get_selected_idx() == -1)
// This preset collection has no preset activated yet. Only the get_edited_preset() is valid.
return nullptr;
// const std::string &inherits = this->get_edited_preset().inherits();
// if (inherits.empty())
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
std::string inherits = this->get_edited_preset().inherits();
if (inherits.empty())
{
if (this->get_selected_preset().is_system || this->get_selected_preset().is_default)
return &this->get_selected_preset();
if (this->get_selected_preset().is_external)
const Preset &selected_preset = this->get_selected_preset();
if (selected_preset.is_system || selected_preset.is_default)
return &selected_preset;
const Preset &edited_preset = this->get_edited_preset();
const std::string &inherits = edited_preset.inherits();
const Preset *preset = nullptr;
if (inherits.empty()) {
if (selected_preset.is_external)
return nullptr;
inherits = m_type != Preset::Type::TYPE_PRINTER ? "- default -" :
this->get_edited_preset().printer_technology() == ptFFF ?
"- default FFF -" : "- default SLA -" ;
preset = &this->default_preset(m_type == Preset::Type::TYPE_PRINTER && edited_preset.printer_technology() == ptSLA ? 1 : 0);
} else
preset = this->find_preset(inherits, false);
if (preset == nullptr) {
// Resolve the "renamed_from" field.
assert(! inherits.empty());
auto it = this->find_preset_renamed(inherits);
if (it != m_presets.end())
preset = &(*it);
}
const Preset* preset = this->find_preset(inherits, false);
return (preset == nullptr/* || preset->is_default*/ || preset->is_external) ? nullptr : preset;
}
@ -902,9 +919,29 @@ const Preset* PresetCollection::get_preset_parent(const Preset& child) const
// return this->get_selected_preset().is_system ? &this->get_selected_preset() : nullptr;
return nullptr;
const Preset* preset = this->find_preset(inherits, false);
if (preset == nullptr) {
auto it = this->find_preset_renamed(inherits);
if (it != m_presets.end())
preset = &(*it);
}
return (preset == nullptr/* || preset->is_default */|| preset->is_external) ? nullptr : preset;
}
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile PresetCollection::get_preset_with_vendor_profile(const Preset &preset) const
{
const Preset *p = &preset;
const VendorProfile *v = nullptr;
do {
if (p->vendor != nullptr) {
v = p->vendor;
break;
}
p = this->get_preset_parent(*p);
} while (p != nullptr);
return PresetWithVendorProfile(preset, v);
}
const std::string& PresetCollection::get_preset_name_by_alias(const std::string& alias) const
{
for (size_t i = this->m_presets.front().is_visible ? 0 : m_num_default_presets; i < this->m_presets.size(); ++i) {
@ -956,19 +993,22 @@ void PresetCollection::set_default_suppressed(bool default_suppressed)
}
}
size_t PresetCollection::update_compatible_internal(const Preset &active_printer, const Preset *active_print, bool unselect_if_incompatible)
size_t PresetCollection::update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, bool unselect_if_incompatible)
{
DynamicPrintConfig config;
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.name));
const ConfigOption *opt = active_printer.config.option("nozzle_diameter");
config.set_key_value("printer_preset", new ConfigOptionString(active_printer.preset.name));
const ConfigOption *opt = active_printer.preset.config.option("nozzle_diameter");
if (opt)
config.set_key_value("num_extruders", new ConfigOptionInt((int)static_cast<const ConfigOptionFloats*>(opt)->values.size()));
for (size_t idx_preset = m_num_default_presets; idx_preset < m_presets.size(); ++ idx_preset) {
bool selected = idx_preset == m_idx_selected;
Preset &preset_selected = m_presets[idx_preset];
Preset &preset_edited = selected ? m_edited_preset : preset_selected;
if (! preset_edited.update_compatible(active_printer, &config, active_print) &&
selected && unselect_if_incompatible)
const PresetWithVendorProfile this_preset_with_vendor_profile = this->get_preset_with_vendor_profile(preset_edited);
preset_edited.is_compatible = is_compatible_with_printer(this_preset_with_vendor_profile, active_printer, &config);
if (active_print != nullptr)
preset_edited.is_compatible &= is_compatible_with_print(this_preset_with_vendor_profile, *active_print);
if (! preset_edited.is_compatible && selected && unselect_if_incompatible)
m_idx_selected = -1;
if (selected)
preset_selected.is_compatible = preset_edited.is_compatible;
@ -986,7 +1026,7 @@ size_t PresetCollection::update_compatible_internal(const Preset &active_printer
// Update the wxChoice UI component from this list of presets.
// Hide the
void PresetCollection::update_platter_ui(GUI::PresetComboBox *ui)
void PresetCollection::update_plater_ui(GUI::PresetComboBox *ui)
{
if (ui == nullptr)
return;
@ -1386,6 +1426,17 @@ std::vector<std::string> PresetCollection::merge_presets(PresetCollection &&othe
return duplicates;
}
void PresetCollection::update_map_system_profile_renamed()
{
m_map_system_profile_renamed.clear();
for (Preset &preset : m_presets)
for (const std::string &renamed_from : preset.renamed_from) {
const auto [it, success] = m_map_system_profile_renamed.insert(std::pair<std::string, std::string>(renamed_from, preset.name));
if (! success)
BOOST_LOG_TRIVIAL(error) << boost::format("Preset name \"%1%\" was marked as renamed from \"%2%\", though preset name \"%3%\" was marked as renamed from \"%2%\" as well.") % preset.name % renamed_from % it->second;
}
}
std::string PresetCollection::name() const
{
switch (this->type()) {
@ -1459,4 +1510,20 @@ const Preset* PrinterPresetCollection::find_by_model_id(const std::string &model
return it != cend() ? &*it : nullptr;
}
namespace PresetUtils {
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset)
{
const VendorProfile::PrinterModel *out = nullptr;
if (preset.vendor != nullptr) {
auto *printer_model = preset.config.opt<ConfigOptionString>("printer_model");
if (printer_model != nullptr && ! printer_model->value.empty()) {
auto it = std::find_if(preset.vendor->models.begin(), preset.vendor->models.end(), [printer_model](const VendorProfile::PrinterModel &pm) { return pm.id == printer_model->value; });
if (it != preset.vendor->models.end())
out = &(*it);
}
}
return out;
}
} // namespace PresetUtils
} // namespace Slic3r

View file

@ -61,6 +61,10 @@ public:
PrinterTechnology technology;
std::string family;
std::vector<PrinterVariant> variants;
std::vector<std::string> default_materials;
// Vendor & Printer Model specific print bed model & texture.
std::string bed_model;
std::string bed_texture;
PrinterVariant* variant(const std::string &name) {
for (auto &v : this->variants)
@ -79,6 +83,8 @@ public:
VendorProfile() {}
VendorProfile(std::string id) : id(std::move(id)) {}
bool valid() const { return ! name.empty() && ! id.empty() && config_version.valid(); }
// Load VendorProfile from an ini file.
// If `load_all` is false, only the header with basic info (name, version, URLs) is loaded.
static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true);
@ -91,6 +97,17 @@ public:
bool operator==(const VendorProfile &rhs) const { return this->id == rhs.id; }
};
class Preset;
// Helper to hold a profile with its vendor definition, where the vendor definition may have been extracted from a parent system preset.
// The parent preset is only accessible through PresetCollection, therefore to allow definition of the various is_compatible_with methods
// outside of the PresetCollection, this composite is returned by PresetCollection::get_preset_with_vendor_profile() when needed.
struct PresetWithVendorProfile {
PresetWithVendorProfile(const Preset &preset, const VendorProfile *vendor) : preset(preset), vendor(vendor) {}
const Preset &preset;
const VendorProfile *vendor;
};
// Note: it is imporant that map is used here rather than unordered_map,
// because we need iterators to not be invalidated,
// because Preset and the ConfigWizard hold pointers to VendorProfiles.
@ -150,6 +167,10 @@ public:
// Alias of the preset
std::string alias = "";
// List of profile names, from which this profile was renamed at some point of time.
// This list is then used to match profiles by their names when loaded from .gcode, .3mf, .amf,
// and to match the "inherits" field of user profiles with updated system profiles.
std::vector<std::string> renamed_from;
void save();
@ -161,10 +182,6 @@ public:
void set_dirty(bool dirty = true) { this->is_dirty = dirty; }
void reset_dirty() { this->is_dirty = false; }
bool is_compatible_with_print(const Preset &active_print) const;
bool is_compatible_with_printer(const Preset &active_printer, const DynamicPrintConfig *extra_config) const;
bool is_compatible_with_printer(const Preset &active_printer) const;
// Returns the name of the preset, from which this preset inherits.
static std::string& inherits(DynamicPrintConfig &cfg) { return cfg.option<ConfigOptionString>("inherits", true)->value; }
std::string& inherits() { return Preset::inherits(this->config); }
@ -198,9 +215,6 @@ public:
// This call returns a reference, it may add a new entry into the DynamicPrintConfig.
PrinterTechnology& printer_technology_ref() { return this->config.option<ConfigOptionEnum<PrinterTechnology>>("printer_technology", true)->value; }
// Mark this preset as compatible if it is compatible with active_printer.
bool update_compatible(const Preset &active_printer, const DynamicPrintConfig *extra_config, const Preset *active_print = nullptr);
// Set is_visible according to application config
void set_visible_from_appconfig(const AppConfig &app_config);
@ -233,6 +247,10 @@ protected:
static std::string remove_suffix_modified(const std::string &name);
};
bool is_compatible_with_print (const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_print);
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer, const DynamicPrintConfig *extra_config);
bool is_compatible_with_printer(const PresetWithVendorProfile &preset, const PresetWithVendorProfile &active_printer);
// Collections of presets of the same type (one of the Print, Filament or Printer type).
class PresetCollection
{
@ -323,12 +341,17 @@ public:
// The parent preset may be a system preset or a user preset, which will be
// reflected by the UI.
const Preset* get_selected_preset_parent() const;
// get parent preset for some child preset
// Get parent preset for a child preset, based on the "inherits" field of a child,
// where the "inherits" profile name is searched for in both m_presets and m_map_system_profile_renamed.
const Preset* get_preset_parent(const Preset& child) const;
// Return the selected preset including the user modifications.
Preset& get_edited_preset() { return m_edited_preset; }
const Preset& get_edited_preset() const { return m_edited_preset; }
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile get_preset_with_vendor_profile(const Preset &preset) const;
PresetWithVendorProfile get_edited_preset_with_vendor_profile() const { return this->get_preset_with_vendor_profile(this->get_edited_preset()); }
const std::string& get_preset_name_by_alias(const std::string& alias) const;
// used to update preset_choice from Tab
@ -361,7 +384,8 @@ public:
size_t n = this->m_presets.size();
size_t i_compatible = n;
for (; i < n; ++ i)
if (m_presets[i].is_compatible) {
// Since we use the filament selection from Wizard, it's needed to control the preset visibility too
if (m_presets[i].is_compatible && m_presets[i].is_visible) {
if (prefered_condition(m_presets[i].name))
return i;
if (i_compatible == n)
@ -388,13 +412,13 @@ public:
// For Print / Filament presets, disable those, which are not compatible with the printer.
template<typename PreferedCondition>
void update_compatible(const Preset &active_printer, const Preset *active_print, bool select_other_if_incompatible, PreferedCondition prefered_condition)
void update_compatible(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, bool select_other_if_incompatible, PreferedCondition prefered_condition)
{
if (this->update_compatible_internal(active_printer, active_print, select_other_if_incompatible) == (size_t)-1)
// Find some other compatible preset, or the "-- default --" preset.
this->select_preset(this->first_compatible_idx(prefered_condition));
}
void update_compatible(const Preset &active_printer, const Preset *active_print, bool select_other_if_incompatible)
void update_compatible(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, bool select_other_if_incompatible)
{ this->update_compatible(active_printer, active_print, select_other_if_incompatible, [](const std::string&){return true;}); }
size_t num_visible() const { return std::count_if(m_presets.begin(), m_presets.end(), [](const Preset &preset){return preset.is_visible;}); }
@ -418,7 +442,7 @@ public:
// Update the choice UI from the list of presets.
// Only the compatible presets are shown.
// If an incompatible preset is selected, it is shown as well.
void update_platter_ui(GUI::PresetComboBox *ui);
void update_plater_ui(GUI::PresetComboBox *ui);
// Update a dirty floag of the current preset, update the labels of the UI component accordingly.
// Return true if the dirty flag changed.
@ -451,6 +475,9 @@ protected:
// Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector<std::string> merge_presets(PresetCollection &&other, const VendorMap &new_vendors);
// Update m_map_system_profile_renamed from loaded system profiles.
void update_map_system_profile_renamed();
private:
PresetCollection();
PresetCollection(const PresetCollection &other);
@ -476,8 +503,16 @@ private:
}
std::deque<Preset>::const_iterator find_preset_internal(const std::string &name) const
{ return const_cast<PresetCollection*>(this)->find_preset_internal(name); }
std::deque<Preset>::iterator find_preset_renamed(const std::string &name) {
auto it_renamed = m_map_system_profile_renamed.find(name);
auto it = (it_renamed == m_map_system_profile_renamed.end()) ? m_presets.end() : this->find_preset_internal(it_renamed->second);
assert((it_renamed == m_map_system_profile_renamed.end()) || (it != m_presets.end() && it->name == it_renamed->second));
return it;
}
std::deque<Preset>::const_iterator find_preset_renamed(const std::string &name) const
{ return const_cast<PresetCollection*>(this)->find_preset_renamed(name); }
size_t update_compatible_internal(const Preset &active_printer, const Preset *active_print, bool unselect_if_incompatible);
size_t update_compatible_internal(const PresetWithVendorProfile &active_printer, const PresetWithVendorProfile *active_print, bool unselect_if_incompatible);
static std::vector<std::string> dirty_options(const Preset *edited, const Preset *reference, const bool is_printer_type = false);
@ -487,6 +522,8 @@ private:
// Use deque to force the container to allocate an object per each entry,
// so that the addresses of the presets don't change during resizing of the container.
std::deque<Preset> m_presets;
// Map from old system profile name to a current system profile name.
std::map<std::string, std::string> m_map_system_profile_renamed;
// Initially this preset contains a copy of the selected preset. Later on, this copy may be modified by the user.
Preset m_edited_preset;
// Selected preset.
@ -494,7 +531,7 @@ private:
// Is the "- default -" preset suppressed?
bool m_default_suppressed = true;
size_t m_num_default_presets = 0;
// Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Platter.
// Compatible & incompatible marks, to be placed at the wxBitmapComboBox items of a Plater.
// These bitmaps are not owned by PresetCollection, but by a PresetBundle.
const wxBitmap *m_bitmap_compatible = nullptr;
const wxBitmap *m_bitmap_incompatible = nullptr;
@ -527,6 +564,11 @@ public:
const Preset* find_by_model_id(const std::string &model_id) const;
};
namespace PresetUtils {
// PrinterModel of a system profile, from which this preset is derived, or null if it is not derived from a system profile.
const VendorProfile::PrinterModel* system_printer_model(const Preset &preset);
} // namespace PresetUtils
} // namespace Slic3r
#endif /* slic3r_Preset_hpp_ */

View file

@ -29,6 +29,7 @@
#include "libslic3r/libslic3r.h"
#include "libslic3r/Utils.hpp"
#include "GUI_App.hpp"
// Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir.
// This breaks compatibility with the upstream Slic3r if the --datadir is used to switch between the two versions.
@ -287,6 +288,11 @@ std::string PresetBundle::load_system_presets()
// No config bundle loaded, reset.
this->reset(false);
}
this->prints .update_map_system_profile_renamed();
this->sla_prints .update_map_system_profile_renamed();
this->filaments .update_map_system_profile_renamed();
this->sla_materials.update_map_system_profile_renamed();
this->printers .update_map_system_profile_renamed();
return errors_cummulative;
}
@ -346,55 +352,47 @@ const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& p
void PresetBundle::load_installed_filaments(AppConfig &config)
{
if (! config.has_section(AppConfig::SECTION_FILAMENTS)) {
std::unordered_set<const Preset*> comp_filaments;
for (const Preset &printer : printers) {
if (! printer.is_visible || printer.printer_technology() != ptFFF) {
continue;
}
for (const Preset &filament : filaments) {
if (filament.is_compatible_with_printer(printer)) {
comp_filaments.insert(&filament);
}
}
}
for (const auto &filament: comp_filaments) {
// Compatibility with the PrusaSlicer 2.1.1 and older, where the filament profiles were not installable yet.
// Find all filament profiles, which are compatible with installed printers, and act as if these filament profiles
// were installed.
std::unordered_set<const Preset*> compatible_filaments;
for (const Preset &printer : printers)
if (printer.is_visible && printer.printer_technology() == ptFFF) {
const PresetWithVendorProfile printer_with_vendor_profile = printers.get_preset_with_vendor_profile(printer);
for (const Preset &filament : filaments)
if (is_compatible_with_printer(filaments.get_preset_with_vendor_profile(filament), printer_with_vendor_profile))
compatible_filaments.insert(&filament);
}
// and mark these filaments as installed, therefore this code will not be executed at the next start of the application.
for (const auto &filament: compatible_filaments)
config.set(AppConfig::SECTION_FILAMENTS, filament->name, "1");
}
}
for (auto &preset : filaments) {
for (auto &preset : filaments)
preset.set_visible_from_appconfig(config);
}
}
void PresetBundle::load_installed_sla_materials(AppConfig &config)
{
if (! config.has_section(AppConfig::SECTION_MATERIALS)) {
std::unordered_set<const Preset*> comp_sla_materials;
for (const Preset &printer : printers) {
if (! printer.is_visible || printer.printer_technology() != ptSLA) {
continue;
}
for (const Preset &material : sla_materials) {
if (material.is_compatible_with_printer(printer)) {
comp_sla_materials.insert(&material);
}
}
}
for (const auto &material: comp_sla_materials) {
// Compatibility with the PrusaSlicer 2.1.1 and older, where the SLA material profiles were not installable yet.
// Find all SLA material profiles, which are compatible with installed printers, and act as if these SLA material profiles
// were installed.
for (const Preset &printer : printers)
if (printer.is_visible && printer.printer_technology() == ptSLA) {
const PresetWithVendorProfile printer_with_vendor_profile = printers.get_preset_with_vendor_profile(printer);
for (const Preset &material : sla_materials)
if (is_compatible_with_printer(sla_materials.get_preset_with_vendor_profile(material), printer_with_vendor_profile))
comp_sla_materials.insert(&material);
}
// and mark these SLA materials as installed, therefore this code will not be executed at the next start of the application.
for (const auto &material: comp_sla_materials)
config.set(AppConfig::SECTION_MATERIALS, material->name, "1");
}
}
for (auto &preset : sla_materials) {
for (auto &preset : sla_materials)
preset.set_visible_from_appconfig(config);
}
}
// Load selections (current print, current filaments, current printer) from config.ini
@ -562,9 +560,11 @@ DynamicPrintConfig PresetBundle::full_fff_config() const
while (filament_configs.size() < num_extruders)
filament_configs.emplace_back(&this->filaments.first_visible().config);
for (const DynamicPrintConfig *cfg : filament_configs) {
compatible_printers_condition.emplace_back(Preset::compatible_printers_condition(*const_cast<DynamicPrintConfig*>(cfg)));
compatible_prints_condition .emplace_back(Preset::compatible_prints_condition(*const_cast<DynamicPrintConfig*>(cfg)));
inherits .emplace_back(Preset::inherits(*const_cast<DynamicPrintConfig*>(cfg)));
// The compatible_prints/printers_condition() returns a reference to configuration key, which may not yet exist.
DynamicPrintConfig &cfg_rw = *const_cast<DynamicPrintConfig*>(cfg);
compatible_printers_condition.emplace_back(Preset::compatible_printers_condition(cfg_rw));
compatible_prints_condition .emplace_back(Preset::compatible_prints_condition(cfg_rw));
inherits .emplace_back(Preset::inherits(cfg_rw));
}
// Option values to set a ConfigOptionVector from.
std::vector<const ConfigOption*> filament_opts(num_extruders, nullptr);
@ -876,6 +876,9 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
}
// 4) Load the project config values (the per extruder wipe matrix etc).
this->project_config.apply_only(config, s_project_options);
update_custom_gcode_per_print_z_from_config(GUI::wxGetApp().plater()->model().custom_gcode_per_print_z.gcodes, &this->project_config);
break;
}
case ptSLA:
@ -1136,7 +1139,6 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
PresetCollection *presets = nullptr;
std::vector<std::string> *loaded = nullptr;
std::string preset_name;
std::string alias_name;
if (boost::starts_with(section.first, "print:")) {
presets = &this->prints;
loaded = &loaded_prints;
@ -1145,12 +1147,6 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
presets = &this->filaments;
loaded = &loaded_filaments;
preset_name = section.first.substr(9);
for (const auto& item : section.second)
if (boost::starts_with(item.first, "alias")) {
alias_name = item.second.data();
break;
}
} else if (boost::starts_with(section.first, "sla_print:")) {
presets = &this->sla_prints;
loaded = &loaded_sla_prints;
@ -1159,9 +1155,6 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
presets = &this->sla_materials;
loaded = &loaded_sla_materials;
preset_name = section.first.substr(13);
int end_pos = preset_name.find_first_of("0.");
alias_name = preset_name.substr(0, end_pos-1);
} else if (boost::starts_with(section.first, "printer:")) {
presets = &this->printers;
loaded = &loaded_printers;
@ -1217,19 +1210,32 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
// Load the print, filament or printer preset.
const DynamicPrintConfig *default_config = nullptr;
DynamicPrintConfig config;
std::string alias_name;
std::vector<std::string> renamed_from;
auto parse_config_section = [&section, &alias_name, &renamed_from, &path](DynamicPrintConfig &config) {
for (auto &kvp : section.second) {
if (kvp.first == "alias")
alias_name = kvp.second.data();
else if (kvp.first == "renamed_from") {
if (! unescape_strings_cstyle(kvp.second.data(), renamed_from)) {
BOOST_LOG_TRIVIAL(error) << "Error in a Vendor Config Bundle \"" << path << "\": The preset \"" <<
section.first << "\" contains invalid \"renamed_from\" key, which is being ignored.";
}
}
config.set_deserialize(kvp.first, kvp.second.data());
}
};
if (presets == &this->printers) {
// Select the default config based on the printer_technology field extracted from kvp.
DynamicPrintConfig config_src;
for (auto &kvp : section.second)
config_src.set_deserialize(kvp.first, kvp.second.data());
parse_config_section(config_src);
default_config = &presets->default_preset_for(config_src).config;
config = *default_config;
config.apply(config_src);
} else {
default_config = &presets->default_preset().config;
config = *default_config;
for (auto &kvp : section.second)
config.set_deserialize(kvp.first, kvp.second.data());
parse_config_section(config);
}
Preset::normalize(config);
// Report configuration fields, which are misplaced into a wrong group.
@ -1308,12 +1314,22 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
loaded.vendor = vendor_profile;
}
// next step of an preset name aliasing
int end_pos = preset_name.find_first_of("@");
if (end_pos != std::string::npos)
alias_name = preset_name.substr(0, end_pos - 1);
loaded.alias = alias_name.empty() ? preset_name : alias_name;
// Derive the profile logical name aka alias from the preset name if the alias was not stated explicitely.
if (alias_name.empty()) {
int end_pos = preset_name.find_first_of("@");
if (end_pos != std::string::npos) {
alias_name = preset_name.substr(0, end_pos);
if (renamed_from.empty())
// Add the preset name with the '@' character removed into the "renamed_from" list.
renamed_from.emplace_back(alias_name + preset_name.substr(end_pos + 1));
boost::trim_right(alias_name);
}
}
if (alias_name.empty())
loaded.alias = preset_name;
else
loaded.alias = std::move(alias_name);
loaded.renamed_from = std::move(renamed_from);
++ presets_loaded;
}
@ -1385,23 +1401,24 @@ void PresetBundle::update_multi_material_filament_presets()
void PresetBundle::update_compatible(bool select_other_if_incompatible)
{
const Preset &printer_preset = this->printers.get_edited_preset();
const Preset &printer_preset = this->printers.get_edited_preset();
const PresetWithVendorProfile printer_preset_with_vendor_profile = this->printers.get_preset_with_vendor_profile(printer_preset);
switch (printer_preset.printer_technology()) {
case ptFFF:
{
assert(printer_preset.config.has("default_print_profile"));
assert(printer_preset.config.has("default_filament_profile"));
const Preset &print_preset = this->prints.get_edited_preset();
const std::string &prefered_print_profile = printer_preset.config.opt_string("default_print_profile");
const std::vector<std::string> &prefered_filament_profiles = printer_preset.config.option<ConfigOptionStrings>("default_filament_profile")->values;
prefered_print_profile.empty() ?
this->prints.update_compatible(printer_preset, nullptr, select_other_if_incompatible) :
this->prints.update_compatible(printer_preset, nullptr, select_other_if_incompatible,
this->prints.update_compatible(printer_preset_with_vendor_profile, nullptr, select_other_if_incompatible) :
this->prints.update_compatible(printer_preset_with_vendor_profile, nullptr, select_other_if_incompatible,
[&prefered_print_profile](const std::string& profile_name) { return profile_name == prefered_print_profile; });
const PresetWithVendorProfile print_preset_with_vendor_profile = this->prints.get_edited_preset_with_vendor_profile();
prefered_filament_profiles.empty() ?
this->filaments.update_compatible(printer_preset, &print_preset, select_other_if_incompatible) :
this->filaments.update_compatible(printer_preset, &print_preset, select_other_if_incompatible,
this->filaments.update_compatible(printer_preset_with_vendor_profile, &print_preset_with_vendor_profile, select_other_if_incompatible) :
this->filaments.update_compatible(printer_preset_with_vendor_profile, &print_preset_with_vendor_profile, select_other_if_incompatible,
[&prefered_filament_profiles](const std::string& profile_name)
{ return std::find(prefered_filament_profiles.begin(), prefered_filament_profiles.end(), profile_name) != prefered_filament_profiles.end(); });
if (select_other_if_incompatible) {
@ -1433,16 +1450,16 @@ void PresetBundle::update_compatible(bool select_other_if_incompatible)
{
assert(printer_preset.config.has("default_sla_print_profile"));
assert(printer_preset.config.has("default_sla_material_profile"));
const Preset &sla_print_preset = this->sla_prints.get_edited_preset();
const std::string &prefered_sla_print_profile = printer_preset.config.opt_string("default_sla_print_profile");
const PresetWithVendorProfile sla_print_preset_with_vendor_profile = this->sla_prints.get_edited_preset_with_vendor_profile();
const std::string &prefered_sla_print_profile = printer_preset.config.opt_string("default_sla_print_profile");
(prefered_sla_print_profile.empty()) ?
this->sla_prints.update_compatible(printer_preset, nullptr, select_other_if_incompatible) :
this->sla_prints.update_compatible(printer_preset, nullptr, select_other_if_incompatible,
this->sla_prints.update_compatible(printer_preset_with_vendor_profile, nullptr, select_other_if_incompatible) :
this->sla_prints.update_compatible(printer_preset_with_vendor_profile, nullptr, select_other_if_incompatible,
[&prefered_sla_print_profile](const std::string& profile_name){ return profile_name == prefered_sla_print_profile; });
const std::string &prefered_sla_material_profile = printer_preset.config.opt_string("default_sla_material_profile");
prefered_sla_material_profile.empty() ?
this->sla_materials.update_compatible(printer_preset, &sla_print_preset, select_other_if_incompatible) :
this->sla_materials.update_compatible(printer_preset, &sla_print_preset, select_other_if_incompatible,
this->sla_materials.update_compatible(printer_preset_with_vendor_profile, &sla_print_preset_with_vendor_profile, select_other_if_incompatible) :
this->sla_materials.update_compatible(printer_preset_with_vendor_profile, &sla_print_preset_with_vendor_profile, select_other_if_incompatible,
[&prefered_sla_material_profile](const std::string& profile_name){ return profile_name == prefered_sla_material_profile; });
break;
}
@ -1555,7 +1572,7 @@ void PresetBundle::load_default_preset_bitmaps(wxWindow *window)
this->load_compatible_bitmaps(window);
}
void PresetBundle::update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui)
void PresetBundle::update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui)
{
if (ui == nullptr || this->printers.get_edited_preset().printer_technology() == ptSLA ||
this->filament_presets.size() <= idx_extruder )

View file

@ -109,8 +109,8 @@ public:
// Export a config bundle file containing all the presets and the names of the active presets.
void export_configbundle(const std::string &path, bool export_system_settings = false);
// Update a filament selection combo box on the platter for an idx_extruder.
void update_platter_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui);
// Update a filament selection combo box on the plater for an idx_extruder.
void update_plater_filament_ui(unsigned int idx_extruder, GUI::PresetComboBox *ui);
// Enable / disable the "- default -" preset.
void set_default_suppressed(bool default_suppressed);

View file

@ -0,0 +1,605 @@
#include "RemovableDriveManager.hpp"
#include <iostream>
#include "boost/nowide/convert.hpp"
#if _WIN32
#include <windows.h>
#include <tchar.h>
#include <winioctl.h>
#include <shlwapi.h>
#include <Dbt.h>
GUID WceusbshGUID = { 0x25dbce51, 0x6c8f, 0x4a72,
0x8a,0x6d,0xb5,0x4c,0x2b,0x4f,0xc8,0x35 };
#else
//linux includes
#include <errno.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <glob.h>
#include <pwd.h>
#include <boost/filesystem.hpp>
#include <boost/filesystem/convenience.hpp>
#endif
namespace Slic3r {
namespace GUI {
#if _WIN32
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
void RemovableDriveManager::search_for_drives()
{
m_current_drives.clear();
//get logical drives flags by letter in alphabetical order
DWORD drives_mask = GetLogicalDrives();
for (size_t i = 0; i < 26; i++)
{
if(drives_mask & (1 << i))
{
std::string path (1,(char)('A' + i));
path+=":";
UINT drive_type = GetDriveTypeA(path.c_str());
// DRIVE_REMOVABLE on W are sd cards and usb thumbnails (not usb harddrives)
if (drive_type == DRIVE_REMOVABLE)
{
// get name of drive
std::wstring wpath = boost::nowide::widen(path);//std::wstring(path.begin(), path.end());
std::wstring volume_name;
volume_name.resize(1024);
std::wstring file_system_name;
file_system_name.resize(1024);
LPWSTR lp_volume_name_buffer = new wchar_t;
BOOL error = GetVolumeInformationW(wpath.c_str(), &volume_name[0], sizeof(volume_name), NULL, NULL, NULL, &file_system_name[0], sizeof(file_system_name));
if(error != 0)
{
volume_name.erase(std::find(volume_name.begin(), volume_name.end(), '\0'), volume_name.end());
/*
if (volume_name == L"")
{
volume_name = L"REMOVABLE DRIVE";
}
*/
if (file_system_name != L"")
{
ULARGE_INTEGER free_space;
GetDiskFreeSpaceExA(path.c_str(), &free_space, NULL, NULL);
if (free_space.QuadPart > 0)
{
path += "\\";
m_current_drives.push_back(DriveData(boost::nowide::narrow(volume_name), path));
}
}
}
}
}
}
}
void RemovableDriveManager::eject_drive(const std::string &path)
{
if(m_current_drives.empty())
return;
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
if ((*it).path == path)
{
// get handle to device
std::string mpath = "\\\\.\\" + path;
mpath = mpath.substr(0, mpath.size() - 1);
HANDLE handle = CreateFileA(mpath.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
{
std::cerr << "Ejecting " << mpath << " failed " << GetLastError() << " \n";
return;
}
DWORD deviceControlRetVal(0);
//these 3 commands should eject device safely but they dont, the device does disappear from file explorer but the "device was safely remove" notification doesnt trigger.
//sd cards does trigger WM_DEVICECHANGE messege, usb drives dont
DeviceIoControl(handle, FSCTL_LOCK_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
DeviceIoControl(handle, FSCTL_DISMOUNT_VOLUME, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
// some implemenatations also calls IOCTL_STORAGE_MEDIA_REMOVAL here but it returns error to me
BOOL error = DeviceIoControl(handle, IOCTL_STORAGE_EJECT_MEDIA, nullptr, 0, nullptr, 0, &deviceControlRetVal, nullptr);
if (error == 0)
{
CloseHandle(handle);
std::cerr << "Ejecting " << mpath << " failed " << deviceControlRetVal << " " << GetLastError() << " \n";
return;
}
CloseHandle(handle);
m_did_eject = true;
m_current_drives.erase(it);
m_ejected_path = m_last_save_path;
m_ejected_name = m_last_save_name;
break;
}
}
}
bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
{
if (m_current_drives.empty())
return false;
std::size_t found = path.find_last_of("\\");
std::string new_path = path.substr(0, found);
int letter = PathGetDriveNumberA(new_path.c_str());
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
char drive = (*it).path[0];
if (drive == ('A' + letter))
return true;
}
return false;
}
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
{
std::size_t found = path.find_last_of("\\");
std::string new_path = path.substr(0, found);
int letter = PathGetDriveNumberA(new_path.c_str());
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
char drive = (*it).path[0];
if (drive == ('A' + letter))
return (*it).path;
}
return "";
}
void RemovableDriveManager::register_window()
{
//creates new unvisible window that is recieving callbacks from system
// structure to register
WNDCLASSEX wndClass;
wndClass.cbSize = sizeof(WNDCLASSEX);
wndClass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
wndClass.hInstance = reinterpret_cast<HINSTANCE>(GetModuleHandle(0));
wndClass.lpfnWndProc = reinterpret_cast<WNDPROC>(WinProcCallback);//this is callback
wndClass.cbClsExtra = 0;
wndClass.cbWndExtra = 0;
wndClass.hIcon = LoadIcon(0, IDI_APPLICATION);
wndClass.hbrBackground = CreateSolidBrush(RGB(192, 192, 192));
wndClass.hCursor = LoadCursor(0, IDC_ARROW);
wndClass.lpszClassName = L"PrusaSlicer_aux_class";
wndClass.lpszMenuName = NULL;
wndClass.hIconSm = wndClass.hIcon;
if(!RegisterClassEx(&wndClass))
{
DWORD err = GetLastError();
return;
}
HWND hWnd = CreateWindowEx(
WS_EX_NOACTIVATE,
L"PrusaSlicer_aux_class",
L"PrusaSlicer_aux_wnd",
WS_DISABLED, // style
CW_USEDEFAULT, 0,
640, 480,
NULL, NULL,
GetModuleHandle(NULL),
NULL);
if(hWnd == NULL)
{
DWORD err = GetLastError();
}
//ShowWindow(hWnd, SW_SHOWNORMAL);
UpdateWindow(hWnd);
}
INT_PTR WINAPI WinProcCallback(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// here we need to catch messeges about device removal
// problem is that when ejecting usb (how is it implemented above) there is no messege dispached. Only after physical removal of the device.
//uncomment register_window() in init() to register and comment update() in GUI_App.cpp (only for windows!) to stop recieving periodical updates
LRESULT lRet = 1;
static HDEVNOTIFY hDeviceNotify;
switch (message)
{
case WM_CREATE:
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory(&NotificationFilter, sizeof(NotificationFilter));
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = WceusbshGUID;
hDeviceNotify = RegisterDeviceNotification(hWnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
break;
case WM_DEVICECHANGE:
{
// here is the important
if(wParam == DBT_DEVICEREMOVECOMPLETE)
{
- RemovableDriveManager::get_instance().update(0, true);
}
}
break;
default:
// Send all other messages on to the default windows handler.
lRet = DefWindowProc(hWnd, message, wParam, lParam);
break;
}
return lRet;
}
#else
void RemovableDriveManager::search_for_drives()
{
m_current_drives.clear();
#if __APPLE__
// if on macos obj-c class will enumerate
if(m_rdmmm)
{
m_rdmmm->list_devices();
}
#else
//search /media/* folder
search_path("/media/*", "/media");
//search_path("/Volumes/*", "/Volumes");
std::string path(std::getenv("USER"));
std::string pp(path);
{
//search /media/USERNAME/* folder
pp = "/media/"+pp;
path = "/media/" + path + "/*";
search_path(path, pp);
//search /run/media/USERNAME/* folder
path = "/run" + path;
pp = "/run"+pp;
search_path(path, pp);
}
#endif
}
void RemovableDriveManager::search_path(const std::string &path,const std::string &parent_path)
{
glob_t globbuf;
globbuf.gl_offs = 2;
int error = glob(path.c_str(), GLOB_TILDE, NULL, &globbuf);
if(error == 0)
{
for(size_t i = 0; i < globbuf.gl_pathc; i++)
{
inspect_file(globbuf.gl_pathv[i], parent_path);
}
}else
{
//if error - path probably doesnt exists so function just exits
//std::cout<<"glob error "<< error<< "\n";
}
globfree(&globbuf);
}
void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path)
{
//confirms if the file is removable drive and adds it to vector
//if not same file system - could be removable drive
if(!compare_filesystem_id(path, parent_path))
{
//free space
boost::filesystem::space_info si = boost::filesystem::space(path);
if(si.available != 0)
{
//user id
struct stat buf;
stat(path.c_str(), &buf);
uid_t uid = buf.st_uid;
std::string username(std::getenv("USER"));
struct passwd *pw = getpwuid(uid);
if (pw != 0 && pw->pw_name == username)
m_current_drives.push_back(DriveData(boost::filesystem::basename(boost::filesystem::path(path)), path));
}
}
}
bool RemovableDriveManager::compare_filesystem_id(const std::string &path_a, const std::string &path_b)
{
struct stat buf;
stat(path_a.c_str() ,&buf);
dev_t id_a = buf.st_dev;
stat(path_b.c_str() ,&buf);
dev_t id_b = buf.st_dev;
return id_a == id_b;
}
void RemovableDriveManager::eject_drive(const std::string &path)
{
if (m_current_drives.empty())
return;
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
if((*it).path == path)
{
std::string correct_path(path);
for (size_t i = 0; i < correct_path.size(); ++i)
{
if(correct_path[i]==' ')
{
correct_path = correct_path.insert(i,1,'\\');
i++;
}
}
//std::cout<<"Ejecting "<<(*it).name<<" from "<< correct_path<<"\n";
// there is no usable command in c++ so terminal command is used instead
// but neither triggers "succesful safe removal messege"
std::string command = "";
#if __APPLE__
//m_rdmmm->eject_device(path);
command = "diskutil unmount ";
#else
command = "umount ";
#endif
command += correct_path;
int err = system(command.c_str());
if(err)
{
std::cerr<<"Ejecting failed\n";
return;
}
m_did_eject = true;
m_current_drives.erase(it);
m_ejected_path = m_last_save_path;
m_ejected_name = m_last_save_name;
break;
}
}
}
bool RemovableDriveManager::is_path_on_removable_drive(const std::string &path)
{
if (m_current_drives.empty())
return false;
std::size_t found = path.find_last_of("/");
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
if(compare_filesystem_id(new_path, (*it).path))
return true;
}
return false;
}
std::string RemovableDriveManager::get_drive_from_path(const std::string& path)
{
std::size_t found = path.find_last_of("/");
std::string new_path = found == path.size() - 1 ? path.substr(0, found) : path;
//check if same filesystem
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
if (compare_filesystem_id(new_path, (*it).path))
return (*it).path;
}
return "";
}
#endif
RemovableDriveManager::RemovableDriveManager():
m_drives_count(0),
m_last_update(0),
m_last_save_path(""),
m_last_save_name(""),
m_last_save_path_verified(false),
m_is_writing(false),
m_did_eject(false),
m_plater_ready_to_slice(true),
m_ejected_path(""),
m_ejected_name("")
#if __APPLE__
, m_rdmmm(new RDMMMWrapper())
#endif
{}
RemovableDriveManager::~RemovableDriveManager()
{
#if __APPLE__
delete m_rdmmm;
#endif
}
void RemovableDriveManager::init()
{
//add_callback([](void) { RemovableDriveManager::get_instance().print(); });
#if _WIN32
//register_window();
#elif __APPLE__
m_rdmmm->register_window();
#endif
update(0, true);
}
bool RemovableDriveManager::update(const long time,const bool check)
{
if(time != 0) //time = 0 is forced update
{
long diff = m_last_update - time;
if(diff <= -2)
{
m_last_update = time;
}else
{
return false; // return value shouldnt matter if update didnt run
}
}
search_for_drives();
if (m_drives_count != m_current_drives.size())
{
if (check)
{
check_and_notify();
}
m_drives_count = m_current_drives.size();
}
return !m_current_drives.empty();
}
bool RemovableDriveManager::is_drive_mounted(const std::string &path)
{
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
if ((*it).path == path)
{
return true;
}
}
return false;
}
std::string RemovableDriveManager::get_drive_path()
{
if (m_current_drives.size() == 0)
{
reset_last_save_path();
return "";
}
if (m_last_save_path_verified)
return m_last_save_path;
return m_current_drives.back().path;
}
std::string RemovableDriveManager::get_last_save_path()
{
if (!m_last_save_path_verified)
return "";
return m_last_save_path;
}
std::string RemovableDriveManager::get_last_save_name()
{
return m_last_save_name;
}
std::vector<DriveData> RemovableDriveManager::get_all_drives()
{
return m_current_drives;
}
void RemovableDriveManager::check_and_notify()
{
if(m_drive_count_changed_callback)
{
m_drive_count_changed_callback(m_plater_ready_to_slice);
}
if(m_callbacks.size() != 0 && m_drives_count > m_current_drives.size() /*&& m_last_save_path_verified */&& !is_drive_mounted(m_last_save_path))
{
for (auto it = m_callbacks.begin(); it != m_callbacks.end(); ++it)
{
(*it)();
}
}
}
void RemovableDriveManager::add_remove_callback(std::function<void()> callback)
{
m_callbacks.push_back(callback);
}
void RemovableDriveManager::erase_callbacks()
{
m_callbacks.clear();
}
void RemovableDriveManager::set_drive_count_changed_callback(std::function<void(const bool)> callback)
{
m_drive_count_changed_callback = callback;
}
void RemovableDriveManager::set_plater_ready_to_slice(bool b)
{
m_plater_ready_to_slice = b;
}
void RemovableDriveManager::set_last_save_path(const std::string& path)
{
if(m_last_save_path_verified)// if old path is on drive
{
if(get_drive_from_path(path) != "") //and new is too, rewrite the path
{
m_last_save_path_verified = false;
m_last_save_path = path;
}//else do nothing
}else
{
m_last_save_path = path;
}
}
void RemovableDriveManager::verify_last_save_path()
{
std::string last_drive = get_drive_from_path(m_last_save_path);
if (last_drive != "")
{
m_last_save_path_verified = true;
m_last_save_path = last_drive;
m_last_save_name = get_drive_name(last_drive);
}else
{
reset_last_save_path();
}
}
std::string RemovableDriveManager::get_drive_name(const std::string& path)
{
if (m_current_drives.size() == 0)
return "";
for (auto it = m_current_drives.begin(); it != m_current_drives.end(); ++it)
{
if ((*it).path == path)
{
return (*it).name;
}
}
return "";
}
bool RemovableDriveManager::is_last_drive_removed()
{
if(!m_last_save_path_verified)
{
return true;
}
bool r = !is_drive_mounted(m_last_save_path);
if (r)
{
reset_last_save_path();
}
return r;
}
bool RemovableDriveManager::is_last_drive_removed_with_update(const long time)
{
update(time, false);
return is_last_drive_removed();
}
void RemovableDriveManager::reset_last_save_path()
{
m_last_save_path_verified = false;
m_last_save_path = "";
m_last_save_name = "";
}
void RemovableDriveManager::set_is_writing(const bool b)
{
m_is_writing = b;
if (b)
{
m_did_eject = false;
}
}
bool RemovableDriveManager::get_is_writing()
{
return m_is_writing;
}
bool RemovableDriveManager::get_did_eject()
{
return m_did_eject;
}
void RemovableDriveManager::set_did_eject(const bool b)
{
m_did_eject = b;
}
size_t RemovableDriveManager::get_drives_count()
{
return m_current_drives.size();
}
std::string RemovableDriveManager::get_ejected_path()
{
return m_ejected_path;
}
std::string RemovableDriveManager::get_ejected_name()
{
return m_ejected_name;
}
}}//namespace Slicer::Gui

View file

@ -0,0 +1,121 @@
#ifndef slic3r_GUI_RemovableDriveManager_hpp_
#define slic3r_GUI_RemovableDriveManager_hpp_
#include <vector>
#include <string>
#include <functional>
namespace Slic3r {
namespace GUI {
#if __APPLE__
class RDMMMWrapper;
#endif
struct DriveData
{
std::string name;
std::string path;
DriveData(std::string n, std::string p):name(n),path(p){}
};
class RemovableDriveManager
{
#if __APPLE__
friend class RDMMMWrapper;
#endif
public:
static RemovableDriveManager& get_instance()
{
static RemovableDriveManager instance;
return instance;
}
RemovableDriveManager(RemovableDriveManager const&) = delete;
void operator=(RemovableDriveManager const&) = delete;
~RemovableDriveManager();
//call only once. on apple register for unmnount callbacks. on windows register for device notification is prepared but not called (eject usb drive on widnows doesnt trigger the callback, sdc ard does), also enumerates devices for first time so init shoud be called on linux too.
void init();
//update() searches for removable devices, returns false if empty. /time = 0 is forced update, time expects wxGetLocalTime()
bool update(const long time = 0,const bool check = false);
bool is_drive_mounted(const std::string &path);
void eject_drive(const std::string &path);
//returns path to last drive which was used, if none was used, returns device that was enumerated last
std::string get_last_save_path();
std::string get_last_save_name();
//returns path to last drive which was used, if none was used, returns empty string
std::string get_drive_path();
std::vector<DriveData> get_all_drives();
bool is_path_on_removable_drive(const std::string &path);
// callback will notify only if device with last save path was removed
void add_remove_callback(std::function<void()> callback);
// erases all remove callbacks added by add_remove_callback()
void erase_callbacks();
//drive_count_changed callback is called on every added or removed device
void set_drive_count_changed_callback(std::function<void(const bool)> callback);
//thi serves to set correct value for drive_count_changed callback
void set_plater_ready_to_slice(bool b);
// marks one of the eveices in vector as last used
void set_last_save_path(const std::string &path);
void verify_last_save_path();
bool is_last_drive_removed();
// param as update()
bool is_last_drive_removed_with_update(const long time = 0);
void set_is_writing(const bool b);
bool get_is_writing();
bool get_did_eject();
void set_did_eject(const bool b);
std::string get_drive_name(const std::string& path);
size_t get_drives_count();
std::string get_ejected_path();
std::string get_ejected_name();
private:
RemovableDriveManager();
void search_for_drives();
//triggers callbacks if last used drive was removed
void check_and_notify();
//returns drive path (same as path in DriveData) if exists otherwise empty string ""
std::string get_drive_from_path(const std::string& path);
void reset_last_save_path();
std::vector<DriveData> m_current_drives;
std::vector<std::function<void()>> m_callbacks;
std::function<void(const bool)> m_drive_count_changed_callback;
size_t m_drives_count;
long m_last_update;
std::string m_last_save_path;
bool m_last_save_path_verified;
std::string m_last_save_name;
bool m_is_writing;//on device
bool m_did_eject;
bool m_plater_ready_to_slice;
std::string m_ejected_path;
std::string m_ejected_name;
#if _WIN32
//registers for notifications by creating invisible window
void register_window();
#else
#if __APPLE__
RDMMMWrapper * m_rdmmm;
#endif
void search_path(const std::string &path, const std::string &parent_path);
bool compare_filesystem_id(const std::string &path_a, const std::string &path_b);
void inspect_file(const std::string &path, const std::string &parent_path);
#endif
};
// apple wrapper for RemovableDriveManagerMM which searches for drives and/or ejects them
#if __APPLE__
class RDMMMWrapper
{
public:
RDMMMWrapper();
~RDMMMWrapper();
void register_window();
void list_devices();
void eject_device(const std::string &path);
void log(const std::string &msg);
protected:
void *m_imp;
//friend void RemovableDriveManager::inspect_file(const std::string &path, const std::string &parent_path);
};
#endif
}}
#endif

View file

@ -0,0 +1,10 @@
#import <Cocoa/Cocoa.h>
@interface RemovableDriveManagerMM : NSObject
-(instancetype) init;
-(void) add_unmount_observer;
-(void) on_device_unmount: (NSNotification*) notification;
-(NSArray*) list_dev;
-(void)eject_drive:(NSString *)path;
@end

View file

@ -0,0 +1,157 @@
#import "RemovableDriveManager.hpp"
#import "RemovableDriveManagerMM.h"
#import <AppKit/AppKit.h>
#import <DiskArbitration/DiskArbitration.h>
@implementation RemovableDriveManagerMM
-(instancetype) init
{
self = [super init];
if(self)
{
}
return self;
}
-(void) on_device_unmount: (NSNotification*) notification
{
NSLog(@"on device change");
Slic3r::GUI::RemovableDriveManager::get_instance().update(0,true);
}
-(void) add_unmount_observer
{
NSLog(@"add unmount observer");
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidUnmountNotification object:nil];
[[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector: @selector(on_device_unmount:) name:NSWorkspaceDidMountNotification object:nil];
}
-(NSArray*) list_dev
{
// DEPRICATED:
//NSArray* devices = [[NSWorkspace sharedWorkspace] mountedRemovableMedia];
//return devices;
NSArray *mountedRemovableMedia = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes];
NSMutableArray *result = [NSMutableArray array];
for(NSURL *volURL in mountedRemovableMedia)
{
int err = 0;
DADiskRef disk;
DASessionRef session;
CFDictionaryRef descDict;
session = DASessionCreate(NULL);
if (session == NULL) {
err = EINVAL;
}
if (err == 0) {
disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)volURL);
if (session == NULL) {
err = EINVAL;
}
}
if (err == 0) {
descDict = DADiskCopyDescription(disk);
if (descDict == NULL) {
err = EINVAL;
}
}
if (err == 0) {
CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
BOOL ejectable = [mediaEjectableKey boolValue];
CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
CFTypeRef deviceModelKey = CFDictionaryGetValue(descDict, kDADiskDescriptionDeviceModelKey);
if (mediaEjectableKey != NULL)
{
BOOL op = ejectable && (CFEqual(deviceProtocolName, CFSTR("USB")) || CFEqual(deviceModelKey, CFSTR("SD Card Reader")));
//!CFEqual(deviceModelKey, CFSTR("Disk Image"));
//
if (op) {
[result addObject:volURL.path];
}
}
}
if (descDict != NULL) {
CFRelease(descDict);
}
}
return result;
}
-(void)eject_drive:(NSString *)path
{
DADiskRef disk;
DASessionRef session;
NSURL *url = [[NSURL alloc] initFileURLWithPath:path];
int err = 0;
session = DASessionCreate(NULL);
if (session == NULL) {
err = EINVAL;
}
if (err == 0) {
disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)url);
}
if( err == 0)
{
DADiskUnmount(disk, kDADiskUnmountOptionDefault,
NULL, NULL);
}
if (disk != NULL) {
CFRelease(disk);
}
if (session != NULL) {
CFRelease(session);
}
}
namespace Slic3r {
namespace GUI {
RDMMMWrapper::RDMMMWrapper():m_imp(nullptr){
m_imp = [[RemovableDriveManagerMM alloc] init];
}
RDMMMWrapper::~RDMMMWrapper()
{
if(m_imp)
{
[m_imp release];
}
}
void RDMMMWrapper::register_window()
{
if(m_imp)
{
[m_imp add_unmount_observer];
}
}
void RDMMMWrapper::list_devices()
{
if(m_imp)
{
NSArray* devices = [m_imp list_dev];
for (NSString* volumePath in devices)
{
NSLog(@"%@", volumePath);
Slic3r::GUI::RemovableDriveManager::get_instance().inspect_file(std::string([volumePath UTF8String]), "/Volumes");
}
}
}
void RDMMMWrapper::log(const std::string &msg)
{
NSLog(@"%s", msg.c_str());
}
void RDMMMWrapper::eject_device(const std::string &path)
{
if(m_imp)
{
NSString * pth = [NSString stringWithCString:path.c_str()
encoding:[NSString defaultCStringEncoding]];
[m_imp eject_drive:pth];
}
}
}}//namespace Slicer::GUI
/*
*/
@end

View file

@ -2004,7 +2004,11 @@ void Selection::render_sidebar_layers_hints(const std::string& sidebar_field) co
const float max_y = box.max(1) + Margin;
// view dependend order of rendering to keep correct transparency
#if ENABLE_6DOF_CAMERA
bool camera_on_top = wxGetApp().plater()->get_camera().is_looking_downward();
#else
bool camera_on_top = wxGetApp().plater()->get_camera().get_theta() <= 90.0f;
#endif // ENABLE_6DOF_CAMERA
float z1 = camera_on_top ? min_z : max_z;
float z2 = camera_on_top ? max_z : min_z;

View file

@ -905,7 +905,7 @@ void Tab::update_wiping_button_visibility() {
}
// Call a callback to update the selection of presets on the platter:
// Call a callback to update the selection of presets on the plater:
// To update the content of the selection boxes,
// to update the filament colors of the selection boxes,
// to update the "dirty" flags of the selection boxes,
@ -2755,7 +2755,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current)
PrinterTechnology printer_technology = m_preset_bundle->printers.get_edited_preset().printer_technology();
PresetCollection &dependent = (printer_technology == ptFFF) ? m_preset_bundle->filaments : m_preset_bundle->sla_materials;
bool old_preset_dirty = dependent.current_is_dirty();
bool new_preset_compatible = dependent.get_edited_preset().is_compatible_with_print(*m_presets->find_preset(preset_name, true));
bool new_preset_compatible = is_compatible_with_print(dependent.get_edited_preset_with_vendor_profile(), m_presets->get_preset_with_vendor_profile(*m_presets->find_preset(preset_name, true)));
if (! canceled)
canceled = old_preset_dirty && ! new_preset_compatible && ! may_discard_current_dirty_preset(&dependent, preset_name);
if (! canceled) {
@ -2773,6 +2773,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current)
// With the introduction of the SLA printer types, we need to support switching between
// the FFF and SLA printers.
const Preset &new_printer_preset = *m_presets->find_preset(preset_name, true);
const PresetWithVendorProfile new_printer_preset_with_vendor_profile = m_presets->get_preset_with_vendor_profile(new_printer_preset);
PrinterTechnology old_printer_technology = m_presets->get_edited_preset().printer_technology();
PrinterTechnology new_printer_technology = new_printer_preset.printer_technology();
if (new_printer_technology == ptSLA && old_printer_technology == ptFFF && !may_switch_to_SLA_preset())
@ -2793,7 +2794,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current)
};
for (PresetUpdate &pu : updates) {
pu.old_preset_dirty = (old_printer_technology == pu.technology) && pu.presets->current_is_dirty();
pu.new_preset_compatible = (new_printer_technology == pu.technology) && pu.presets->get_edited_preset().is_compatible_with_printer(new_printer_preset);
pu.new_preset_compatible = (new_printer_technology == pu.technology) && is_compatible_with_printer(pu.presets->get_edited_preset_with_vendor_profile(), new_printer_preset_with_vendor_profile);
if (!canceled)
canceled = pu.old_preset_dirty && !pu.new_preset_compatible && !may_discard_current_dirty_preset(pu.presets, preset_name);
}
@ -2824,7 +2825,7 @@ void Tab::select_preset(std::string preset_name, bool delete_current)
if (canceled) {
update_tab_ui();
// Trigger the on_presets_changed event so that we also restore the previous value in the plater selector,
// if this action was initiated from the platter.
// if this action was initiated from the plater.
on_presets_changed();
} else {
if (current_dirty)
@ -2929,7 +2930,13 @@ void Tab::OnTreeSelChange(wxTreeEvent& event)
#ifdef __linux__
std::unique_ptr<wxWindowUpdateLocker> no_updates(new wxWindowUpdateLocker(this));
#else
// wxWindowUpdateLocker noUpdates(this);
/* On Windows we use DoubleBuffering during rendering,
* so on Window is no needed to call a Freeze/Thaw functions.
* But under OSX (builds compiled with MacOSX10.14.sdk) wxStaticBitmap rendering is broken without Freeze/Thaw call.
*/
#ifdef __WXOSX__
wxWindowUpdateLocker noUpdates(this);
#endif
#endif
if (m_pages.empty())
@ -3046,7 +3053,7 @@ void Tab::save_preset(std::string name /*= ""*/)
m_preset_bundle->update_compatible(false);
// Add the new item into the UI component, remove dirty flags and activate the saved item.
update_tab_ui();
// Update the selection boxes at the platter.
// Update the selection boxes at the plater.
on_presets_changed();
// If current profile is saved, "delete preset" button have to be enabled
m_btn_delete_preset->Enable(true);
@ -3437,27 +3444,25 @@ void TabSLAMaterial::build()
DynamicPrintConfig new_conf = *m_config;
if (opt_key == "bottle_volume") {
double new_bottle_weight = boost::any_cast<double>(value)/(new_conf.option("material_density")->getFloat() * 1000);
double new_bottle_weight = boost::any_cast<double>(value)*(new_conf.option("material_density")->getFloat() / 1000);
new_conf.set_key_value("bottle_weight", new ConfigOptionFloat(new_bottle_weight));
}
if (opt_key == "bottle_weight") {
double new_bottle_volume = boost::any_cast<double>(value)*(new_conf.option("material_density")->getFloat() * 1000);
double new_bottle_volume = boost::any_cast<double>(value)/new_conf.option("material_density")->getFloat() * 1000;
new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume));
}
if (opt_key == "material_density") {
double new_bottle_volume = new_conf.option("bottle_weight")->getFloat() * boost::any_cast<double>(value) * 1000;
double new_bottle_volume = new_conf.option("bottle_weight")->getFloat() / boost::any_cast<double>(value) * 1000;
new_conf.set_key_value("bottle_volume", new ConfigOptionFloat(new_bottle_volume));
}
load_config(new_conf);
update_dirty();
on_value_change(opt_key, value);
if (opt_key == "bottle_volume" || opt_key == "bottle_cost") {
wxGetApp().sidebar().update_sliced_info_sizer();
wxGetApp().sidebar().Layout();
}
// Change of any from those options influences for an update of "Sliced Info"
wxGetApp().sidebar().update_sliced_info_sizer();
wxGetApp().sidebar().Layout();
};
optgroup = page->new_optgroup(_(L("Layers")));

View file

@ -231,7 +231,7 @@ public:
// Counter for the updating (because of an update() function can have a recursive behavior):
// 1. increase value from the very beginning of an update() function
// 2. decrease value at the end of an update() function
// 3. propagate changed configuration to the Platter when (m_update_cnt == 0) only
// 3. propagate changed configuration to the Plater when (m_update_cnt == 0) only
int m_update_cnt = 0;
public:

File diff suppressed because it is too large Load diff

View file

@ -17,6 +17,7 @@
#include <set>
#include <functional>
#include "libslic3r/Model.hpp"
#include "libslic3r/GCodeWriter.hpp"
namespace Slic3r {
enum class ModelVolumeType : int;
@ -729,6 +730,9 @@ public:
wxBitmap& bmp() { return m_bmp; }
const std::string& name() const{ return m_icon_name; }
int px_cnt()const {return m_px_cnt;}
bool is_horizontal()const {return m_is_horizontal;}
private:
wxWindow* m_parent{ nullptr };
wxBitmap m_bmp = wxBitmap();
@ -778,6 +782,8 @@ public:
const wxString& name = wxEmptyString);
~DoubleSlider() {}
using t_mode = Slic3r::Model::CustomGCodeInfo::MODE;
/* For exporting GCode in GCodeWriter is used XYZF_NUM(val) = PRECISION(val, 3) for XYZ values.
* So, let use same value as a permissible error for layer height.
*/
@ -801,33 +807,24 @@ public:
// Set low and high slider position. If the span is non-empty, disable the "one layer" mode.
void SetSelectionSpan(const int lower_val, const int higher_val);
void SetMaxValue(const int max_value);
void SetKoefForLabels(const double koef) {
m_label_koef = koef;
}
void SetSliderValues(const std::vector<double>& values) {
m_values = values;
}
void SetKoefForLabels(const double koef) { m_label_koef = koef; }
void SetSliderValues(const std::vector<double>& values) { m_values = values; }
void ChangeOneLayerLock();
std::vector<Slic3r::Model::CustomGCode> GetTicksValues() const;
void SetTicksValues(const std::vector<Slic3r::Model::CustomGCode> &heights);
void EnableTickManipulation(bool enable = true) {
m_is_enabled_tick_manipulation = enable;
}
void DisableTickManipulation() {
EnableTickManipulation(false);
}
Slic3r::Model::CustomGCodeInfo GetTicksValues() const;
void SetTicksValues(const Slic3r::Model::CustomGCodeInfo &custom_gcode_per_print_z);
void EnableTickManipulation(bool enable = true) { m_is_enabled_tick_manipulation = enable; }
void DisableTickManipulation() { EnableTickManipulation(false); }
enum ManipulationState {
msSingleExtruder, // single extruder printer preset is selected
msMultiExtruder // multiple extruder printer preset is selected, and "Whole print" is selected
};
void SetManipulationState(ManipulationState state) {
m_state = state;
void SetManipulationMode(t_mode mode) { m_mode = mode; }
t_mode GetManipulationMode() const { return m_mode; }
void SetModeAndOnlyExtruder(const bool is_one_extruder_printed_model, const int only_extruder)
{
m_mode = !is_one_extruder_printed_model ? t_mode::MultiExtruder :
only_extruder < 0 ? t_mode::SingleExtruder :
t_mode::MultiAsSingle;
m_only_extruder = only_extruder;
}
void SetManipulationState(int extruders_cnt) {
m_state = extruders_cnt ==1 ? msSingleExtruder : msMultiExtruder;
}
ManipulationState GetManipulationState() const { return m_state; }
bool is_horizontal() const { return m_style == wxSL_HORIZONTAL; }
bool is_one_layer() const { return m_is_one_layer; }
@ -846,13 +843,27 @@ public:
void OnKeyUp(wxKeyEvent &event);
void OnChar(wxKeyEvent &event);
void OnRightDown(wxMouseEvent& event);
int get_extruder_for_tick(int tick);
void OnRightUp(wxMouseEvent& event);
void add_code(std::string code, int selected_extruder = -1);
void add_code_as_tick(std::string code, int selected_extruder = -1);
// add default action for tick, when press "+"
void add_current_tick(bool call_from_keyboard = false);
// delete current tick, when press "-"
void delete_current_tick();
void edit_tick();
void change_extruder(int extruder);
void edit_extruder_sequence();
struct TICK_CODE
{
bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; }
bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; }
int tick = 0;
std::string gcode = Slic3r::ColorChangeCode;
int extruder = 0;
std::string color;
};
protected:
void render();
@ -874,11 +885,11 @@ protected:
void detect_selected_slider(const wxPoint& pt);
void correct_lower_value();
void correct_higher_value();
wxString get_tooltip(IconFocus icon_focus);
void move_current_thumb(const bool condition);
void action_tick(const TicksAction action);
void enter_window(wxMouseEvent& event, const bool enter);
private:
bool is_point_in_rect(const wxPoint& pt, const wxRect& rect);
int is_point_near_tick(const wxPoint& pt);
@ -890,8 +901,18 @@ protected:
wxSize get_size();
void get_size(int *w, int *h);
double get_double_value(const SelectedSlider& selection);
wxString get_tooltip(IconFocus icon_focus);
std::string get_color_for_tool_change_tick(std::set<TICK_CODE>::const_iterator it) const;
std::string get_color_for_color_change_tick(std::set<TICK_CODE>::const_iterator it) const;
int get_extruder_for_tick(int tick);
std::set<int> get_used_extruders_for_tick(int tick);
void post_ticks_changed_event(const std::string& gcode = "");
bool check_ticks_changed_event(const std::string& gcode);
void append_change_extruder_menu_item(wxMenu*);
void append_add_color_change_menu_item(wxMenu*);
private:
bool is_osx { false };
wxFont m_font;
int m_min_value;
@ -910,7 +931,7 @@ private:
ScalableBitmap m_bmp_one_layer_unlock_off;
ScalableBitmap m_bmp_revert;
ScalableBitmap m_bmp_cog;
SelectedSlider m_selection;
SelectedSlider m_selection;
bool m_is_left_down = false;
bool m_is_right_down = false;
bool m_is_one_layer = false;
@ -920,11 +941,12 @@ private:
bool m_is_enabled_tick_manipulation = true;
bool m_show_context_menu = false;
bool m_show_edit_menu = false;
bool m_edit_extruder_sequence = false;
bool m_suppress_add_code = false;
ManipulationState m_state = msSingleExtruder;
std::string m_custom_gcode = "";
std::string m_pause_print_msg;
bool m_force_edit_extruder_sequence = false;
bool m_force_mode_apply = true;
bool m_force_add_tick = false;
bool m_force_delete_tick = false;
t_mode m_mode = t_mode::SingleExtruder;
int m_only_extruder = -1;
wxRect m_rect_lower_thumb;
wxRect m_rect_higher_thumb;
@ -953,62 +975,44 @@ private:
std::vector<wxPen*> m_line_pens;
std::vector<wxPen*> m_segm_pens;
std::set<int> m_ticks;
std::vector<double> m_values;
struct TICK_CODE
struct TICK_CODE_INFO
{
TICK_CODE(int tick):tick(tick), gcode(Slic3r::ColorChangeCode), extruder(0), color("") {}
TICK_CODE(int tick, const std::string& code) :
tick(tick), gcode(code), extruder(0) {}
TICK_CODE(int tick, int extruder) :
tick(tick), gcode(Slic3r::ColorChangeCode), extruder(extruder) {}
TICK_CODE(int tick, const std::string& code, int extruder, const std::string& color) :
tick(tick), gcode(code), extruder(extruder), color(color) {}
std::set<TICK_CODE> ticks;
t_mode mode = t_mode::SingleExtruder;
bool operator<(const TICK_CODE& other) const { return other.tick > this->tick; }
bool operator>(const TICK_CODE& other) const { return other.tick < this->tick; }
TICK_CODE operator=(const TICK_CODE& other) const {
TICK_CODE ret_val(other.tick, other.gcode, other.extruder, other.color);
return ret_val;
}
bool empty() const { return ticks.empty(); }
void set_pause_print_msg(const std::string& message) { pause_print_msg = message; }
int tick;
std::string gcode;
int extruder;
std::string color;
};
bool add_tick (const int tick, std::string &code, int extruder, double print_z);
bool edit_tick (std::set<TICK_CODE>::iterator it, double print_z);
void switch_code(const std::string& code_from, const std::string& code_to);
void erase_all_ticks_with_code (const std::string& gcode);
bool has_tick_with_code (const std::string& gcode);
std::set<TICK_CODE> m_ticks_;
void suppress_plus (bool suppress) { m_suppress_plus = suppress;}
void suppress_minus(bool suppress) { m_suppress_minus = suppress;}
bool suppressed_plus () { return m_suppress_plus ; }
bool suppressed_minus() { return m_suppress_minus; }
private:
std::string custom_gcode = "";
std::string pause_print_msg = "";
bool m_suppress_plus = false;
bool m_suppress_minus = false;
}
m_ticks;
public:
struct ExtrudersSequence
{
bool is_mm_intervals;
double interval_by_mm;
int interval_by_layers;
std::vector<size_t> extruders;
bool is_mm_intervals = true;
double interval_by_mm = 3.0;
int interval_by_layers = 10;
std::vector<size_t> extruders = { 0 };
ExtrudersSequence() :
is_mm_intervals(true),
interval_by_mm(3.0),
interval_by_layers(10),
extruders({ 0 }) {}
ExtrudersSequence(const ExtrudersSequence& other) :
is_mm_intervals(other.is_mm_intervals),
interval_by_mm(other.interval_by_mm),
interval_by_layers(other.interval_by_layers),
extruders(other.extruders) {}
ExtrudersSequence& operator=(const ExtrudersSequence& other) {
this->is_mm_intervals = other.is_mm_intervals;
this->interval_by_mm = other.interval_by_mm;
this->interval_by_layers= other.interval_by_layers;
this->extruders = other.extruders;
return *this;
}
bool operator==(const ExtrudersSequence& other) const
{
return (other.is_mm_intervals == this->is_mm_intervals ) &&
@ -1107,6 +1111,7 @@ public:
void SetBitmap_(const ScalableBitmap& bmp);
void SetBitmapDisabled_(const ScalableBitmap &bmp);
int GetBitmapHeight();
void msw_rescale();
@ -1116,6 +1121,10 @@ private:
std::string m_disabled_icon_name = "";
int m_width {-1}; // should be multiplied to em_unit
int m_height{-1}; // should be multiplied to em_unit
// bitmap dimensions
int m_px_cnt{ 16 };
bool m_is_horizontal{ false };
};