mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-22 22:24:01 -06:00
Merge branch 'master' into upstream2
This commit is contained in:
commit
749a06a092
253 changed files with 61495 additions and 5223 deletions
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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); }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ;
|
||||
|
||||
|
|
|
@ -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) )
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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](){
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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_
|
||||
|
||||
|
|
255
src/slic3r/GUI/Mouse3DHandlerMac.mm
Normal file
255
src/slic3r/GUI/Mouse3DHandlerMac.mm
Normal 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
|
|
@ -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(); }
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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 = [§ion, &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 )
|
||||
|
|
|
@ -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);
|
||||
|
|
605
src/slic3r/GUI/RemovableDriveManager.cpp
Normal file
605
src/slic3r/GUI/RemovableDriveManager.cpp
Normal 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
|
121
src/slic3r/GUI/RemovableDriveManager.hpp
Normal file
121
src/slic3r/GUI/RemovableDriveManager.hpp
Normal 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
|
||||
|
10
src/slic3r/GUI/RemovableDriveManagerMM.h
Normal file
10
src/slic3r/GUI/RemovableDriveManagerMM.h
Normal 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
|
157
src/slic3r/GUI/RemovableDriveManagerMM.mm
Normal file
157
src/slic3r/GUI/RemovableDriveManagerMM.mm
Normal 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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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")));
|
||||
|
|
|
@ -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
|
@ -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 };
|
||||
};
|
||||
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue