Fixed conflicts after merge with master

This commit is contained in:
enricoturri1966 2021-09-01 13:28:10 +02:00
commit 639cf17e19
67 changed files with 1800 additions and 687 deletions

View file

@ -119,7 +119,7 @@ void Bed3D::Axes::render() const
glsafe(::glEnable(GL_DEPTH_TEST));
shader->start_using();
shader->set_uniform("emission_factor", 0.0);
shader->set_uniform("emission_factor", 0.0f);
// x axis
const_cast<GLModel*>(&m_arrow)->set_color(-1, { 0.75f, 0.0f, 0.0f, 1.0f });
@ -498,7 +498,7 @@ void Bed3D::render_model() const
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.0);
shader->set_uniform("emission_factor", 0.0f);
glsafe(::glPushMatrix());
glsafe(::glTranslated(m_model_offset.x(), m_model_offset.y(), m_model_offset.z()));
model->render();

View file

@ -281,7 +281,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field(el, have_skirt);
bool have_brim = config->opt_enum<BrimType>("brim_type") != btNoBrim;
for (auto el : { "brim_width", "brim_offset" })
for (auto el : { "brim_width", "brim_separation" })
toggle_field(el, have_brim);
// perimeter_extruder uses the same logic as in Print::extruders()
toggle_field("perimeter_extruder", have_perimeters || have_brim);
@ -312,7 +312,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig* config)
toggle_field("support_material_speed", have_support_material || have_brim || have_skirt);
toggle_field("raft_contact_distance", have_raft && !have_support_soluble);
toggle_field("raft_expansion", have_raft);
for (auto el : { "raft_expansion", "first_layer_acceleration_over_raft", "first_layer_speed_over_raft" })
toggle_field(el, have_raft);
bool has_ironing = config->opt_bool("ironing");
for (auto el : { "ironing_type", "ironing_flowrate", "ironing_spacing", "ironing_speed" })

View file

@ -22,6 +22,95 @@ namespace Slic3r {
namespace GUI {
namespace {
// escaping of path string according to
// https://cgit.freedesktop.org/xdg/xdg-specs/tree/desktop-entry/desktop-entry-spec.xml
std::string escape_string(const std::string& str)
{
// The buffer needs to be bigger if escaping <,>,&
std::vector<char> out(str.size() * 4, 0);
char *outptr = out.data();
for (size_t i = 0; i < str.size(); ++ i) {
char c = str[i];
// must be escaped
if (c == '\"') { //double quote
(*outptr ++) = '\\';
(*outptr ++) = '\"';
} else if (c == '`') { // backtick character
(*outptr ++) = '\\';
(*outptr ++) = '`';
} else if (c == '$') { // dollar sign
(*outptr ++) = '\\';
(*outptr ++) = '$';
} else if (c == '\\') { // backslash character
(*outptr ++) = '\\';
(*outptr ++) = '\\';
(*outptr ++) = '\\';
(*outptr ++) = '\\';
// Reserved characters
// At Ubuntu, all these characters must NOT be escaped for desktop integration to work
/*
} else if (c == ' ') { // space
(*outptr ++) = '\\';
(*outptr ++) = ' ';
} else if (c == '\t') { // tab
(*outptr ++) = '\\';
(*outptr ++) = '\t';
} else if (c == '\n') { // newline
(*outptr ++) = '\\';
(*outptr ++) = '\n';
} else if (c == '\'') { // single quote
(*outptr ++) = '\\';
(*outptr ++) = '\'';
} else if (c == '>') { // greater-than sign
(*outptr ++) = '\\';
(*outptr ++) = '&';
(*outptr ++) = 'g';
(*outptr ++) = 't';
(*outptr ++) = ';';
} else if (c == '<') { //less-than sign
(*outptr ++) = '\\';
(*outptr ++) = '&';
(*outptr ++) = 'l';
(*outptr ++) = 't';
(*outptr ++) = ';';
} else if (c == '~') { // tilde
(*outptr ++) = '\\';
(*outptr ++) = '~';
} else if (c == '|') { // vertical bar
(*outptr ++) = '\\';
(*outptr ++) = '|';
} else if (c == '&') { // ampersand
(*outptr ++) = '\\';
(*outptr ++) = '&';
(*outptr ++) = 'a';
(*outptr ++) = 'm';
(*outptr ++) = 'p';
(*outptr ++) = ';';
} else if (c == ';') { // semicolon
(*outptr ++) = '\\';
(*outptr ++) = ';';
} else if (c == '*') { //asterisk
(*outptr ++) = '\\';
(*outptr ++) = '*';
} else if (c == '?') { // question mark
(*outptr ++) = '\\';
(*outptr ++) = '?';
} else if (c == '#') { // hash mark
(*outptr ++) = '\\';
(*outptr ++) = '#';
} else if (c == '(') { // parenthesis
(*outptr ++) = '\\';
(*outptr ++) = '(';
} else if (c == ')') {
(*outptr ++) = '\\';
(*outptr ++) = ')';
*/
} else
(*outptr ++) = c;
}
return std::string(out.data(), outptr - out.data());
}
// Disects path strings stored in system variable divided by ':' and adds into vector
void resolve_path_from_var(const std::string& var, std::vector<std::string>& paths)
{
@ -157,7 +246,8 @@ void DesktopIntegrationDialog::perform_desktop_integration()
}
// Escape ' characters in appimage, other special symbols will be esacaped in desktop file by 'excutable_path'
boost::replace_all(excutable_path, "'", "'\\''");
//boost::replace_all(excutable_path, "'", "'\\''");
excutable_path = escape_string(excutable_path);
// Find directories icons and applications
// $XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored.
@ -243,14 +333,14 @@ void DesktopIntegrationDialog::perform_desktop_integration()
"Name=PrusaSlicer%1%\n"
"GenericName=3D Printing Software\n"
"Icon=PrusaSlicer%2%\n"
"Exec=\'%3%\' %%F\n"
"Exec=\"%3%\" %%F\n"
"Terminal=false\n"
"Type=Application\n"
"MimeType=model/stl;application/vnd.ms-3mfdocument;application/prs.wavefront-obj;application/x-amf;\n"
"Categories=Graphics;3DGraphics;Engineering;\n"
"Keywords=3D;Printing;Slicer;slice;3D;printer;convert;gcode;stl;obj;amf;SLA\n"
"StartupNotify=false\n"
"StartupWMClass=prusa-slicer", name_suffix, version_suffix, excutable_path);
"StartupWMClass=prusa-slicer\n", name_suffix, version_suffix, excutable_path);
std::string path = GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix);
if (create_desktop_file(path, desktop_file)){
@ -292,40 +382,44 @@ void DesktopIntegrationDialog::perform_desktop_integration()
app_config->set("desktop_integration_app_path", GUI::format("%1%/applications/PrusaSlicer%2%.desktop", target_dir_desktop, version_suffix));
// Repeat for Gcode viewer - use same paths as for slicer files
// Icon
if (!target_dir_icons.empty())
{
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
if (copy_icon(icon_path, dest_path))
// save path to icon
app_config->set("desktop_integration_icon_viewer_path", dest_path);
else
BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed.";
}
// Do NOT add gcode viewer desktop file on ChromeOS
if (platform_flavor() != PlatformFlavor::LinuxOnChromium) {
// Icon
if (!target_dir_icons.empty())
{
std::string icon_path = GUI::format("%1%/icons/PrusaSlicer-gcodeviewer_192px.png",resources_dir());
std::string dest_path = GUI::format("%1%/icons/%2%PrusaSlicer-gcodeviewer%3%.png", target_dir_icons, icon_theme_path, version_suffix);
if (copy_icon(icon_path, dest_path))
// save path to icon
app_config->set("desktop_integration_icon_viewer_path", dest_path);
else
BOOST_LOG_TRIVIAL(error) << "Copying Gcode Viewer icon to icons directory failed.";
}
// Desktop file
std::string desktop_file = GUI::format(
"[Desktop Entry]\n"
"Name=Prusa Gcode Viewer%1%\n"
"GenericName=3D Printing Software\n"
"Icon=PrusaSlicer-gcodeviewer%2%\n"
"Exec=\'%3%\' --gcodeviwer %%F\n"
"Terminal=false\n"
"Type=Application\n"
"MimeType=text/x.gcode;\n"
"Categories=Graphics;3DGraphics;\n"
"Keywords=3D;Printing;Slicer;\n"
"StartupNotify=false", name_suffix, version_suffix, excutable_path);
// Desktop file
std::string desktop_file = GUI::format(
"[Desktop Entry]\n"
"Name=Prusa Gcode Viewer%1%\n"
"GenericName=3D Printing Software\n"
"Icon=PrusaSlicer-gcodeviewer%2%\n"
"Exec=\"%3%\" --gcodeviewer %%F\n"
"Terminal=false\n"
"Type=Application\n"
"MimeType=text/x.gcode;\n"
"Categories=Graphics;3DGraphics;\n"
"Keywords=3D;Printing;Slicer;\n"
"StartupNotify=false\n", name_suffix, version_suffix, excutable_path);
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
if (create_desktop_file(desktop_path, desktop_file))
// save path to desktop file
app_config->set("desktop_integration_app_viewer_path", desktop_path);
else {
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
std::string desktop_path = GUI::format("%1%/applications/PrusaSlicerGcodeViewer%2%.desktop", target_dir_desktop, version_suffix);
if (create_desktop_file(desktop_path, desktop_file))
// save path to desktop file
app_config->set("desktop_integration_app_viewer_path", desktop_path);
else {
BOOST_LOG_TRIVIAL(error) << "Performing desktop integration failed - could not create Gcodeviewer desktop file";
show_error(nullptr, _L("Performing desktop integration failed - could not create Gcodeviewer desktop file. PrusaSlicer desktop file was probably created successfully."));
}
}
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::DesktopIntegrationSuccess);
}
void DesktopIntegrationDialog::undo_desktop_intgration()
@ -343,17 +437,20 @@ void DesktopIntegrationDialog::undo_desktop_intgration()
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
std::remove(path.c_str());
}
// gcode viwer .desktop
path = std::string(app_config->get("desktop_integration_app_viewer_path"));
if (!path.empty()) {
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
std::remove(path.c_str());
}
// gcode viewer icon
path = std::string(app_config->get("desktop_integration_icon_viewer_path"));
if (!path.empty()) {
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
std::remove(path.c_str());
// No gcode viewer at ChromeOS
if (platform_flavor() != PlatformFlavor::LinuxOnChromium) {
// gcode viewer .desktop
path = std::string(app_config->get("desktop_integration_app_viewer_path"));
if (!path.empty()) {
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
std::remove(path.c_str());
}
// gcode viewer icon
path = std::string(app_config->get("desktop_integration_icon_viewer_path"));
if (!path.empty()) {
BOOST_LOG_TRIVIAL(debug) << "removing " << path;
std::remove(path.c_str());
}
}
wxGetApp().plater()->get_notification_manager()->push_notification(NotificationType::UndoDesktopIntegrationSuccess);
}

File diff suppressed because it is too large Load diff

View file

@ -22,17 +22,22 @@ namespace GUI {
class GCodeViewer
{
using IBufferType = unsigned short;
using Color = std::array<float, 3>;
using Color = std::array<float, 4>;
using VertexBuffer = std::vector<float>;
using MultiVertexBuffer = std::vector<VertexBuffer>;
using IndexBuffer = std::vector<IBufferType>;
using MultiIndexBuffer = std::vector<IndexBuffer>;
#if ENABLE_SEAMS_USING_MODELS
using InstanceBuffer = std::vector<float>;
using InstanceIdBuffer = std::vector<size_t>;
#endif // ENABLE_SEAMS_USING_MODELS
static const std::vector<Color> Extrusion_Role_Colors;
static const std::vector<Color> Options_Colors;
static const std::vector<Color> Travel_Colors;
static const Color Wipe_Color;
static const std::vector<Color> Range_Colors;
static const Color Wipe_Color;
static const Color Neutral_Color;
enum class EOptionsColors : unsigned char
{
@ -80,7 +85,10 @@ class GCodeViewer
size_t position_size_floats() const { return 3; }
size_t position_size_bytes() const { return position_size_floats() * sizeof(float); }
size_t normal_offset_floats() const { return position_size_floats(); }
size_t normal_offset_floats() const {
assert(format == EFormat::PositionNormal1 || format == EFormat::PositionNormal3);
return position_size_floats();
}
size_t normal_offset_bytes() const { return normal_offset_floats() * sizeof(float); }
size_t normal_size_floats() const {
@ -96,6 +104,47 @@ class GCodeViewer
void reset();
};
#if ENABLE_SEAMS_USING_MODELS
// buffer containing instances data used to render a toolpaths using instanced models
// instance record format: 5 floats -> position.x|position.y|position.z|width|height
// which is sent to the shader as -> vec3 (offset) + vec2 (scales) in GLModel::render_instanced()
struct InstanceVBuffer
{
// ranges used to render only subparts of the intances
struct Ranges
{
struct Range
{
// offset in bytes of the 1st instance to render
unsigned int offset;
// count of instances to render
unsigned int count;
// vbo id
unsigned int vbo{ 0 };
// Color to apply to the instances
Color color;
};
std::vector<Range> ranges;
void reset();
};
// cpu-side buffer containing all instances data
InstanceBuffer buffer;
// indices of the moves for all instances
std::vector<size_t> s_ids;
Ranges render_ranges;
size_t data_size_bytes() const { return s_ids.size() * instance_size_bytes(); }
size_t instance_size_floats() const { return 5; }
size_t instance_size_bytes() const { return instance_size_floats() * sizeof(float); }
void reset();
};
#endif // ENABLE_SEAMS_USING_MODELS
// ibo buffer containing indices data (for lines/triangles) used to render a specific toolpath type
struct IBuffer
{
@ -229,13 +278,34 @@ class GCodeViewer
{
Point,
Line,
#if ENABLE_SEAMS_USING_MODELS
Triangle,
Model
#else
Triangle
#endif // ENABLE_SEAMS_USING_MODELS
};
ERenderPrimitiveType render_primitive_type;
// buffers for point, line and triangle primitive types
VBuffer vertices;
std::vector<IBuffer> indices;
#if ENABLE_SEAMS_USING_MODELS
struct Model
{
GLModel model;
Color color;
InstanceVBuffer instances;
void reset();
};
// contain the buffer for model primitive types
Model model;
#endif // ENABLE_SEAMS_USING_MODELS
std::string shader;
std::vector<Path> paths;
// std::set seems to perform significantly better, at least on Windows.
@ -283,9 +353,24 @@ class GCodeViewer
}
size_t max_indices_per_segment_size_bytes() const { return max_indices_per_segment() * sizeof(IBufferType); }
#if ENABLE_SEAMS_USING_MODELS
bool has_data() const {
switch (render_primitive_type)
{
case ERenderPrimitiveType::Point:
case ERenderPrimitiveType::Line:
case ERenderPrimitiveType::Triangle: {
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
}
case ERenderPrimitiveType::Model: { return model.model.is_initialized() && !model.instances.buffer.empty(); }
default: { return false; }
}
}
#else
bool has_data() const {
return !vertices.vbos.empty() && vertices.vbos.front() != 0 && !indices.empty() && indices.front().ibo != 0;
}
#endif // ENABLE_SEAMS_USING_MODELS
};
// helper to render shells
@ -433,18 +518,30 @@ class GCodeViewer
int64_t gl_multi_lines_calls_count{ 0 };
int64_t gl_multi_triangles_calls_count{ 0 };
int64_t gl_triangles_calls_count{ 0 };
#if ENABLE_SEAMS_USING_MODELS
int64_t gl_instanced_models_calls_count{ 0 };
#endif // ENABLE_SEAMS_USING_MODELS
// memory
int64_t results_size{ 0 };
int64_t total_vertices_gpu_size{ 0 };
int64_t total_indices_gpu_size{ 0 };
#if ENABLE_SEAMS_USING_MODELS
int64_t total_instances_gpu_size{ 0 };
#endif // ENABLE_SEAMS_USING_MODELS
int64_t max_vbuffer_gpu_size{ 0 };
int64_t max_ibuffer_gpu_size{ 0 };
int64_t paths_size{ 0 };
int64_t render_paths_size{ 0 };
#if ENABLE_SEAMS_USING_MODELS
int64_t models_instances_size{ 0 };
#endif // ENABLE_SEAMS_USING_MODELS
// other
int64_t travel_segments_count{ 0 };
int64_t wipe_segments_count{ 0 };
int64_t extrude_segments_count{ 0 };
#if ENABLE_SEAMS_USING_MODELS
int64_t instances_count{ 0 };
#endif // ENABLE_SEAMS_USING_MODELS
int64_t vbuffers_count{ 0 };
int64_t ibuffers_count{ 0 };
@ -470,22 +567,34 @@ class GCodeViewer
gl_multi_lines_calls_count = 0;
gl_multi_triangles_calls_count = 0;
gl_triangles_calls_count = 0;
#if ENABLE_SEAMS_USING_MODELS
gl_instanced_models_calls_count = 0;
#endif // ENABLE_SEAMS_USING_MODELS
}
void reset_sizes() {
results_size = 0;
total_vertices_gpu_size = 0;
total_indices_gpu_size = 0;
#if ENABLE_SEAMS_USING_MODELS
total_instances_gpu_size = 0;
#endif // ENABLE_SEAMS_USING_MODELS
max_vbuffer_gpu_size = 0;
max_ibuffer_gpu_size = 0;
paths_size = 0;
render_paths_size = 0;
#if ENABLE_SEAMS_USING_MODELS
models_instances_size = 0;
#endif // ENABLE_SEAMS_USING_MODELS
}
void reset_others() {
travel_segments_count = 0;
wipe_segments_count = 0;
extrude_segments_count = 0;
#if ENABLE_SEAMS_USING_MODELS
instances_count = 0;
#endif // ENABLE_SEAMS_USING_MODELS
vbuffers_count = 0;
ibuffers_count = 0;
}
@ -563,6 +672,9 @@ public:
Endpoints endpoints;
Endpoints current;
Endpoints last_current;
#if ENABLE_SEAMS_USING_MODELS
Endpoints global;
#endif // ENABLE_SEAMS_USING_MODELS
Vec3f current_position{ Vec3f::Zero() };
Marker marker;
GCodeWindow gcode_window;
@ -632,7 +744,7 @@ public:
void update_shells_color_by_extruder(const DynamicPrintConfig* config);
void reset();
void render() const;
void render();
bool has_data() const { return !m_roles.empty(); }
bool can_export_toolpaths() const;
@ -678,17 +790,18 @@ private:
void load_toolpaths(const GCodeProcessor::Result& gcode_result);
void load_shells(const Print& print, bool initialized);
void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const;
void render_toolpaths() const;
void render_shells() const;
void render_legend(float& legend_height) const;
void render_toolpaths();
void render_shells();
void render_legend(float& legend_height);
#if ENABLE_GCODE_VIEWER_STATISTICS
void render_statistics() const;
void render_statistics();
#endif // ENABLE_GCODE_VIEWER_STATISTICS
bool is_visible(ExtrusionRole role) const {
return role < erCount && (m_extrusions.role_visibility_flags & (1 << role)) != 0;
}
bool is_visible(const Path& path) const { return is_visible(path.role); }
void log_memory_used(const std::string& label, int64_t additional = 0) const;
Color option_color(EMoveType move_type) const;
};
} // namespace GUI

View file

@ -4159,7 +4159,7 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const
glsafe(::glEnable(GL_DEPTH_TEST));
shader->start_using();
shader->set_uniform("emission_factor", 0.0);
shader->set_uniform("emission_factor", 0.0f);
for (GLVolume* vol : visible_volumes) {
shader->set_uniform("uniform_color", (vol->printable && !vol->is_outside) ? orange : gray);
@ -5181,7 +5181,7 @@ void GLCanvas3D::_render_objects()
m_camera_clipping_plane = ClippingPlane::ClipsNothing();
}
void GLCanvas3D::_render_gcode() const
void GLCanvas3D::_render_gcode()
{
m_gcode_viewer.render();
}

View file

@ -904,7 +904,7 @@ private:
#else
void _render_objects();
#endif // ENABLE_DELAYED_TRANSPARENT_VOLUMES_RENDERING
void _render_gcode() const;
void _render_gcode();
void _render_selection() const;
void _render_sequential_clearance();
#if ENABLE_RENDER_SELECTION_CENTER

View file

@ -208,6 +208,85 @@ void GLModel::render() const
}
}
#if ENABLE_SEAMS_USING_MODELS
void GLModel::render_instanced(unsigned int instances_vbo, unsigned int instances_count) const
{
if (instances_vbo == 0)
return;
GLShaderProgram* shader = wxGetApp().get_current_shader();
assert(shader == nullptr || boost::algorithm::iends_with(shader->get_name(), "_instanced"));
// vertex attributes
GLint position_id = (shader != nullptr) ? shader->get_attrib_location("v_position") : -1;
GLint normal_id = (shader != nullptr) ? shader->get_attrib_location("v_normal") : -1;
assert(position_id != -1 && normal_id != -1);
// instance attributes
GLint offset_id = (shader != nullptr) ? shader->get_attrib_location("i_offset") : -1;
GLint scales_id = (shader != nullptr) ? shader->get_attrib_location("i_scales") : -1;
assert(offset_id != -1 && scales_id != -1);
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, instances_vbo));
if (offset_id != -1) {
glsafe(::glVertexAttribPointer(offset_id, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)0));
glsafe(::glEnableVertexAttribArray(offset_id));
glsafe(::glVertexAttribDivisor(offset_id, 1));
}
if (scales_id != -1) {
glsafe(::glVertexAttribPointer(scales_id, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (GLvoid*)(3 * sizeof(float))));
glsafe(::glEnableVertexAttribArray(scales_id));
glsafe(::glVertexAttribDivisor(scales_id, 1));
}
for (const RenderData& data : m_render_data) {
if (data.vbo_id == 0 || data.ibo_id == 0)
continue;
GLenum mode;
switch (data.type)
{
default:
case PrimitiveType::Triangles: { mode = GL_TRIANGLES; break; }
case PrimitiveType::Lines: { mode = GL_LINES; break; }
case PrimitiveType::LineStrip: { mode = GL_LINE_STRIP; break; }
case PrimitiveType::LineLoop: { mode = GL_LINE_LOOP; break; }
}
if (shader != nullptr)
shader->set_uniform("uniform_color", data.color);
else
glsafe(::glColor4fv(data.color.data()));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, data.vbo_id));
if (position_id != -1) {
glsafe(::glVertexAttribPointer(position_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)0));
glsafe(::glEnableVertexAttribArray(position_id));
}
if (normal_id != -1) {
glsafe(::glVertexAttribPointer(normal_id, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (GLvoid*)(3 * sizeof(float))));
glsafe(::glEnableVertexAttribArray(normal_id));
}
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, data.ibo_id));
glsafe(::glDrawElementsInstanced(mode, static_cast<GLsizei>(data.indices_count), GL_UNSIGNED_INT, (const void*)0, instances_count));
glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
if (normal_id != -1)
glsafe(::glDisableVertexAttribArray(normal_id));
if (position_id != -1)
glsafe(::glDisableVertexAttribArray(position_id));
}
if (scales_id != -1)
glsafe(::glDisableVertexAttribArray(scales_id));
if (offset_id != -1)
glsafe(::glDisableVertexAttribArray(offset_id));
glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0));
}
#endif // ENABLE_SEAMS_USING_MODELS
void GLModel::send_to_gpu(RenderData& data, const std::vector<float>& vertices, const std::vector<unsigned int>& indices)
{
assert(data.vbo_id == 0);
@ -598,5 +677,53 @@ GLModel::InitializationData straight_arrow(float tip_width, float tip_height, fl
return data;
}
GLModel::InitializationData diamond(int resolution)
{
resolution = std::max(4, resolution);
GLModel::InitializationData data;
GLModel::InitializationData::Entity entity;
entity.type = GLModel::PrimitiveType::Triangles;
const float step = 2.0f * float(PI) / float(resolution);
// positions
for (int i = 0; i < resolution; ++i) {
float ii = float(i) * step;
entity.positions.emplace_back(0.5f * ::cos(ii), 0.5f * ::sin(ii), 0.0f);
}
entity.positions.emplace_back(0.0f, 0.0f, 0.5f);
entity.positions.emplace_back(0.0f, 0.0f, -0.5f);
// normals
for (const Vec3f& v : entity.positions) {
entity.normals.emplace_back(v.normalized());
}
// triangles
// top
for (int i = 0; i < resolution; ++i) {
entity.indices.push_back(i + 0);
entity.indices.push_back(i + 1);
entity.indices.push_back(resolution);
}
entity.indices.push_back(resolution - 1);
entity.indices.push_back(0);
entity.indices.push_back(resolution);
// bottom
for (int i = 0; i < resolution; ++i) {
entity.indices.push_back(i + 0);
entity.indices.push_back(resolution + 1);
entity.indices.push_back(i + 1);
}
entity.indices.push_back(resolution - 1);
entity.indices.push_back(resolution + 1);
entity.indices.push_back(0);
data.entities.emplace_back(entity);
return data;
}
} // namespace GUI
} // namespace Slic3r

View file

@ -72,6 +72,9 @@ namespace GUI {
void reset();
void render() const;
#if ENABLE_SEAMS_USING_MODELS
void render_instanced(unsigned int instances_vbo, unsigned int instances_count) const;
#endif // ENABLE_SEAMS_USING_MODELS
bool is_initialized() const { return !m_render_data.empty(); }
@ -100,6 +103,11 @@ namespace GUI {
// used to render sidebar hints for position and scale
GLModel::InitializationData straight_arrow(float tip_width, float tip_height, float stem_width, float stem_height, float thickness);
// create a diamond with the given resolution
// the origin of the diamond is in its center
// the diamond is contained into a box with size [1, 1, 1]
GLModel::InitializationData diamond(int resolution);
} // namespace GUI
} // namespace Slic3r

View file

@ -38,9 +38,17 @@ std::pair<bool, std::string> GLShadersManager::init()
// used to render printbed
valid &= append_shader("printbed", { "printbed.vs", "printbed.fs" });
// used to render options in gcode preview
valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" });
if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20))
valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" });
#if ENABLE_SEAMS_USING_MODELS
if (GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 3))
valid &= append_shader("gouraud_light_instanced", { "gouraud_light_instanced.vs", "gouraud_light_instanced.fs" });
else {
#endif // ENABLE_SEAMS_USING_MODELS
valid &= append_shader("options_110", { "options_110.vs", "options_110.fs" });
if (GUI::wxGetApp().is_glsl_version_greater_or_equal_to(1, 20))
valid &= append_shader("options_120", { "options_120.vs", "options_120.fs" });
#if ENABLE_SEAMS_USING_MODELS
}
#endif // ENABLE_SEAMS_USING_MODELS
// used to render extrusion and travel paths as lines in gcode preview
valid &= append_shader("toolpaths_lines", { "toolpaths_lines.vs", "toolpaths_lines.fs" });
// used to render objects in 3d editor

View file

@ -1049,7 +1049,7 @@ void ObjectList::key_event(wxKeyEvent& event)
|| event.GetKeyCode() == WXK_BACK
#endif //__WXOSX__
) {
wxGetApp().plater()->remove_selected();
remove();
}
else if (event.GetKeyCode() == WXK_F5)
wxGetApp().plater()->reload_all_from_disk();
@ -1702,8 +1702,7 @@ void ObjectList::load_shape_object_from_gallery(const wxArrayString& input_files
snapshot_label += ", " + wxString::FromUTF8(paths[i].filename().string().c_str());
take_snapshot(snapshot_label);
std::vector<size_t> res = wxGetApp().plater()->load_files(paths, true, false);
if (!res.empty())
if (! wxGetApp().plater()->load_files(paths, true, false).empty())
wxGetApp().mainframe->update_title();
}
@ -1803,21 +1802,21 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type)
cnv->get_gizmos_manager().reset_all_states();
Plater::TakeSnapshot(plater, _L("Remove paint-on supports"));
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
mv->supported_facets.clear();
mv->supported_facets.reset();
break;
case InfoItemType::CustomSeam:
cnv->get_gizmos_manager().reset_all_states();
Plater::TakeSnapshot(plater, _L("Remove paint-on seam"));
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
mv->seam_facets.clear();
mv->seam_facets.reset();
break;
case InfoItemType::MmuSegmentation:
cnv->get_gizmos_manager().reset_all_states();
Plater::TakeSnapshot(plater, _L("Remove Multi Material painting"));
for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes)
mv->mmu_segmentation_facets.clear();
mv->mmu_segmentation_facets.reset();
break;
case InfoItemType::Sinking:
@ -1856,7 +1855,7 @@ void ObjectList::del_settings_from_config(const wxDataViewItem& parent_item)
if (is_layer_settings)
layer_height = m_config->opt_float("layer_height");
m_config->clear();
m_config->reset();
if (extruder >= 0)
m_config->set_key_value("extruder", new ConfigOptionInt(extruder));
@ -1932,7 +1931,7 @@ bool ObjectList::del_subobject_from_object(const int obj_idx, const int idx, con
const auto last_volume = object->volumes[0];
if (!last_volume->config.empty()) {
object->config.apply(last_volume->config);
last_volume->config.clear();
last_volume->config.reset();
// update extruder color in ObjectList
wxDataViewItem obj_item = m_objects_model->GetItemById(obj_idx);
@ -2637,7 +2636,7 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed)
model_object->config.has("extruder") ? model_object->config.extruder() : 0,
get_mesh_errors_count(obj_idx) > 0);
update_info_items(obj_idx, nullptr, true);
update_info_items(obj_idx, nullptr, call_selection_changed);
// add volumes to the object
if (model_object->volumes.size() > 1) {
@ -3769,7 +3768,7 @@ void ObjectList::last_volume_is_deleted(const int obj_idx)
auto volume = (*m_objects)[obj_idx]->volumes.front();
// clear volume's config values
volume->config.clear();
volume->config.reset();
// set a default extruder value, since user can't add it manually
volume->config.set_key_value("extruder", new ConfigOptionInt(0));

View file

@ -191,7 +191,7 @@ void GLGizmoBase::render_grabbers(float size) const
if (shader == nullptr)
return;
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
for (int i = 0; i < (int)m_grabbers.size(); ++i) {
if (m_grabbers[i].enabled)
m_grabbers[i].render(m_hover_id == i, size);

View file

@ -136,7 +136,7 @@ void GLGizmoCut::on_render()
if (shader == nullptr)
return;
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
m_grabbers[0].color = GrabberColor;
m_grabbers[0].render(m_hover_id == 0, (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0));

View file

@ -131,6 +131,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l
m_imgui->text("");
ImGui::Separator();
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["highlight_by_angle"] + ":");
ImGui::AlignTextToFramePadding();
std::string format_str = std::string("%.f") + I18N::translate_utf8("°",

View file

@ -19,6 +19,9 @@ protected:
wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override;
std::string get_gizmo_entering_text() const override { return _u8L("Entering Paint-on supports"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Paint-on supports"); }
private:
bool on_init() override;

View file

@ -542,6 +542,7 @@ RENDER_AGAIN:
m_imgui->disabled_begin(! m_enable_hollowing);
float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("offset"));
ImGui::SameLine(settings_sliders_left);
ImGui::PushItemWidth(window_width - settings_sliders_left);
@ -558,6 +559,7 @@ RENDER_AGAIN:
bool slider_released = ImGui::IsItemDeactivatedAfterEdit(); // someone has just released the slider
if (current_mode >= quality_mode) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("quality"));
ImGui::SameLine(settings_sliders_left);
m_imgui->slider_float(" ", &quality, quality_min, quality_max, "%.1f");
@ -574,6 +576,7 @@ RENDER_AGAIN:
}
if (current_mode >= closing_d_mode) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("closing_distance"));
ImGui::SameLine(settings_sliders_left);
m_imgui->slider_float(" ", &closing_d, closing_d_min, closing_d_max, "%.1f mm");
@ -621,6 +624,7 @@ RENDER_AGAIN:
float diameter_upper_cap = 60.;
if (m_new_hole_radius * 2.f > diameter_upper_cap)
m_new_hole_radius = diameter_upper_cap / 2.f;
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("hole_diameter"));
ImGui::SameLine(diameter_slider_left);
ImGui::PushItemWidth(window_width - diameter_slider_left);
@ -636,6 +640,7 @@ RENDER_AGAIN:
bool edited = ImGui::IsItemEdited();
bool deactivated = ImGui::IsItemDeactivatedAfterEdit();
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc["hole_depth"]);
ImGui::SameLine(diameter_slider_left);
m_imgui->slider_float(" ", &m_new_hole_height, 0.f, 10.f, "%.1f mm", 1.f, false);
@ -692,8 +697,10 @@ RENDER_AGAIN:
// Following is rendered in both editing and non-editing mode:
// m_imgui->text("");
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f)
if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view"));
}
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){

View file

@ -128,6 +128,9 @@ protected:
wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override;
std::string get_gizmo_entering_text() const override { return _u8L("Entering Multimaterial painting"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Multimaterial painting"); }
size_t m_first_selected_extruder_idx = 0;
size_t m_second_selected_extruder_idx = 1;
std::vector<std::string> m_original_extruders_names;

View file

@ -141,7 +141,7 @@ void GLGizmoMove3D::on_render()
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
// draw grabber
float mean_size = (float)((box.size().x() + box.size().y() + box.size().z()) / 3.0);
m_grabbers[m_hover_id].render(true, mean_size);
@ -208,7 +208,7 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box
const_cast<GLModel*>(&m_vbo_cone)->set_color(-1, color);
if (!picking) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
}
glsafe(::glPushMatrix());

View file

@ -47,20 +47,14 @@ void GLGizmoPainterBase::activate_internal_undo_redo_stack(bool activate)
plater->undo_redo_topmost_string_getter(plater->can_undo(), last_snapshot_name);
if (activate && !m_internal_stack_active) {
std::string str = get_painter_type() == PainterGizmoType::FDM_SUPPORTS
? _u8L("Entering Paint-on supports")
: _u8L("Entering Seam painting");
if (last_snapshot_name != str)
if (std::string str = this->get_gizmo_entering_text(); last_snapshot_name != str)
Plater::TakeSnapshot(plater, str);
plater->enter_gizmos_stack();
m_internal_stack_active = true;
}
if (!activate && m_internal_stack_active) {
plater->leave_gizmos_stack();
std::string str = get_painter_type() == PainterGizmoType::SEAM
? _u8L("Leaving Seam painting")
: _u8L("Leaving Paint-on supports");
if (last_snapshot_name != str)
if (std::string str = this->get_gizmo_leaving_text(); last_snapshot_name != str)
Plater::TakeSnapshot(plater, str);
m_internal_stack_active = false;
}

View file

@ -173,6 +173,9 @@ protected:
virtual wxString handle_snapshot_action_name(bool shift_down, Button button_down) const = 0;
virtual std::string get_gizmo_entering_text() const = 0;
virtual std::string get_gizmo_leaving_text() const = 0;
friend class ::Slic3r::GUI::GLGizmoMmuSegmentation;
};

View file

@ -339,7 +339,7 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick
const_cast<GLModel*>(&m_cone)->set_color(-1, color);
if (!picking) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
}
glsafe(::glPushMatrix());

View file

@ -236,7 +236,7 @@ void GLGizmoScale3D::on_render()
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
// draw grabbers
m_grabbers[0].render(true, grabber_mean_size);
m_grabbers[1].render(true, grabber_mean_size);
@ -251,7 +251,7 @@ void GLGizmoScale3D::on_render()
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
// draw grabbers
m_grabbers[2].render(true, grabber_mean_size);
m_grabbers[3].render(true, grabber_mean_size);
@ -266,7 +266,7 @@ void GLGizmoScale3D::on_render()
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
// draw grabbers
m_grabbers[4].render(true, grabber_mean_size);
m_grabbers[5].render(true, grabber_mean_size);
@ -284,7 +284,7 @@ void GLGizmoScale3D::on_render()
GLShaderProgram* shader = wxGetApp().get_shader("gouraud_light");
if (shader != nullptr) {
shader->start_using();
shader->set_uniform("emission_factor", 0.1);
shader->set_uniform("emission_factor", 0.1f);
// draw grabbers
for (int i = 6; i < 10; ++i) {
m_grabbers[i].render(true, grabber_mean_size);

View file

@ -138,6 +138,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
const float max_tooltip_width = ImGui::GetFontSize() * 20.0f;
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("cursor_size"));
ImGui::SameLine(cursor_size_slider_left);
ImGui::PushItemWidth(window_width - cursor_size_slider_left);
@ -188,8 +189,10 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit)
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f)
if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view"));
}
else {
if (m_imgui->button(m_desc.at("reset_direction"))) {
wxGetApp().CallAfter([this](){

View file

@ -20,6 +20,9 @@ protected:
wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override;
std::string get_gizmo_entering_text() const override { return _u8L("Entering Seam painting"); }
std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Seam painting"); }
private:
bool on_init() override;

View file

@ -199,7 +199,7 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
ImGui::SameLine(m_gui_cfg->bottom_left_width);
if (m_imgui->button(_L("Preview"))) {
m_state = State::preview;
// simplify but not aply on mesh
// simplify but not apply on mesh
process();
}
ImGui::SameLine();
@ -207,15 +207,12 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
if (!m_is_valid_result) {
m_state = State::close_on_end;
process();
} else {
} else if (m_exist_preview) {
// use preview and close
if (m_exist_preview) {
// fix hollowing, sla support points, modifiers, ...
auto plater = wxGetApp().plater();
plater->changed_mesh(m_obj_index);
}
after_apply();
} else { // no changes made
close();
}
}
}
} else {
m_imgui->disabled_begin(m_state == State::canceling);
@ -237,18 +234,22 @@ void GLGizmoSimplify::on_render_input_window(float x, float y, float bottom_limi
m_parent.reload_scene(true);
// set m_state must be before close() !!!
m_state = State::settings;
if (close_on_end) {
// fix hollowing, sla support points, modifiers, ...
auto plater = wxGetApp().plater();
plater->changed_mesh(m_obj_index);
close();
}
if (close_on_end) after_apply();
// Fix warning icon in object list
wxGetApp().obj_list()->update_item_error_icon(m_obj_index, -1);
}
}
void GLGizmoSimplify::after_apply() {
// set flag to NOT revert changes when switch GLGizmoBase::m_state
m_exist_preview = false;
// fix hollowing, sla support points, modifiers, ...
auto plater = wxGetApp().plater();
plater->changed_mesh(m_obj_index);
close();
}
void GLGizmoSimplify::close() {
// close gizmo == open it again
GLGizmosManager &gizmos_mgr = m_parent.get_gizmos_manager();
@ -282,11 +283,11 @@ void GLGizmoSimplify::process()
}
};
std::function<void(int)> statusfn = [this](int percent) {
int64_t last = 0;
std::function<void(int)> statusfn = [this, &last](int percent) {
m_progress = percent;
// check max 4fps
static int64_t last = 0;
int64_t now = m_parent.timestamp_now();
if ((now - last) < 250) return;
last = now;

View file

@ -32,6 +32,7 @@ protected:
virtual void on_set_state() override;
private:
void after_apply();
void close();
void process();
void set_its(indexed_triangle_set &its);

View file

@ -169,7 +169,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
const_cast<GLModel*>(&m_cone)->set_color(-1, render_color);
const_cast<GLModel*>(&m_sphere)->set_color(-1, render_color);
if (shader && !picking)
shader->set_uniform("emission_factor", 0.5);
shader->set_uniform("emission_factor", 0.5f);
// Inverse matrix of the instance scaling is applied so that the mark does not scale with the object.
glsafe(::glPushMatrix());
@ -224,7 +224,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
render_color[3] = 0.7f;
const_cast<GLModel*>(&m_cylinder)->set_color(-1, render_color);
if (shader)
shader->set_uniform("emission_factor", 0.5);
shader->set_uniform("emission_factor", 0.5f);
for (const sla::DrainHole& drain_hole : m_c->selection_info()->model_object()->sla_drain_holes) {
if (is_mesh_point_clipped(drain_hole.pos.cast<double>()))
continue;
@ -786,8 +786,7 @@ RENDER_AGAIN:
// Following is rendered in both editing and non-editing mode:
ImGui::Separator();
if (m_c->object_clipper()->get_position() == 0.f)
{
if (m_c->object_clipper()->get_position() == 0.f) {
ImGui::AlignTextToFramePadding();
m_imgui->text(m_desc.at("clipping_of_view"));
}

View file

@ -620,7 +620,7 @@ void MainFrame::update_title()
wxString dirty_marker = (!m_plater->model().objects.empty() && m_plater->is_project_dirty()) ? "*" : "";
if (!dirty_marker.empty() || !project.empty()) {
if (!dirty_marker.empty() && project.empty())
project = _("Untitled");
project = _L("Untitled");
title = dirty_marker + project + " - ";
}
}
@ -819,7 +819,7 @@ bool MainFrame::is_active_and_shown_tab(Tab* tab)
bool MainFrame::can_start_new_project() const
{
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || !m_plater->model().objects.empty());
return (m_plater != nullptr) && (!m_plater->get_project_filename(".3mf").IsEmpty() || GetTitle().StartsWith('*') || !m_plater->model().objects.empty());
}
bool MainFrame::can_save() const

View file

@ -106,7 +106,6 @@ void MeshClipper::recalculate_triangles()
Transform3d tr = Transform3d::Identity();
tr.rotate(q);
tr = m_trafo.get_matrix() * tr;
height_mesh += 0.001f; // to avoid z-fighting
if (m_limiting_plane != ClippingPlane::ClipsNothing())
{
@ -165,6 +164,8 @@ void MeshClipper::recalculate_triangles()
m_triangles2d = triangulate_expolygons_2f(expolys, m_trafo.get_matrix().matrix().determinant() < 0.);
tr.pretranslate(0.001 * m_plane.get_normal().normalized()); // to avoid z-fighting
m_vertex_array.release_geometry();
for (auto it=m_triangles2d.cbegin(); it != m_triangles2d.cend(); it=it+3) {
m_vertex_array.push_geometry(tr * Vec3d((*(it+0))(0), (*(it+0))(1), height_mesh), up);

View file

@ -112,7 +112,7 @@ public:
// The class references extern TriangleMesh, which must stay alive
// during MeshRaycaster existence.
MeshRaycaster(const TriangleMesh& mesh)
: m_emesh(mesh)
: m_emesh(mesh, true) // calculate epsilon for triangle-ray intersection from an average edge length
{
m_normals.reserve(mesh.stl.facet_start.size());
for (const stl_facet& facet : mesh.stl.facet_start)

View file

@ -1369,7 +1369,18 @@ void NotificationManager::push_hint_notification(bool open_next)
}
NotificationData data{ NotificationType::DidYouKnowHint, NotificationLevel::RegularNotification, 300, "" };
push_notification_data(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler, open_next), 0);
// from user - open now
if (!open_next) {
push_notification_data(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler, open_next), 0);
stop_delayed_notifications_of_type(NotificationType::DidYouKnowHint);
// at startup - delay for half a second to let other notification pop up, than try every 30 seconds
// show only if no notifications are shown
} else {
auto condition = [this]() {
return this->get_notification_count() == 0;
};
push_delayed_notification(std::make_unique<NotificationManager::HintNotification>(data, m_id_provider, m_evt_handler, open_next), condition, 500, 30000);
}
}
bool NotificationManager::is_hint_notification_open()
@ -1425,6 +1436,28 @@ bool NotificationManager::push_notification_data(std::unique_ptr<NotificationMan
}
}
void NotificationManager::push_delayed_notification(std::unique_ptr<NotificationManager::PopNotification> notification, std::function<bool(void)> condition_callback, int64_t initial_delay, int64_t delay_interval)
{
if (initial_delay == 0 && condition_callback()) {
if( push_notification_data(std::move(notification), 0))
return;
}
m_waiting_notifications.emplace_back(std::move(notification), condition_callback, initial_delay == 0 ? delay_interval : initial_delay, delay_interval);
wxGetApp().plater()->get_current_canvas3D()->schedule_extra_frame(initial_delay == 0 ? delay_interval : initial_delay);
}
void NotificationManager::stop_delayed_notifications_of_type(const NotificationType type)
{
for (auto it = m_waiting_notifications.begin(); it != m_waiting_notifications.end();) {
if ((*it).notification->get_type() == type) {
it = m_waiting_notifications.erase(it);
}
else {
++it;
}
}
}
void NotificationManager::render_notifications(GLCanvas3D& canvas, float overlay_width)
{
sort_notifications();
@ -1477,6 +1510,26 @@ bool NotificationManager::update_notifications(GLCanvas3D& canvas)
++it;
}
// delayed notifications
for (auto it = m_waiting_notifications.begin(); it != m_waiting_notifications.end();) {
// substract time
if ((*it).remaining_time > 0)
(*it).remaining_time -= time_since_render;
if ((*it).remaining_time <= 0) {
if ((*it).condition_callback()) { // push notification, erase it from waiting list (frame is scheduled by push)
(*it).notification->reset_timer();
if (push_notification_data(std::move((*it).notification), 0)) {
it = m_waiting_notifications.erase(it);
continue;
}
}
// not possible to push, delay for delay_interval
(*it).remaining_time = (*it).delay_interval;
}
next_render = std::min<int64_t>(next_render, (*it).remaining_time);
++it;
}
// request next frame in future
if (next_render < max)
canvas.schedule_extra_frame(int(next_render));
@ -1569,6 +1622,15 @@ void NotificationManager::device_ejected()
notification->close();
}
}
size_t NotificationManager::get_notification_count() const
{
size_t ret = 0;
for (const std::unique_ptr<PopNotification>& notification : m_pop_notifications) {
if (notification->get_state() != PopNotification::EState::Hidden)
ret++;
}
return ret;
}
}//namespace GUI
}//namespace Slic3r

View file

@ -190,6 +190,8 @@ public:
void set_move_from_overlay(bool move) { m_move_from_overlay = move; }
// perform update_state on each notification and ask for more frames if needed, return true for render needed
bool update_notifications(GLCanvas3D& canvas);
// returns number of all notifications shown
size_t get_notification_count() const;
private:
// duration 0 means not disapearing
struct NotificationData {
@ -262,6 +264,8 @@ private:
EState get_state() const { return m_state; }
bool is_hovered() const { return m_state == EState::Hovered; }
void set_hovered() { if (m_state != EState::Finished && m_state != EState::ClosePending && m_state != EState::Hidden && m_state != EState::Unknown) m_state = EState::Hovered; }
// set start of notification to now. Used by delayed notifications
void reset_timer() { m_notification_start = GLCanvas3D::timestamp_now(); m_state = EState::Shown; }
protected:
// Call after every size change
virtual void init();
@ -515,10 +519,34 @@ private:
// in HintNotification.hpp
class HintNotification;
// Data of waiting notifications
struct DelayedNotification
{
std::unique_ptr<PopNotification> notification;
std::function<bool(void)> condition_callback;
int64_t remaining_time;
int64_t delay_interval;
DelayedNotification(std::unique_ptr<PopNotification> n, std::function<bool(void)> cb, int64_t r, int64_t d)
: notification(std::move(n))
, condition_callback(cb)
, remaining_time(r)
, delay_interval(d)
{}
};
//pushes notification into the queue of notifications that are rendered
//can be used to create custom notification
bool push_notification_data(const NotificationData& notification_data, int timestamp);
bool push_notification_data(std::unique_ptr<NotificationManager::PopNotification> notification, int timestamp);
// Delayed notifications goes first to the m_waiting_notifications vector and only after remaining time is <= 0
// and condition callback is success, notification is regular pushed from update function.
// Otherwise another delay interval waiting. Timestamp is 0.
// Note that notification object is constructed when being added to the waiting list, but there are no updates called on it and its timer is reset at regular push.
// Also note that no control of same notification is done during push_delayed_notification but if waiting notif fails to push, it continues waiting.
void push_delayed_notification(std::unique_ptr<NotificationManager::PopNotification> notification, std::function<bool(void)> condition_callback, int64_t initial_delay, int64_t delay_interval);
// Removes all notifications of type from m_waiting_notifications
void stop_delayed_notifications_of_type(const NotificationType type);
//finds older notification of same type and moves it to the end of queue. returns true if found
bool activate_existing(const NotificationManager::PopNotification* notification);
// Put the more important notifications to the bottom of the list.
@ -531,6 +559,8 @@ private:
// Cache of IDs to identify and reuse ImGUI windows.
NotificationIDProvider m_id_provider;
std::deque<std::unique_ptr<PopNotification>> m_pop_notifications;
// delayed waiting notifications, first is remaining time
std::deque<DelayedNotification> m_waiting_notifications;
//timestamps used for slicing finished - notification could be gone so it needs to be stored here
std::unordered_set<int> m_used_timestamps;
// True if G-code preview is active. False if the Plater is active.

View file

@ -1834,7 +1834,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, main_frame(main_frame)
, config(Slic3r::DynamicPrintConfig::new_from_defaults_keys({
"bed_shape", "bed_custom_texture", "bed_custom_model", "complete_objects", "duplicate_distance", "extruder_clearance_radius", "skirts", "skirt_distance",
"brim_width", "brim_offset", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
"brim_width", "brim_separation", "brim_type", "variable_layer_height", "nozzle_diameter", "single_extruder_multi_material",
"wipe_tower", "wipe_tower_x", "wipe_tower_y", "wipe_tower_width", "wipe_tower_rotation_angle", "wipe_tower_brim_width",
"extruder_colour", "filament_colour", "max_print_height", "printer_model", "printer_technology",
// These values are necessary to construct SlicingParameters by the Canvas3D variable layer height editor.
@ -2221,7 +2221,14 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
std::vector<size_t> obj_idxs;
for (size_t i = 0; i < input_files.size(); ++i) {
#ifdef _WIN32
auto path = input_files[i];
// On Windows, we swap slashes to back slashes, see GH #6803 as read_from_file() does not understand slashes on Windows thus it assignes full path to names of loaded objects.
path.make_preferred();
#else // _WIN32
// Don't make a copy on Posix. Slash is a path separator, back slashes are not accepted as a substitute.
const auto &path = input_files[i];
#endif // _WIN32
const auto filename = path.filename();
dlg.Update(static_cast<int>(100.0f * static_cast<float>(i) / static_cast<float>(input_files.size())), _L("Loading file") + ": " + from_path(filename));
dlg.Fit();
@ -2336,9 +2343,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
else {
model = Slic3r::Model::read_from_file(path.string(), nullptr, nullptr, only_if(load_config, Model::LoadAttribute::CheckVersion));
for (auto obj : model.objects)
if (obj->name.empty() ||
obj->name.find_first_of("/") != std::string::npos) // When file is imported from Fusion360 the path containes "/" instead of "\\" (see https://github.com/prusa3d/PrusaSlicer/issues/6803)
// But read_from_file doesn't support that direction separator and as a result object name containes full path
if (obj->name.empty())
obj->name = fs::path(obj->input_file).filename().string();
}
} catch (const ConfigurationError &e) {
@ -2457,7 +2462,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
}
if (load_model) {
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().string());
wxGetApp().app_config->update_skein_dir(input_files[input_files.size() - 1].parent_path().make_preferred().string());
// XXX: Plater.pm had @loaded_files, but didn't seem to fill them with the filenames...
statusbar()->set_status_text(_L("Loaded"));
}
@ -2915,6 +2920,7 @@ void Plater::priv::update_print_volume_state()
BoundingBox bed_box_2D = get_extents(Polygon::new_scale(this->config->opt<ConfigOptionPoints>("bed_shape")->values));
BoundingBoxf3 print_volume(unscale(bed_box_2D.min(0), bed_box_2D.min(1), 0.0), unscale(bed_box_2D.max(0), bed_box_2D.max(1), scale_(this->config->opt_float("max_print_height"))));
// Allow the objects to protrude below the print bed, only the part of the object above the print bed will be sliced.
print_volume.offset(BedEpsilon);
print_volume.min(2) = -1e10;
this->q->model().update_print_volume_state(print_volume);
}
@ -4786,10 +4792,7 @@ void Plater::load_project(const wxString& filename)
p->reset();
std::vector<fs::path> input_paths;
input_paths.push_back(into_path(filename));
if (! load_files(input_paths).empty()) {
if (! load_files({ into_path(filename) }).empty()) {
// At least one file was loaded.
p->set_project_filename(filename);
reset_project_dirty_initial_presets();
@ -4806,7 +4809,7 @@ void Plater::add_model(bool imperial_units/* = false*/)
std::vector<fs::path> paths;
for (const auto &file : input_files)
paths.push_back(into_path(file));
paths.emplace_back(into_path(file));
wxString snapshot_label;
assert(! paths.empty());
@ -4839,12 +4842,8 @@ void Plater::extract_config_from_project()
wxString input_file;
wxGetApp().load_project(this, input_file);
if (input_file.empty())
return;
std::vector<fs::path> input_paths;
input_paths.push_back(into_path(input_file));
load_files(input_paths, false, true);
if (! input_file.empty())
load_files({ into_path(input_file) }, false, true);
}
void Plater::load_gcode()
@ -5075,15 +5074,11 @@ bool Plater::load_files(const wxArrayString& filenames)
}
case LoadType::LoadGeometry: {
Plater::TakeSnapshot snapshot(this, _L("Import Object"));
std::vector<fs::path> in_paths;
in_paths.emplace_back(*it);
load_files(in_paths, true, false);
load_files({ *it }, true, false);
break;
}
case LoadType::LoadConfig: {
std::vector<fs::path> in_paths;
in_paths.emplace_back(*it);
load_files(in_paths, false, true);
load_files({ *it }, false, true);
break;
}
case LoadType::Unknown : {
@ -5158,8 +5153,11 @@ void Plater::delete_object_from_model(size_t obj_idx) { p->delete_object_from_mo
void Plater::remove_selected()
{
if (p->get_selection().is_empty())
return;
Plater::TakeSnapshot snapshot(this, _L("Delete Selected Objects"));
this->p->view3D->delete_selected();
p->view3D->delete_selected();
}
void Plater::increase_instances(size_t num)
@ -6221,9 +6219,9 @@ void Plater::clear_before_change_mesh(int obj_idx)
bool paint_removed = false;
for (ModelVolume* mv : mo->volumes) {
paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty();
mv->supported_facets.clear();
mv->seam_facets.clear();
mv->mmu_segmentation_facets.clear();
mv->supported_facets.reset();
mv->seam_facets.reset();
mv->mmu_segmentation_facets.reset();
}
if (paint_removed) {
// snapshot_time is captured by copy so the lambda knows where to undo/redo to.
@ -6585,6 +6583,11 @@ bool Plater::is_render_statistic_dialog_visible() const
return p->show_render_statistic_dialog;
}
Plater::TakeSnapshot::TakeSnapshot(Plater *plater, const std::string &snapshot_name)
: TakeSnapshot(plater, from_u8(snapshot_name)) {}
// Wrapper around wxWindow::PopupMenu to suppress error messages popping out while tracking the popup menu.
bool Plater::PopupMenu(wxMenu *menu, const wxPoint& pos)
{

View file

@ -383,10 +383,11 @@ public:
Plater *m_plater;
};
// ROII wrapper for taking an Undo / Redo snapshot while disabling the snapshot taking by the methods called from inside this snapshot.
// RAII wrapper for taking an Undo / Redo snapshot while disabling the snapshot taking by the methods called from inside this snapshot.
class TakeSnapshot
{
public:
TakeSnapshot(Plater *plater, const std::string &snapshot_name);
TakeSnapshot(Plater *plater, const wxString &snapshot_name) : m_plater(plater)
{
m_plater->take_snapshot(snapshot_name);

View file

@ -1874,7 +1874,7 @@ void Selection::render_sidebar_scale_hints(const std::string& sidebar_field) con
const_cast<GLModel*>(&m_arrow)->set_color(-1, uniform_scale ? UNIFORM_SCALE_COLOR : get_color(axis));
GLShaderProgram* shader = wxGetApp().get_current_shader();
if (shader != nullptr)
shader->set_uniform("emission_factor", 0.0);
shader->set_uniform("emission_factor", 0.0f);
glsafe(::glTranslated(0.0, 5.0, 0.0));
m_arrow.render();

View file

@ -1509,7 +1509,7 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Brim"));
optgroup->append_single_option_line("brim_type", category_path + "brim");
optgroup->append_single_option_line("brim_width", category_path + "brim");
optgroup->append_single_option_line("brim_offset", category_path + "brim");
optgroup->append_single_option_line("brim_separation", category_path + "brim");
page = add_options_page(L("Support material"), "support");
category_path = "support-material_1698#";
@ -1565,12 +1565,14 @@ void TabPrint::build()
optgroup = page->new_optgroup(L("Modifiers"));
optgroup->append_single_option_line("first_layer_speed");
optgroup->append_single_option_line("first_layer_speed_over_raft");
optgroup = page->new_optgroup(L("Acceleration control (advanced)"));
optgroup->append_single_option_line("perimeter_acceleration");
optgroup->append_single_option_line("infill_acceleration");
optgroup->append_single_option_line("bridge_acceleration");
optgroup->append_single_option_line("first_layer_acceleration");
optgroup->append_single_option_line("first_layer_acceleration_over_raft");
optgroup->append_single_option_line("default_acceleration");
optgroup = page->new_optgroup(L("Autospeed (advanced)"));